on xenial help sign in
merge trunk
7f844e9Michael Zanetti
CMakeLists.txt+13
@@ -40,6 +40,19 @@ if(cmake_build_type_lower MATCHES coverage)
40 ENABLE_COVERAGE_REPORT(TARGETS ${SHELL_APP} FILTER /usr/include ${CMAKE_SOURCE_DIR}/tests/* ${CMAKE_BINARY_DIR}/*)
40 ENABLE_COVERAGE_REPORT(TARGETS ${SHELL_APP} FILTER /usr/include ${CMAKE_SOURCE_DIR}/tests/* ${CMAKE_BINARY_DIR}/*)
41endif()
41endif()
42
42
43+find_package (ECM 1.7.0 QUIET NO_MODULE)
44+if (ECM_FOUND)
45+ # Provides us with -DECM_ENABLE_SANITIZERS='X'
46+ # Where X can be address, thread, memory, leak, undefined
47+ include("${ECM_MODULE_DIR}/ECMEnableSanitizers.cmake")
48+endif()
49+
50+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
51+ if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.0.0")
52+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override" )
53+ endif()
54+endif()
55+
43# Make sure we have all the needed symbols
56# Make sure we have all the needed symbols
44set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,defs")
57set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,defs")
45set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,defs")
58set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,defs")
debian/changelog+33
@@ -1,3 +1,36 @@
1+unity8 (8.15+17.04.20161207.1-0ubuntu1) zesty; urgency=medium
2+
3+ [ Albert Astals Cid ]
4+ * Add the Wsuggest-override flag to gcc
5+ * Add support for compiler sanitizers via ECM
6+ * Use timeStep as delay time (LP: #1642919)
7+ * Bring back fix for 1517830 (LP: #1517830)
8+ * Fix compile warnings in mocks
9+ * Do not hide panel when launching an application if the mouse is on
10+ the panel (LP: #1591311)
11+
12+ [ Daniel d'Andrada ]
13+ * Fix "make tryApplicationWindow"
14+ * Take save/restore functions out of WindowResizeArea
15+
16+ [ Josh Arenson ]
17+ * Enable the greeter to remember which session the user last logged
18+ into (LP: #1631365)
19+
20+ [ Lukáš Tinkl ]
21+ * Fix the Super key not invoking the dash scope home (LP: #1607427)
22+
23+ [ Michael Zanetti ]
24+ * Add the ApplicationDrawer
25+ * tune right edge push (LP: #1646094)
26+ * improve close button visiblity when hovering with the mouse
27+ * Update virtual touchpad visuals and add a tutorial. (LP: #1585220)
28+
29+ [ Pete Woods ]
30+ * MenuItemFactory: Add subtitle support to SwitchItem widget
31+
32+ -- Michał Sawicz <michal.sawicz@canonical.com> Wed, 07 Dec 2016 13:49:24 +0000
33+
1unity8 (8.15+17.04.20161129-0ubuntu1) zesty; urgency=medium
34unity8 (8.15+17.04.20161129-0ubuntu1) zesty; urgency=medium
2
35
3 [ Albert Astals Cid ]
36 [ Albert Astals Cid ]
plugins/LightDM/FullLightDM/CMakeLists.txt+5−1
@@ -6,10 +6,14 @@ add_definitions(-DFULL_LIGHTDM)
6
6
7include_directories(
7include_directories(
8 ../
8 ../
9− ${LIBLIGHTDM_INCLUDE_DIRS}
10 ${CMAKE_CURRENT_BINARY_DIR}
9 ${CMAKE_CURRENT_BINARY_DIR}
11 )
10 )
12
11
12+include_directories(
13+ SYSTEM
14+ ${LIBLIGHTDM_INCLUDE_DIRS}
15+ )
16+
13foreach(source_file ${QMLPLUGIN_SRC})
17foreach(source_file ${QMLPLUGIN_SRC})
14 list(APPEND RELATIVE_QMLPLUGIN_SRC ../${source_file})
18 list(APPEND RELATIVE_QMLPLUGIN_SRC ../${source_file})
15endforeach()
19endforeach()
plugins/Unity/Indicators/CMakeLists.txt+3
@@ -11,6 +11,9 @@ include_directories(
11include_directories(
11include_directories(
12 SYSTEM
12 SYSTEM
13 ${GLIB_INCLUDE_DIRS}
13 ${GLIB_INCLUDE_DIRS}
14+)
15+include_directories(
16+ SYSTEM
14 ${GIO_INCLUDE_DIRS}
17 ${GIO_INCLUDE_DIRS}
15 ${QMENUMODEL_INCLUDE_DIRS}
18 ${QMENUMODEL_INCLUDE_DIRS}
16)
19)
plugins/Utils/ElapsedTimer.h+1−1
@@ -41,7 +41,7 @@ class ElapsedTimer : public AbstractElapsedTimer {
41public:
41public:
42 void start() override { m_timer.start(); }
42 void start() override { m_timer.start(); }
43 qint64 msecsSinceReference() const override { return m_timer.msecsSinceReference(); }
43 qint64 msecsSinceReference() const override { return m_timer.msecsSinceReference(); }
44− qint64 elapsed() const override { return m_timer.elapsed(); }
44+ qint64 elapsed() const override { return m_timer.isValid() ? m_timer.elapsed() : 0; }
45private:
45private:
46 QElapsedTimer m_timer;
46 QElapsedTimer m_timer;
47};
47};
plugins/Utils/Timer.cpp+5−2
@@ -37,13 +37,16 @@ void Timer::setInterval(int msecs)
37void Timer::start()
37void Timer::start()
38{
38{
39 m_timer.start();
39 m_timer.start();
40− AbstractTimer::start();
41}
40}
42
41
43void Timer::stop()
42void Timer::stop()
44{
43{
45 m_timer.stop();
44 m_timer.stop();
46− AbstractTimer::stop();
45+}
46+
47+bool Timer::isRunning() const
48+{
49+ return m_timer.isActive();
47}
50}
48
51
49bool Timer::isSingleShot() const
52bool Timer::isSingleShot() const
plugins/Utils/Timer.h+5−6
@@ -30,18 +30,16 @@ class AbstractTimer : public QObject
30{
30{
31 Q_OBJECT
31 Q_OBJECT
32public:
32public:
33− AbstractTimer(QObject *parent) : QObject(parent), m_isRunning(false) {}
33+ AbstractTimer(QObject *parent) : QObject(parent) {}
34 virtual int interval() const = 0;
34 virtual int interval() const = 0;
35 virtual void setInterval(int msecs) = 0;
35 virtual void setInterval(int msecs) = 0;
36− virtual void start() { m_isRunning = true; }
36+ virtual void start() = 0;
37− virtual void stop() { m_isRunning = false; }
37+ virtual void stop() = 0;
38− bool isRunning() const { return m_isRunning; }
38+ virtual bool isRunning() const = 0;
39 virtual bool isSingleShot() const = 0;
39 virtual bool isSingleShot() const = 0;
40 virtual void setSingleShot(bool value) = 0;
40 virtual void setSingleShot(bool value) = 0;
41Q_SIGNALS:
41Q_SIGNALS:
42 void timeout();
42 void timeout();
43−private:
44− bool m_isRunning;
45};
43};
46
44
47/** A QTimer wrapper */
45/** A QTimer wrapper */
@@ -55,6 +53,7 @@ public:
55 void setInterval(int msecs) override;
53 void setInterval(int msecs) override;
56 void start() override;
54 void start() override;
57 void stop() override;
55 void stop() override;
56+ bool isRunning() const override;
58 bool isSingleShot() const override;
57 bool isSingleShot() const override;
59 void setSingleShot(bool value) override;
58 void setSingleShot(bool value) override;
60private:
59private:
plugins/Utils/globalfunctions.cpp+5
@@ -51,3 +51,8 @@ QQuickItem *GlobalFunctions::itemAt(QQuickItem* parent, int x, int y, QJSValue m
51 }
51 }
52 return nullptr;
52 return nullptr;
53}
53}
54+
55+bool GlobalFunctions::itemUnderMouse(QQuickItem* item)
56+{
57+ return item && item->isUnderMouse();
58+}
plugins/Utils/globalfunctions.h+2
@@ -37,6 +37,8 @@ public:
37 int x,
37 int x,
38 int y,
38 int y,
39 QJSValue matcher);
39 QJSValue matcher);
40+
41+ static Q_INVOKABLE bool itemUnderMouse(QQuickItem* item);
40};
42};
41
43
42#endif // GLOBALFUNCTIONS_H
44#endif // GLOBALFUNCTIONS_H
po/unity8.pot+80−43
@@ -8,7 +8,7 @@ msgid ""
8msgstr ""
8msgstr ""
9"Project-Id-Version: unity8\n"
9"Project-Id-Version: unity8\n"
10"Report-Msgid-Bugs-To: \n"
10"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2016-11-29 09:43+0000\n"
11+"POT-Creation-Date: 2016-12-07 13:49+0000\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -23,7 +23,7 @@ msgid "Password: "
23msgstr ""
23msgstr ""
24
24
25#: plugins/Unity/Launcher/launcheritem.cpp:50
25#: plugins/Unity/Launcher/launcheritem.cpp:50
26−#: plugins/Unity/Launcher/launcheritem.cpp:109
26+#: plugins/Unity/Launcher/launcheritem.cpp:122
27msgid "Pin shortcut"
27msgid "Pin shortcut"
28msgstr ""
28msgstr ""
29
29
@@ -31,7 +31,7 @@ msgstr ""
31msgid "Quit"
31msgid "Quit"
32msgstr ""
32msgstr ""
33
33
34−#: plugins/Unity/Launcher/launcheritem.cpp:109
34+#: plugins/Unity/Launcher/launcheritem.cpp:122
35msgid "Unpin shortcut"
35msgid "Unpin shortcut"
36msgstr ""
36msgstr ""
37
37
@@ -175,87 +175,95 @@ msgstr ""
175msgid "Same as clicking on a launcher icon."
175msgid "Same as clicking on a launcher icon."
176msgstr ""
176msgstr ""
177
177
178−#: qml/Components/KeyboardShortcutsOverlay.qml:181
178+#: qml/Components/KeyboardShortcutsOverlay.qml:177
179+msgid "Super + A"
180+msgstr ""
181+
182+#: qml/Components/KeyboardShortcutsOverlay.qml:182
183+msgid "Opens the Application Drawer."
184+msgstr ""
185+
186+#: qml/Components/KeyboardShortcutsOverlay.qml:194
179msgid "Scopes"
187msgid "Scopes"
180msgstr ""
188msgstr ""
181
189
182−#: qml/Components/KeyboardShortcutsOverlay.qml:188
190+#: qml/Components/KeyboardShortcutsOverlay.qml:201
183msgid "Super (Tap)"
191msgid "Super (Tap)"
184msgstr ""
192msgstr ""
185
193
186−#: qml/Components/KeyboardShortcutsOverlay.qml:193
194+#: qml/Components/KeyboardShortcutsOverlay.qml:206
187msgid "Opens the Scopes home."
195msgid "Opens the Scopes home."
188msgstr ""
196msgstr ""
189
197
190−#: qml/Components/KeyboardShortcutsOverlay.qml:209
198+#: qml/Components/KeyboardShortcutsOverlay.qml:222
191msgid "Switching"
199msgid "Switching"
192msgstr ""
200msgstr ""
193
201
194−#: qml/Components/KeyboardShortcutsOverlay.qml:216
202+#: qml/Components/KeyboardShortcutsOverlay.qml:229
195msgid "Alt + Tab"
203msgid "Alt + Tab"
196msgstr ""
204msgstr ""
197
205
198−#: qml/Components/KeyboardShortcutsOverlay.qml:221
206+#: qml/Components/KeyboardShortcutsOverlay.qml:234
199msgid "Switches between applications."
207msgid "Switches between applications."
200msgstr ""
208msgstr ""
201
209
202−#: qml/Components/KeyboardShortcutsOverlay.qml:229
210+#: qml/Components/KeyboardShortcutsOverlay.qml:242
203msgid "Super + W"
211msgid "Super + W"
204msgstr ""
212msgstr ""
205
213
206−#: qml/Components/KeyboardShortcutsOverlay.qml:234
214+#: qml/Components/KeyboardShortcutsOverlay.qml:247
207msgid "Opens the desktop spread."
215msgid "Opens the desktop spread."
208msgstr ""
216msgstr ""
209
217
210−#: qml/Components/KeyboardShortcutsOverlay.qml:242
218+#: qml/Components/KeyboardShortcutsOverlay.qml:255
211msgid "Cursor Left or Right"
219msgid "Cursor Left or Right"
212msgstr ""
220msgstr ""
213
221
214−#: qml/Components/KeyboardShortcutsOverlay.qml:247
222+#: qml/Components/KeyboardShortcutsOverlay.qml:260
215msgid "Moves the focus."
223msgid "Moves the focus."
216msgstr ""
224msgstr ""
217
225
218−#: qml/Components/KeyboardShortcutsOverlay.qml:259
226+#: qml/Components/KeyboardShortcutsOverlay.qml:272
219msgid "Windows"
227msgid "Windows"
220msgstr ""
228msgstr ""
221
229
222−#: qml/Components/KeyboardShortcutsOverlay.qml:266
230+#: qml/Components/KeyboardShortcutsOverlay.qml:279
223msgid "Ctrl + Super + D"
231msgid "Ctrl + Super + D"
224msgstr ""
232msgstr ""
225
233
226−#: qml/Components/KeyboardShortcutsOverlay.qml:271
234+#: qml/Components/KeyboardShortcutsOverlay.qml:284
227msgid "Minimizes all windows."
235msgid "Minimizes all windows."
228msgstr ""
236msgstr ""
229
237
230−#: qml/Components/KeyboardShortcutsOverlay.qml:279
238+#: qml/Components/KeyboardShortcutsOverlay.qml:292
231msgid "Ctrl + Super + Up"
239msgid "Ctrl + Super + Up"
232msgstr ""
240msgstr ""
233
241
234−#: qml/Components/KeyboardShortcutsOverlay.qml:284
242+#: qml/Components/KeyboardShortcutsOverlay.qml:297
235msgid "Maximizes the current window."
243msgid "Maximizes the current window."
236msgstr ""
244msgstr ""
237
245
238−#: qml/Components/KeyboardShortcutsOverlay.qml:292
246+#: qml/Components/KeyboardShortcutsOverlay.qml:305
239msgid "Ctrl + Super + Down"
247msgid "Ctrl + Super + Down"
240msgstr ""
248msgstr ""
241
249
242−#: qml/Components/KeyboardShortcutsOverlay.qml:297
250+#: qml/Components/KeyboardShortcutsOverlay.qml:310
243msgid "Minimizes or restores the current window."
251msgid "Minimizes or restores the current window."
244msgstr ""
252msgstr ""
245
253
246−#: qml/Components/KeyboardShortcutsOverlay.qml:305
254+#: qml/Components/KeyboardShortcutsOverlay.qml:318
247msgid "Ctrl + Super + Left or Right"
255msgid "Ctrl + Super + Left or Right"
248msgstr ""
256msgstr ""
249
257
250−#: qml/Components/KeyboardShortcutsOverlay.qml:310
258+#: qml/Components/KeyboardShortcutsOverlay.qml:323
251msgid "Semi-maximizes the current window."
259msgid "Semi-maximizes the current window."
252msgstr ""
260msgstr ""
253
261
254−#: qml/Components/KeyboardShortcutsOverlay.qml:318
262+#: qml/Components/KeyboardShortcutsOverlay.qml:331
255msgid "Alt + F4"
263msgid "Alt + F4"
256msgstr ""
264msgstr ""
257
265
258−#: qml/Components/KeyboardShortcutsOverlay.qml:323
266+#: qml/Components/KeyboardShortcutsOverlay.qml:336
259msgid "Closes the current window."
267msgid "Closes the current window."
260msgstr ""
268msgstr ""
261
269
@@ -311,6 +319,28 @@ msgstr ""
311msgid "Preview Share Item"
319msgid "Preview Share Item"
312msgstr ""
320msgstr ""
313
321
322+#: qml/Components/VirtualTouchPad.qml:317
323+msgid ""
324+"Your device is now connected to an external display. Use this screen as a "
325+"touch pad to interact with the pointer."
326+msgstr ""
327+
328+#: qml/Components/VirtualTouchPad.qml:327
329+msgid "Tap left button to click."
330+msgstr ""
331+
332+#: qml/Components/VirtualTouchPad.qml:339
333+msgid "Tap right button to right click."
334+msgstr ""
335+
336+#: qml/Components/VirtualTouchPad.qml:351
337+msgid "Swipe with two fingers to scroll."
338+msgstr ""
339+
340+#: qml/Components/VirtualTouchPad.qml:396
341+msgid "Find more settings in the system settings."
342+msgstr ""
343+
314#: qml/Dash/DashPageHeader.qml:361
344#: qml/Dash/DashPageHeader.qml:361
315msgctxt "Button: Open the Ubuntu Store"
345msgctxt "Button: Open the Ubuntu Store"
316msgid "Store"
346msgid "Store"
@@ -408,12 +438,6 @@ msgstr ""
408msgid "Also installed"
438msgid "Also installed"
409msgstr ""
439msgstr ""
410
440
411−#: qml/DisabledScreenNotice.qml:109
412−msgid ""
413−"Your device is now connected to an external display. Use this screen as a "
414−"touch pad to interact with the pointer."
415−msgstr ""
416−
417#: qml/Greeter/CoverPage.qml:127
441#: qml/Greeter/CoverPage.qml:127
418msgid "Unlock"
442msgid "Unlock"
419msgstr ""
443msgstr ""
@@ -437,7 +461,7 @@ msgid_plural "Please wait %1 minutes and then try again…"
437msgstr[0] ""
461msgstr[0] ""
438msgstr[1] ""
462msgstr[1] ""
439
463
440−#: qml/Greeter/Greeter.qml:596
464+#: qml/Greeter/Greeter.qml:592
441msgid "Try again"
465msgid "Try again"
442msgstr ""
466msgstr ""
443
467
@@ -465,6 +489,19 @@ msgstr ""
465msgid "Select desktop environment"
489msgid "Select desktop environment"
466msgstr ""
490msgstr ""
467
491
492+#: qml/Launcher/Drawer.qml:80
493+msgid "Search…"
494+msgstr ""
495+
496+#: qml/Launcher/Drawer.qml:101
497+msgctxt "Apps sorted alphabetically"
498+msgid "A-Z"
499+msgstr ""
500+
501+#: qml/Launcher/MoreAppsHeader.qml:32
502+msgid "More apps in the store"
503+msgstr ""
504+
468#: qml/Notifications/NotificationMenuItemFactory.qml:124
505#: qml/Notifications/NotificationMenuItemFactory.qml:124
469msgid "Show password"
506msgid "Show password"
470msgstr ""
507msgstr ""
@@ -477,62 +514,62 @@ msgstr ""
477msgid "Conference"
514msgid "Conference"
478msgstr ""
515msgstr ""
479
516
480−#: qml/Panel/Indicators/MenuItemFactory.qml:847
517+#: qml/Panel/Indicators/MenuItemFactory.qml:868
481msgid "Nothing is playing"
518msgid "Nothing is playing"
482msgstr ""
519msgstr ""
483
520
484−#: qml/Panel/Indicators/MenuItemFactory.qml:975
521+#: qml/Panel/Indicators/MenuItemFactory.qml:996
485#, qt-format
522#, qt-format
486msgid "%1 hour"
523msgid "%1 hour"
487msgid_plural "%1 hours"
524msgid_plural "%1 hours"
488msgstr[0] ""
525msgstr[0] ""
489msgstr[1] ""
526msgstr[1] ""
490
527
491−#: qml/Panel/Indicators/MenuItemFactory.qml:979
528+#: qml/Panel/Indicators/MenuItemFactory.qml:1000
492#, qt-format
529#, qt-format
493msgid "%1 minute"
530msgid "%1 minute"
494msgid_plural "%1 minutes"
531msgid_plural "%1 minutes"
495msgstr[0] ""
532msgstr[0] ""
496msgstr[1] ""
533msgstr[1] ""
497
534
498−#: qml/Panel/Indicators/MenuItemFactory.qml:984
535+#: qml/Panel/Indicators/MenuItemFactory.qml:1005
499#, qt-format
536#, qt-format
500msgid "%1 second"
537msgid "%1 second"
501msgid_plural "%1 seconds"
538msgid_plural "%1 seconds"
502msgstr[0] ""
539msgstr[0] ""
503msgstr[1] ""
540msgstr[1] ""
504
541
505−#: qml/Panel/Indicators/MenuItemFactory.qml:987
542+#: qml/Panel/Indicators/MenuItemFactory.qml:1008
506msgid "0 seconds"
543msgid "0 seconds"
507msgstr ""
544msgstr ""
508
545
509#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
546#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
510−#: qml/Panel/Indicators/MenuItemFactory.qml:989
547+#: qml/Panel/Indicators/MenuItemFactory.qml:1010
511#, qt-format
548#, qt-format
512msgid "%1 remaining"
549msgid "%1 remaining"
513msgstr ""
550msgstr ""
514
551
515−#: qml/Panel/Indicators/MenuItemFactory.qml:995
552+#: qml/Panel/Indicators/MenuItemFactory.qml:1016
516msgid "In queue…"
553msgid "In queue…"
517msgstr ""
554msgstr ""
518
555
519−#: qml/Panel/Indicators/MenuItemFactory.qml:999
556+#: qml/Panel/Indicators/MenuItemFactory.qml:1020
520msgid "Downloading"
557msgid "Downloading"
521msgstr ""
558msgstr ""
522
559
523−#: qml/Panel/Indicators/MenuItemFactory.qml:1001
560+#: qml/Panel/Indicators/MenuItemFactory.qml:1022
524msgid "Paused, tap to resume"
561msgid "Paused, tap to resume"
525msgstr ""
562msgstr ""
526
563
527−#: qml/Panel/Indicators/MenuItemFactory.qml:1003
564+#: qml/Panel/Indicators/MenuItemFactory.qml:1024
528msgid "Canceled"
565msgid "Canceled"
529msgstr ""
566msgstr ""
530
567
531−#: qml/Panel/Indicators/MenuItemFactory.qml:1005
568+#: qml/Panel/Indicators/MenuItemFactory.qml:1026
532msgid "Finished"
569msgid "Finished"
533msgstr ""
570msgstr ""
534
571
535−#: qml/Panel/Indicators/MenuItemFactory.qml:1007
572+#: qml/Panel/Indicators/MenuItemFactory.qml:1028
536msgid "Failed, tap to retry"
573msgid "Failed, tap to retry"
537msgstr ""
574msgstr ""
538
575
qml/Components/VirtualTouchPad.qml+269−1
@@ -15,20 +15,58 @@
15 */
15 */
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18+import QtQuick.Layouts 1.1
18import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20+import Qt.labs.settings 1.0
21+import Unity.Screens 0.1
19import UInput 0.1
22import UInput 0.1
23+import "../Components"
20
24
21Item {
25Item {
26+ id: root
22 property var uinput: UInput {
27 property var uinput: UInput {
23 Component.onCompleted: createMouse();
28 Component.onCompleted: createMouse();
24 Component.onDestruction: removeMouse();
29 Component.onDestruction: removeMouse();
25 }
30 }
26
31
32+ Component.onCompleted: {
33+ if (!settings.touchpadTutorialHasRun) {
34+ root.runTutorial()
35+ }
36+ }
37+
38+ function runTutorial() {
39+ // If the tutorial animation is started too early, e.g. in Component.onCompleted,
40+ // root width & height might be reported as 0x0 still. As animations read their
41+ // values at startup and won't update them, lets make sure to only start once
42+ // we have some actual size.
43+ if (root.width > 0 && root.height > 0) {
44+ tutorial.start();
45+ } else {
46+ tutorialTimer.start();
47+ }
48+ }
49+
50+ Timer {
51+ id: tutorialTimer
52+ interval: 50
53+ repeat: false
54+ running: false
55+ onTriggered: root.runTutorial();
56+ }
57+
27− readonly property bool pressed: point1.pressed || point2.pressed
58+ readonly property bool pressed: point1.pressed || point2.pressed || leftButton.pressed || rightButton.pressed
59+
60+ property var settings: Settings {
61+ objectName: "virtualTouchPadSettings"
62+ property bool touchpadTutorialHasRun: false
63+ property bool oskEnabled: true
64+ }
28
65
29 MultiPointTouchArea {
66 MultiPointTouchArea {
30 objectName: "touchPadArea"
67 objectName: "touchPadArea"
31 anchors.fill: parent
68 anchors.fill: parent
69+ enabled: !tutorial.running || tutorial.paused
32
70
33 // FIXME: Once we have Qt DPR support, this should be Qt.styleHints.startDragDistance
71 // FIXME: Once we have Qt DPR support, this should be Qt.styleHints.startDragDistance
34 readonly property int clickThreshold: internalGu * 1.5
72 readonly property int clickThreshold: internalGu * 1.5
@@ -37,6 +75,11 @@ Item {
37 property bool isDrag: false
75 property bool isDrag: false
38
76
39 onPressed: {
77 onPressed: {
78+ if (tutorial.paused) {
79+ tutorial.resume();
80+ return;
81+ }
82+
40 // If double-tapping *really* fast, it could happen that we end up having only point2 pressed
83 // If double-tapping *really* fast, it could happen that we end up having only point2 pressed
41 // Make sure we check for both combos, only point1 or only point2
84 // Make sure we check for both combos, only point1 or only point2
42 if (((point1.pressed && !point2.pressed) || (!point1.pressed && point2.pressed))
85 if (((point1.pressed && !point2.pressed) || (!point1.pressed && point2.pressed))
@@ -135,4 +178,229 @@ Item {
135 }
178 }
136 ]
179 ]
137 }
180 }
181+
182+ RowLayout {
183+ anchors { left: parent.left; right: parent.right; bottom: parent.bottom; margins: -internalGu * 1 }
184+ height: internalGu * 10
185+ spacing: internalGu * 1
186+
187+ MouseArea {
188+ id: leftButton
189+ objectName: "leftButton"
190+ Layout.fillWidth: true
191+ Layout.fillHeight: true
192+ onPressed: uinput.pressMouse(UInput.ButtonLeft);
193+ onReleased: uinput.releaseMouse(UInput.ButtonLeft);
194+ property bool highlight: false
195+ UbuntuShape {
196+ anchors.fill: parent
197+ backgroundColor: leftButton.highlight || leftButton.pressed ? UbuntuColors.ash : UbuntuColors.inkstone
198+ Behavior on backgroundColor { ColorAnimation { duration: UbuntuAnimation.FastDuration } }
199+ }
200+ }
201+
202+ MouseArea {
203+ id: rightButton
204+ objectName: "rightButton"
205+ Layout.fillWidth: true
206+ Layout.fillHeight: true
207+ onPressed: uinput.pressMouse(UInput.ButtonRight);
208+ onReleased: uinput.releaseMouse(UInput.ButtonRight);
209+ property bool highlight: false
210+ UbuntuShape {
211+ anchors.fill: parent
212+ backgroundColor: rightButton.highlight || rightButton.pressed ? UbuntuColors.ash : UbuntuColors.inkstone
213+ Behavior on backgroundColor { ColorAnimation { duration: UbuntuAnimation.FastDuration } }
214+ }
215+ }
216+ }
217+
218+ AbstractButton {
219+ id: oskButton
220+ objectName: "oskButton"
221+ anchors { right: parent.right; top: parent.top; margins: internalGu * 2 }
222+ height: internalGu * 6
223+ width: height
224+
225+ onClicked: {
226+ settings.oskEnabled = !settings.oskEnabled
227+ }
228+
229+ Rectangle {
230+ anchors.fill: parent
231+ radius: width / 2
232+ color: UbuntuColors.inkstone
233+ }
234+
235+ Icon {
236+ anchors.fill: parent
237+ anchors.margins: internalGu * 1.5
238+ name: "input-keyboard-symbolic"
239+ }
240+ }
241+
242+ Screens {
243+ id: screens
244+ }
245+
246+ InputMethod {
247+ id: inputMethod
248+ // Don't resize when there is only one screen to avoid resize clashing with the InputMethod in the Shell.
249+ enabled: screens.count > 1 && settings.oskEnabled && !tutorial.running
250+ objectName: "inputMethod"
251+ anchors.fill: parent
252+ }
253+
254+ Label {
255+ id: tutorialLabel
256+ objectName: "tutorialLabel"
257+ anchors { left: parent.left; top: parent.top; right: parent.right; margins: internalGu * 4; topMargin: internalGu * 10 }
258+ opacity: 0
259+ visible: opacity > 0
260+ font.pixelSize: 2 * internalGu
261+ color: "white"
262+ wrapMode: Text.WordWrap
263+ }
264+
265+ Icon {
266+ id: tutorialImage
267+ objectName: "tutorialImage"
268+ height: internalGu * 8
269+ width: height
270+ name: "input-touchpad-symbolic"
271+ color: "white"
272+ opacity: 0
273+ visible: opacity > 0
274+ anchors { top: tutorialLabel.bottom; horizontalCenter: parent.horizontalCenter; margins: internalGu * 2 }
275+ }
276+
277+ Item {
278+ id: tutorialFinger1
279+ objectName: "tutorialFinger1"
280+ width: internalGu * 5
281+ height: width
282+ property real scale: 1
283+ opacity: 0
284+ visible: opacity > 0
285+ Rectangle {
286+ width: parent.width * parent.scale
287+ height: width
288+ anchors.centerIn: parent
289+ radius: width / 2
290+ color: UbuntuColors.inkstone
291+ }
292+ }
293+
294+ Item {
295+ id: tutorialFinger2
296+ objectName: "tutorialFinger2"
297+ width: internalGu * 5
298+ height: width
299+ property real scale: 1
300+ opacity: 0
301+ visible: opacity > 0
302+ Rectangle {
303+ width: parent.width * parent.scale
304+ height: width
305+ anchors.centerIn: parent
306+ radius: width / 2
307+ color: UbuntuColors.inkstone
308+ }
309+ }
310+
311+ SequentialAnimation {
312+ id: tutorial
313+ objectName: "tutorialAnimation"
314+
315+ PropertyAction { targets: [leftButton, rightButton, oskButton]; property: "enabled"; value: false }
316+ PropertyAction { targets: [leftButton, rightButton, oskButton]; property: "opacity"; value: 0 }
317+ PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Your device is now connected to an external display. Use this screen as a touch pad to interact with the pointer.") }
318+ UbuntuNumberAnimation { targets: [tutorialLabel, tutorialImage]; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
319+ PropertyAction { target: tutorial; property: "paused"; value: true }
320+ PauseAnimation { duration: 500 } // it takes a bit until pausing actually takes effect
321+ UbuntuNumberAnimation { targets: [tutorialLabel, tutorialImage]; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
322+
323+ UbuntuNumberAnimation { target: leftButton; property: "opacity"; to: 1 }
324+ UbuntuNumberAnimation { target: rightButton; property: "opacity"; to: 1 }
325+
326+ PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
327+ PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Tap left button to click.") }
328+ UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
329+ SequentialAnimation {
330+ loops: 2
331+ PropertyAction { target: leftButton; property: "highlight"; value: true }
332+ PauseAnimation { duration: UbuntuAnimation.FastDuration }
333+ PropertyAction { target: leftButton; property: "highlight"; value: false }
334+ PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
335+ }
336+ UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
337+
338+ PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
339+ PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Tap right button to right click.") }
340+ UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
341+ SequentialAnimation {
342+ loops: 2
343+ PropertyAction { target: rightButton; property: "highlight"; value: true }
344+ PauseAnimation { duration: UbuntuAnimation.FastDuration }
345+ PropertyAction { target: rightButton; property: "highlight"; value: false }
346+ PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
347+ }
348+ UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
349+
350+ PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
351+ PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Swipe with two fingers to scroll.") }
352+ UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
353+ PropertyAction { target: tutorialFinger1; property: "x"; value: root.width / 2 - tutorialFinger1.width - internalGu * 1 }
354+ PropertyAction { target: tutorialFinger2; property: "x"; value: root.width / 2 + tutorialFinger1.width + internalGu * 1 - tutorialFinger2.width }
355+ PropertyAction { target: tutorialFinger1; property: "y"; value: root.height / 2 - internalGu * 10 }
356+ PropertyAction { target: tutorialFinger2; property: "y"; value: root.height / 2 - internalGu * 10 }
357+ SequentialAnimation {
358+ ParallelAnimation {
359+ UbuntuNumberAnimation { target: tutorialFinger1; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
360+ UbuntuNumberAnimation { target: tutorialFinger2; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
361+ UbuntuNumberAnimation { target: tutorialFinger1; property: "scale"; from: 0; to: 1; duration: UbuntuAnimation.FastDuration }
362+ UbuntuNumberAnimation { target: tutorialFinger2; property: "scale"; from: 0; to: 1; duration: UbuntuAnimation.FastDuration }
363+ }
364+ ParallelAnimation {
365+ UbuntuNumberAnimation { target: tutorialFinger1; property: "y"; to: root.height / 2 + internalGu * 10; duration: UbuntuAnimation.SleepyDuration }
366+ UbuntuNumberAnimation { target: tutorialFinger2; property: "y"; to: root.height / 2 + internalGu * 10; duration: UbuntuAnimation.SleepyDuration }
367+ }
368+ ParallelAnimation {
369+ UbuntuNumberAnimation { target: tutorialFinger1; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
370+ UbuntuNumberAnimation { target: tutorialFinger2; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
371+ UbuntuNumberAnimation { target: tutorialFinger1; property: "scale"; from: 1; to: 0; duration: UbuntuAnimation.FastDuration }
372+ UbuntuNumberAnimation { target: tutorialFinger2; property: "scale"; from: 1; to: 0; duration: UbuntuAnimation.FastDuration }
373+ }
374+ PauseAnimation { duration: UbuntuAnimation.SlowDuration }
375+ ParallelAnimation {
376+ UbuntuNumberAnimation { target: tutorialFinger1; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
377+ UbuntuNumberAnimation { target: tutorialFinger2; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
378+ UbuntuNumberAnimation { target: tutorialFinger1; property: "scale"; from: 0; to: 1; duration: UbuntuAnimation.FastDuration }
379+ UbuntuNumberAnimation { target: tutorialFinger2; property: "scale"; from: 0; to: 1; duration: UbuntuAnimation.FastDuration }
380+ }
381+ ParallelAnimation {
382+ UbuntuNumberAnimation { target: tutorialFinger1; property: "y"; to: root.height / 2 - internalGu * 10; duration: UbuntuAnimation.SleepyDuration }
383+ UbuntuNumberAnimation { target: tutorialFinger2; property: "y"; to: root.height / 2 - internalGu * 10; duration: UbuntuAnimation.SleepyDuration }
384+ }
385+ ParallelAnimation {
386+ UbuntuNumberAnimation { target: tutorialFinger1; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
387+ UbuntuNumberAnimation { target: tutorialFinger2; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
388+ UbuntuNumberAnimation { target: tutorialFinger1; property: "scale"; from: 1; to: 0; duration: UbuntuAnimation.FastDuration }
389+ UbuntuNumberAnimation { target: tutorialFinger2; property: "scale"; from: 1; to: 0; duration: UbuntuAnimation.FastDuration }
390+ }
391+ PauseAnimation { duration: UbuntuAnimation.SlowDuration }
392+ }
393+ UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
394+
395+ PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
396+ PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Find more settings in the system settings.") }
397+ UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
398+ PauseAnimation { duration: 2000 }
399+ UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
400+
401+ UbuntuNumberAnimation { target: oskButton; property: "opacity"; to: 1 }
402+ PropertyAction { targets: [leftButton, rightButton, oskButton]; property: "enabled"; value: true }
403+
404+ PropertyAction { target: settings; property: "touchpadTutorialHasRun"; value: true }
405+ }
138}
406}
qml/Dash/Previews/PreviewAudioPlayback.qml+1
@@ -70,6 +70,7 @@ PreviewWidget {
70 objectName: "playButton"
70 objectName: "playButton"
71 width: trackRow.column1Width
71 width: trackRow.column1Width
72 height: width
72 height: width
73+ activeFocusOnPress: false
73
74
74 onClicked: {
75 onClicked: {
75 if (trackItem.isPlayingItem) {
76 if (trackItem.isPlayingItem) {
qml/DisabledScreenNotice.qml+3−55
@@ -18,15 +18,12 @@ import QtQuick 2.4
18import QtQuick.Layouts 1.1
18import QtQuick.Layouts 1.1
19import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20import Unity.Session 0.1
20import Unity.Session 0.1
21−import Unity.Screens 0.1
22import QtQuick.Window 2.2
21import QtQuick.Window 2.2
23import "Components"
22import "Components"
24
23
25Item {
24Item {
26 id: root
25 id: root
27
26
28− property bool infoNoteDisplayed: true
29−
30 // For testing
27 // For testing
31 property var screen: Screen
28 property var screen: Screen
32 property var orientationLock: OrientationLock
29 property var orientationLock: OrientationLock
@@ -36,10 +33,6 @@ Item {
36 name: applicationArguments.deviceName
33 name: applicationArguments.deviceName
37 }
34 }
38
35
39− Screens {
40− id: screens
41− }
42−
43 Item {
36 Item {
44 id: contentContainer
37 id: contentContainer
45 objectName: "contentContainer"
38 objectName: "contentContainer"
@@ -75,58 +68,13 @@ Item {
75 }
68 }
76 transformOrigin: Item.Center
69 transformOrigin: Item.Center
77
70
78− VirtualTouchPad {
79− anchors.fill: parent
80−
81− onPressedChanged: {
82− if (pressed && infoNoteDisplayed) {
83− infoNoteDisplayed = false;
84− }
85− }
86− }
87−
88 Rectangle {
71 Rectangle {
89 anchors.fill: parent
72 anchors.fill: parent
90− color: "#3b3b3b"
73+ color: UbuntuColors.jet
91 }
74 }
92
75
93− Item {
76+ VirtualTouchPad {
94− objectName: "infoNoticeArea"
95− anchors.fill: parent
96− opacity: infoNoteDisplayed ? 1 : 0
97− visible: opacity > 0
98− Behavior on opacity {
99− UbuntuNumberAnimation { }
100− }
101−
102− Column {
103− anchors.centerIn: parent
104− width: parent.width - (internalGu * 8)
105− spacing: internalGu * 4
106−
107− Label {
108− id: text
109− text: i18n.tr("Your device is now connected to an external display. Use this screen as a touch pad to interact with the pointer.")
110− color: "white"
111− width: parent.width
112− font.pixelSize: 2.5 * internalGu
113− wrapMode: Text.Wrap
114− }
115− Icon {
116− height: internalGu * 8
117− width: height
118− name: "input-touchpad-symbolic"
119− color: "white"
120− anchors.horizontalCenter: parent.horizontalCenter
121− }
122− }
123− }
124−
125− InputMethod {
126− id: inputMethod
127− // Don't resize when there is only one screen to avoid resize clashing with the InputMethod in the Shell.
128− enabled: screens.count > 1
129− objectName: "inputMethod"
77+ objectName: "virtualTouchPad"
130 anchors.fill: parent
78 anchors.fill: parent
131 }
79 }
132 }
80 }
qml/Greeter/Greeter.qml+1−5
@@ -145,11 +145,7 @@ Showable {
145 }
145 }
146 }
146 }
147
147
148− if (loader.item.sessionToStart === LightDMService.greeter.defaultSession) {
149− return LightDMService.greeter.defaultSession;
148+ return LightDMService.greeter.defaultSession;
150− } else {
151− return "ubuntu"; // The default / fallback
152− }
153 }
149 }
154
150
155 QtObject {
151 QtObject {
qml/Greeter/LoginList.qml+1−1
@@ -165,7 +165,7 @@ StyledItem {
165 }
165 }
166
166
167 delegate: Item {
167 delegate: Item {
168− width: parent.width
168+ width: userList.width
169 height: root.cellHeight
169 height: root.cellHeight
170
170
171 readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex)
171 readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex)
qml/Greeter/WideView.qml+7−2
@@ -125,6 +125,8 @@ FocusScope {
125 id: loginList
125 id: loginList
126 objectName: "loginList"
126 objectName: "loginList"
127
127
128+ property int selectedUserIndex: 0
129+
128 width: units.gu(40)
130 width: units.gu(40)
129 anchors {
131 anchors {
130 left: parent.left
132 left: parent.left
@@ -139,9 +141,12 @@ FocusScope {
139 Behavior on boxVerticalOffset { UbuntuNumberAnimation {} }
141 Behavior on boxVerticalOffset { UbuntuNumberAnimation {} }
140
142
141 model: root.userModel
143 model: root.userModel
142− currentSession: LightDMService.greeter.defaultSession
144+ currentSession: LightDMService.users.data(selectedUserIndex, LightDMService.userRoles.SessionRole);
143 onResponded: root.responded(response)
145 onResponded: root.responded(response)
146+ onSelected: {
144− onSelected: root.selected(index)
147+ root.selected(index)
148+ loginList.selectedUserIndex = index;
149+ }
145 onSessionChooserButtonClicked: parent.state = "SessionsList"
150 onSessionChooserButtonClicked: parent.state = "SessionsList"
146
151
147 Keys.forwardTo: [sessionChooserLoader.item]
152 Keys.forwardTo: [sessionChooserLoader.item]
qml/Launcher/Launcher.qml+9−3
@@ -19,10 +19,13 @@ import "../Components"
19import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20import Ubuntu.Gestures 0.1
20import Ubuntu.Gestures 0.1
21import Unity.Launcher 0.1
21import Unity.Launcher 0.1
22+import Utils 0.1 as Utils
22
23
23FocusScope {
24FocusScope {
24 id: root
25 id: root
25
26
27+ readonly property int ignoreHideIfMouseOverLauncher: 1
28+
26 property bool autohideEnabled: false
29 property bool autohideEnabled: false
27 property bool lockedVisible: false
30 property bool lockedVisible: false
28 property bool available: true // can be used to disable all interactions
31 property bool available: true // can be used to disable all interactions
@@ -105,7 +108,10 @@ FocusScope {
105 }
108 }
106 }
109 }
107
110
108− function hide() {
111+ function hide(flags) {
112+ if ((flags & ignoreHideIfMouseOverLauncher) && Utils.Functions.itemUnderMouse(panel)) {
113+ return;
114+ }
109 switchToNextState("")
115 switchToNextState("")
110 }
116 }
111
117
@@ -416,11 +422,11 @@ FocusScope {
416 property bool animate: true
422 property bool animate: true
417
423
418 onApplicationSelected: {
424 onApplicationSelected: {
419− root.hide();
425+ root.hide(ignoreHideIfMouseOverLauncher);
420 launcherApplicationSelected(appId)
426 launcherApplicationSelected(appId)
421 }
427 }
422 onShowDashHome: {
428 onShowDashHome: {
423− root.hide();
429+ root.hide(ignoreHideIfMouseOverLauncher);
424 root.showDashHome();
430 root.showDashHome();
425 }
431 }
426
432
qml/Panel/Indicators/MenuItemFactory.qml+22−1
@@ -106,7 +106,7 @@ Item {
106 }
106 }
107
107
108 if (component === undefined) {
108 if (component === undefined) {
109− console.debug("Don't know how to make " + modelData.type + " for " + indicator);
109+ console.debug("Don't know how to make " + type + " for " + indicator);
110 }
110 }
111
111
112 return component
112 return component
@@ -398,7 +398,9 @@ Item {
398 id: switchItem
398 id: switchItem
399 objectName: "switchMenu"
399 objectName: "switchMenu"
400 property QtObject menuData: null
400 property QtObject menuData: null
401+ property var menuModel: menuFactory.menuModel
401 property int menuIndex: -1
402 property int menuIndex: -1
403+ property var extendedData: menuData && menuData.ext || undefined
402 property bool serverChecked: menuData && menuData.isToggled || false
404 property bool serverChecked: menuData && menuData.isToggled || false
403
405
404 text: menuData && menuData.label || ""
406 text: menuData && menuData.label || ""
@@ -407,6 +409,25 @@ Item {
407 checked: serverChecked
409 checked: serverChecked
408 highlightWhenPressed: false
410 highlightWhenPressed: false
409
411
412+ property var subtitleAction: UnityMenuAction {
413+ model: menuModel
414+ index: menuIndex
415+ name: getExtendedProperty(extendedData, "xCanonicalSubtitleAction", "")
416+ }
417+ subtitle.text: subtitleAction.valid ? subtitleAction.state : ""
418+
419+ onMenuModelChanged: {
420+ loadAttributes();
421+ }
422+ onMenuIndexChanged: {
423+ loadAttributes();
424+ }
425+
426+ function loadAttributes() {
427+ if (!menuModel || menuIndex == -1) return;
428+ menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-subtitle-action': 'string'});
429+ }
430+
410 ServerPropertySynchroniser {
431 ServerPropertySynchroniser {
411 objectName: "sync"
432 objectName: "sync"
412 syncTimeout: Utils.Constants.indicatorValueTimeout
433 syncTimeout: Utils.Constants.indicatorValueTimeout
qml/Shell.qml+1−1
@@ -124,7 +124,7 @@ StyledItem {
124 greeter.notifyAppFocusRequested(appId);
124 greeter.notifyAppFocusRequested(appId);
125
125
126 panel.indicators.hide();
126 panel.indicators.hide();
127− launcher.hide();
127+ launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
128 }
128 }
129
129
130 // For autopilot consumption
130 // For autopilot consumption
qml/Stage/MoveHandler.qml+2−2
@@ -90,8 +90,8 @@ QtObject {
90 if (target.anyMaximized) {
90 if (target.anyMaximized) {
91 // keep distanceX relative to the normal window width minus the window control buttons (+spacing)
91 // keep distanceX relative to the normal window width minus the window control buttons (+spacing)
92 // so that dragging it back doesn't make the window jump around to weird positions, away from the mouse pointer
92 // so that dragging it back doesn't make the window jump around to weird positions, away from the mouse pointer
93− priv.distanceX = MathUtils.clampAndProject(pos.x, 0, target.width, buttonsWidth, target.resizeArea.normalWidth);
93+ priv.distanceX = MathUtils.clampAndProject(pos.x, 0, target.width, buttonsWidth, target.normalWidth);
94− priv.distanceY = MathUtils.clampAndProject(pos.y, 0, target.height, 0, target.resizeArea.normalHeight);
94+ priv.distanceY = MathUtils.clampAndProject(pos.y, 0, target.height, 0, target.normalHeight);
95 } else {
95 } else {
96 priv.distanceX = pos.x;
96 priv.distanceX = pos.x;
97 priv.distanceY = pos.y;
97 priv.distanceY = pos.y;
qml/Stage/Stage.qml+62−32
@@ -712,6 +712,29 @@ FocusScope {
712 property real restoredX
712 property real restoredX
713 property real restoredY
713 property real restoredY
714
714
715+ // Keeps track of the window geometry while in normal or restored state
716+ // Useful when returning from some maxmized state or when saving the geometry while maximized
717+ // FIXME: find a better solution
718+ property real normalX: 0
719+ property real normalY: 0
720+ property real normalWidth: 0
721+ property real normalHeight: 0
722+ function updateNormalGeometry() {
723+ if (appDelegate.state == "normal" || appDelegate.state == "restored") {
724+ normalX = appDelegate.requestedX;
725+ normalY = appDelegate.requestedY;
726+ normalWidth = appDelegate.width;
727+ normalHeight = appDelegate.height;
728+ }
729+ }
730+ Connections {
731+ target: appDelegate
732+ onXChanged: appDelegate.updateNormalGeometry();
733+ onYChanged: appDelegate.updateNormalGeometry();
734+ onWidthChanged: appDelegate.updateNormalGeometry();
735+ onHeightChanged: appDelegate.updateNormalGeometry();
736+ }
737+
715 Binding {
738 Binding {
716 target: appDelegate
739 target: appDelegate
717 property: "y"
740 property: "y"
@@ -852,6 +875,16 @@ FocusScope {
852 MirFocusController.focusedSurface = null;
875 MirFocusController.focusedSurface = null;
853 }
876 }
854 }
877 }
878+
879+ WindowStateSaver {
880+ id: windowStateSaver
881+ target: appDelegate
882+ screenWidth: appContainer.width
883+ screenHeight: appContainer.height
884+ leftMargin: root.leftMargin
885+ minimumY: PanelState.panelHeight
886+ }
887+
855 Component.onCompleted: {
888 Component.onCompleted: {
856 if (application && application.rotatesWindowContents) {
889 if (application && application.rotatesWindowContents) {
857 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
890 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
@@ -863,7 +896,7 @@ FocusScope {
863 windowedX = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedX + units.gu(3) : (normalZ - 1) * units.gu(3)
896 windowedX = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedX + units.gu(3) : (normalZ - 1) * units.gu(3)
864 windowedY = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedY + units.gu(3) : normalZ * units.gu(3)
897 windowedY = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedY + units.gu(3) : normalZ * units.gu(3)
865 // Now load any saved state. This needs to happen *after* the cascading!
898 // Now load any saved state. This needs to happen *after* the cascading!
866− resizeArea.loadWindowState();
899+ windowStateSaver.load();
867
900
868 // NB: We're differentiating if this delegate was created in response to a new entry in the model
901 // NB: We're differentiating if this delegate was created in response to a new entry in the model
869 // or if the Repeater is just populating itself with delegates to match the model it received.
902 // or if the Repeater is just populating itself with delegates to match the model it received.
@@ -880,6 +913,8 @@ FocusScope {
880 _constructing = false;
913 _constructing = false;
881 }
914 }
882 Component.onDestruction: {
915 Component.onDestruction: {
916+ windowStateSaver.save();
917+
883 if (!root.parent) {
918 if (!root.parent) {
884 // This stage is about to be destroyed. Don't mess up with the model at this point
919 // This stage is about to be destroyed. Don't mess up with the model at this point
885 return;
920 return;
@@ -1482,27 +1517,22 @@ FocusScope {
1482 id: resizeArea
1517 id: resizeArea
1483 objectName: "windowResizeArea"
1518 objectName: "windowResizeArea"
1484
1519
1520+ anchors.fill: appDelegate
1521+
1485 // workaround so that it chooses the correct resize borders when you drag from a corner ResizeGrip
1522 // workaround so that it chooses the correct resize borders when you drag from a corner ResizeGrip
1486 anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
1523 anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
1487
1524
1488 target: appDelegate
1525 target: appDelegate
1526+ minimumY: PanelState.panelHeight // disallow resizing up past Panel
1489 minWidth: units.gu(10)
1527 minWidth: units.gu(10)
1490 minHeight: units.gu(10)
1528 minHeight: units.gu(10)
1491 borderThickness: units.gu(2)
1529 borderThickness: units.gu(2)
1492− windowId: model.application.appId // FIXME: Change this to point to windowId once we have such a thing
1493− screenWidth: appContainer.width
1494− screenHeight: appContainer.height
1495− leftMargin: root.leftMargin
1496 enabled: false
1530 enabled: false
1497 visible: enabled
1531 visible: enabled
1498
1532
1499 onPressed: {
1533 onPressed: {
1500 appDelegate.focus = true;
1534 appDelegate.focus = true;
1501 }
1535 }
1502−
1503− Component.onDestruction: {
1504− saveWindowState();
1505− }
1506 }
1536 }
1507
1537
1508 DecoratedWindow {
1538 DecoratedWindow {
@@ -1652,10 +1682,10 @@ FocusScope {
1652 }
1682 }
1653 }
1683 }
1654
1684
1655− Image {
1685+ MouseArea {
1656− id: closeImage
1686+ id: closeMouseArea
1687+ objectName: "closeMouseArea"
1657 anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset }
1688 anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset }
1658− source: "graphics/window-close.svg"
1659 readonly property var mousePos: hoverMouseArea.mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1689 readonly property var mousePos: hoverMouseArea.mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1660 visible: !appDelegate.isDash && dragArea.distance == 0
1690 visible: !appDelegate.isDash && dragArea.distance == 0
1661 && index == spreadItem.highlightedIndex
1691 && index == spreadItem.highlightedIndex
@@ -1663,20 +1693,20 @@ FocusScope {
1663 && mousePos.y > -units.gu(4)
1693 && mousePos.y > -units.gu(4)
1664 && mousePos.x > -units.gu(4)
1694 && mousePos.x > -units.gu(4)
1665 && mousePos.x < (decoratedWindow.width * 2 / 3)
1695 && mousePos.x < (decoratedWindow.width * 2 / 3)
1666− height: units.gu(2)
1696+ height: units.gu(6)
1667 width: height
1697 width: height
1668− sourceSize.width: width
1698+
1669− sourceSize.height: height
1670−
1671− MouseArea {
1672− id: closeMouseArea
1673− objectName: "closeMouseArea"
1674− anchors.fill: closeImage
1675− anchors.margins: -units.gu(2)
1676− onClicked: {
1699+ onClicked: {
1677− priv.closingIndex = index;
1700+ priv.closingIndex = index;
1678− appDelegate.close();
1701+ appDelegate.close();
1679− }
1702+ }
1703+ Image {
1704+ id: closeImage
1705+ source: "graphics/window-close.svg"
1706+ anchors.fill: closeMouseArea
1707+ anchors.margins: units.gu(2)
1708+ sourceSize.width: width
1709+ sourceSize.height: height
1680 }
1710 }
1681 }
1711 }
1682 }
1712 }
@@ -1711,13 +1741,13 @@ FocusScope {
1711 }
1741 }
1712
1742
1713 // Find the hovered item and mark it active
1743 // Find the hovered item and mark it active
1714− var mapped = mapToItem(appContainer, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1744+ for (var i = appRepeater.count - 1; i >= 0; i--) {
1715− var itemUnder = appContainer.childAt(mapped.x, mapped.y)
1745+ var appDelegate = appRepeater.itemAt(i);
1716− if (itemUnder) {
1717− mapped = mapToItem(itemUnder, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1746+ var mapped = mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1718− var delegateChild = itemUnder.childAt(mapped.x, mapped.y)
1747+ var itemUnder = appDelegate.childAt(mapped.x, mapped.y);
1719− if (delegateChild && (delegateChild.objectName === "dragArea" || delegateChild.objectName === "windowInfoItem")) {
1748+ if (itemUnder && (itemUnder.objectName === "dragArea" || itemUnder.objectName === "windowInfoItem" || itemUnder.objectName == "closeMouseArea")) {
1720− spreadItem.highlightedIndex = appRepeater.indexOf(itemUnder)
1749+ spreadItem.highlightedIndex = i;
1750+ break;
1721 }
1751 }
1722 }
1752 }
1723
1753
qml/Stage/WindowResizeArea.qml+2−70
@@ -18,91 +18,23 @@ import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19import Utils 0.1
19import Utils 0.1
20import Unity.Application 0.1 // for Mir.cursorName
20import Unity.Application 0.1 // for Mir.cursorName
21−import "../Components/PanelState"
22
21
23MouseArea {
22MouseArea {
24 id: root
23 id: root
25
24
26− anchors.fill: target
27 anchors.margins: -borderThickness
25 anchors.margins: -borderThickness
28
26
29 hoverEnabled: target && !target.maximized // don't grab the resize under the panel
27 hoverEnabled: target && !target.maximized // don't grab the resize under the panel
30
28
31− property var windowStateStorage: WindowStateStorage
32 readonly property alias dragging: d.dragging
29 readonly property alias dragging: d.dragging
33− readonly property alias normalWidth: priv.normalWidth
34− readonly property alias normalHeight: priv.normalHeight
35
30
36 // The target item managed by this. Must be a parent or a sibling
31 // The target item managed by this. Must be a parent or a sibling
37 // The area will anchor to it and manage resize events
32 // The area will anchor to it and manage resize events
38 property Item target: null
33 property Item target: null
39− property string windowId: ""
40 property int borderThickness: 0
34 property int borderThickness: 0
35+ property real minimumY: -100000000 // By default, impose no limit
41 property int minWidth: 0
36 property int minWidth: 0
42 property int minHeight: 0
37 property int minHeight: 0
43− property int defaultWidth: units.gu(60)
44− property int defaultHeight: units.gu(50)
45− property int screenWidth: 0
46− property int screenHeight: 0
47− property int leftMargin: 0
48−
49− QtObject {
50− id: priv
51− objectName: "priv"
52−
53− property int normalX: 0
54− property int normalY: 0
55− property int normalWidth: 0
56− property int normalHeight: 0
57−
58− function updateNormalGeometry() {
59− if (root.target.state == "normal" || root.target.state == "restored") {
60− normalX = root.target.requestedX
61− normalY = root.target.requestedY
62− normalWidth = root.target.width
63− normalHeight = root.target.height
64− }
65− }
66− }
67−
68− Connections {
69− target: root.target
70− onXChanged: priv.updateNormalGeometry();
71− onYChanged: priv.updateNormalGeometry();
72− onWidthChanged: priv.updateNormalGeometry();
73− onHeightChanged: priv.updateNormalGeometry();
74− }
75−
76− function loadWindowState() {
77− var windowGeometry = windowStateStorage.getGeometry(root.windowId,
78− Qt.rect(target.windowedX, target.windowedY, defaultWidth, defaultHeight));
79−
80−
81− target.windowedWidth = Qt.binding(function() { return Math.min(Math.max(windowGeometry.width, d.minimumWidth), screenWidth - root.leftMargin); });
82− target.windowedHeight = Qt.binding(function() { return Math.min(Math.max(windowGeometry.height, d.minimumHeight),
83− root.screenHeight - (target.fullscreen ? 0 : PanelState.panelHeight)); });
84− target.windowedX = Qt.binding(function() { return Math.max(Math.min(windowGeometry.x, root.screenWidth - root.leftMargin - target.windowedWidth),
85− (target.fullscreen ? 0 : root.leftMargin)); });
86− target.windowedY = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.windowedHeight), PanelState.panelHeight); });
87−
88− var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
89− target.restore(false /* animated */, windowState);
90−
91− priv.updateNormalGeometry();
92−
93− // initialize the x/y to restore to
94− target.restoredX = priv.normalX;
95− target.restoredY = priv.normalY;
96− }
97−
98− function saveWindowState() {
99− var state = target.windowState;
100− if (state === WindowStateStorage.WindowStateRestored) {
101− state = WindowStateStorage.WindowStateNormal;
102− }
103− windowStateStorage.saveState(root.windowId, state & ~WindowStateStorage.WindowStateMinimized); // clear the minimized bit when saving
104− windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight));
105− }
106
38
107 QtObject {
39 QtObject {
108 id: d
40 id: d
@@ -311,7 +243,7 @@ MouseArea {
311 }
243 }
312
244
313 if (d.topBorder) {
245 if (d.topBorder) {
314− var newTargetY = Math.max(d.startY + deltaY, PanelState.panelHeight); // disallow resizing up past Panel
246+ var newTargetY = Math.max(d.startY + deltaY, root.minimumY);
315 var bottomBorderY = target.windowedY + target.height;
247 var bottomBorderY = target.windowedY + target.height;
316 if (bottomBorderY > newTargetY + d.minimumHeight) {
248 if (bottomBorderY > newTargetY + d.minimumHeight) {
317 if (bottomBorderY < newTargetY + d.maximumHeight) {
249 if (bottomBorderY < newTargetY + d.maximumHeight) {
qml/Stage/WindowStateSaver.qmlnew file+63
@@ -0,0 +1,63 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import Utils 0.1
20+
21+QtObject {
22+ id: root
23+
24+ // set from outside
25+ property var target
26+ property int screenWidth: 0
27+ property int screenHeight: 0
28+ property int leftMargin: 0
29+ property int minimumY: 0
30+
31+ function load() {
32+ var defaultWidth = units.gu(60);
33+ var defaultHeight = units.gu(50);
34+ var windowGeometry = WindowStateStorage.getGeometry(target.appId,
35+ Qt.rect(target.windowedX, target.windowedY, defaultWidth, defaultHeight));
36+
37+ target.windowedWidth = Qt.binding(function() { return Math.min(Math.max(windowGeometry.width, target.minimumWidth), screenWidth - root.leftMargin); });
38+ target.windowedHeight = Qt.binding(function() { return Math.min(Math.max(windowGeometry.height, target.minimumHeight),
39+ screenHeight - (target.fullscreen ? 0 : minimumY)); });
40+ target.windowedX = Qt.binding(function() { return Math.max(Math.min(windowGeometry.x, screenWidth - root.leftMargin - target.windowedWidth),
41+ (target.fullscreen ? 0 : root.leftMargin)); });
42+ target.windowedY = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, screenHeight - target.windowedHeight), minimumY); });
43+
44+ var windowState = WindowStateStorage.getState(target.appId, WindowStateStorage.WindowStateNormal)
45+ target.restore(false /* animated */, windowState);
46+
47+ target.updateNormalGeometry();
48+
49+ // initialize the x/y to restore to
50+ target.restoredX = target.normalX;
51+ target.restoredY = target.normalY;
52+ }
53+
54+ function save() {
55+ var state = target.windowState;
56+ if (state === WindowStateStorage.WindowStateRestored) {
57+ state = WindowStateStorage.WindowStateNormal;
58+ }
59+
60+ WindowStateStorage.saveState(target.appId, state & ~WindowStateStorage.WindowStateMinimized); // clear the minimized bit when saving
61+ WindowStateStorage.saveGeometry(target.appId, Qt.rect(target.normalX, target.normalY, target.normalWidth, target.normalHeight));
62+ }
63+}
src/ShellApplication.cpp+1
@@ -36,6 +36,7 @@ ShellApplication::ShellApplication(int & argc, char ** argv, bool isMirServer)
36{
36{
37
37
38 setApplicationName(QStringLiteral("unity8"));
38 setApplicationName(QStringLiteral("unity8"));
39+ setOrganizationName(QStringLiteral("Canonical"));
39
40
40 connect(this, &QGuiApplication::screenAdded, this, &ShellApplication::onScreenAdded);
41 connect(this, &QGuiApplication::screenAdded, this, &ShellApplication::onScreenAdded);
41
42
tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.cpp+1−1
@@ -22,6 +22,7 @@ namespace QLightDM
22
22
23SessionsModelPrivate::SessionsModelPrivate(SessionsModel* parent)
23SessionsModelPrivate::SessionsModelPrivate(SessionsModel* parent)
24 : testScenario("singleSession")
24 : testScenario("singleSession")
25+ , q_ptr(parent)
25 , m_availableSessions(
26 , m_availableSessions(
26 {
27 {
27 {"ubuntu", "", "Ubuntu", ""},
28 {"ubuntu", "", "Ubuntu", ""},
@@ -36,7 +37,6 @@ SessionsModelPrivate::SessionsModelPrivate(SessionsModel* parent)
36 {"xterm", "", "Recovery Console", ""},
37 {"xterm", "", "Recovery Console", ""},
37 {"", "", "Unknown?", ""}
38 {"", "", "Unknown?", ""}
38 })
39 })
39− , q_ptr(parent)
40{
40{
41 numSessions = numAvailableSessions();
41 numSessions = numAvailableSessions();
42 resetEntries();
42 resetEntries();
tests/mocks/LightDM/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp+23−23
@@ -73,7 +73,7 @@ void UsersModelPrivate::resetEntries_single()
73{
73{
74 entries =
74 entries =
75 {
75 {
76− { "single", "Single User", 0, 0, false, false, "ubuntu", 0 },
76+ { "single", "Single User", 0, 0, false, false, "ubuntu", 0, 0 },
77 };
77 };
78}
78}
79
79
@@ -81,7 +81,7 @@ void UsersModelPrivate::resetEntries_singlePassphrase()
81{
81{
82 entries =
82 entries =
83 {
83 {
84− { "single", "Single User", 0, 0, false, false, "ubuntu", 0 },
84+ { "single", "Single User", 0, 0, false, false, "ubuntu", 0, 0 },
85 };
85 };
86}
86}
87
87
@@ -89,7 +89,7 @@ void UsersModelPrivate::resetEntries_singlePin()
89{
89{
90 entries =
90 entries =
91 {
91 {
92− { "has-pin", "Has PIN", 0, 0, false, false, "ubuntu", 0 },
92+ { "has-pin", "Has PIN", 0, 0, false, false, "ubuntu", 0, 0 },
93 };
93 };
94}
94}
95
95
@@ -97,27 +97,27 @@ void UsersModelPrivate::resetEntries_full()
97{
97{
98 entries =
98 entries =
99 {
99 {
100− { "has-password", "Has Password", 0, 0, false, false, "ubuntu", 0 },
100+ { "has-password", "Has Password", 0, 0, false, false, "ubuntu", 0, 0 },
101− { "has-pin", "Has PIN", 0, 0, false, false, "ubuntu", 0 },
101+ { "has-pin", "Has PIN", 0, 0, false, false, "ubuntu", 0, 0 },
102− { "different-prompt", "Different Prompt", 0, 0, false, false, "ubuntu", 0 },
102+ { "different-prompt", "Different Prompt", 0, 0, false, false, "ubuntu", 0, 0 },
103− { "no-password", "No Password", 0, 0, false, false, "ubuntu", 0 },
103+ { "no-password", "No Password", 0, 0, false, false, "ubuntu", 0, 0 },
104− { "auth-error", "Auth Error", 0, 0, false, false, "ubuntu", 0 },
104+ { "auth-error", "Auth Error", 0, 0, false, false, "ubuntu", 0, 0 },
105− { "two-factor", "Two Factor", 0, 0, false, false, "ubuntu", 0 },
105+ { "two-factor", "Two Factor", 0, 0, false, false, "ubuntu", 0, 0 },
106− { "info-prompt", "Info Prompt", 0, 0, false, false, "ubuntu", 0 },
106+ { "info-prompt", "Info Prompt", 0, 0, false, false, "ubuntu", 0, 0 },
107− { "html-info-prompt", "HTML Info Prompt", 0, 0, false, false, "ubuntu", 0 },
107+ { "html-info-prompt", "HTML Info Prompt", 0, 0, false, false, "ubuntu", 0, 0 },
108− { "long-info-prompt", "Long Info Prompt", 0, 0, false, false, "ubuntu", 0 },
108+ { "long-info-prompt", "Long Info Prompt", 0, 0, false, false, "ubuntu", 0, 0 },
109− { "wide-info-prompt", "Wide Info Prompt", 0, 0, false, false, "ubuntu", 0 },
109+ { "wide-info-prompt", "Wide Info Prompt", 0, 0, false, false, "ubuntu", 0, 0 },
110− { "multi-info-prompt", "Multi Info Prompt", 0, 0, false, false, "ubuntu", 0 },
110+ { "multi-info-prompt", "Multi Info Prompt", 0, 0, false, false, "ubuntu", 0, 0 },
111− { "long-name", "Long name (far far too long to fit, seriously this would never fit on the screen, you will never see this part of the name)", 0, 0, false, false, "ubuntu", 0 },
111+ { "long-name", "Long name (far far too long to fit, seriously this would never fit on the screen, you will never see this part of the name)", 0, 0, false, false, "ubuntu", 0, 0 },
112− { "color-background", "Color Background", "#E95420", 0, false, false, "ubuntu", 0 },
112+ { "color-background", "Color Background", "#E95420", 0, false, false, "ubuntu", 0, 0 },
113 // white and black are a bit redundant, but useful for manually testing if UI is still readable
113 // white and black are a bit redundant, but useful for manually testing if UI is still readable
114− { "white-background", "White Background", "#ffffff", 0, false, false, "ubuntu", 0 },
114+ { "white-background", "White Background", "#ffffff", 0, false, false, "ubuntu", 0, 0 },
115− { "black-background", "Black Background", "#000000", 0, false, false, "ubuntu", 0 },
115+ { "black-background", "Black Background", "#000000", 0, false, false, "ubuntu", 0, 0 },
116− { "no-background", "No Background", "", 0, false, false, "ubuntu", 0 },
116+ { "no-background", "No Background", "", 0, false, false, "ubuntu", 0, 0 },
117− { "unicode", "가나다라마", 0, 0, false, false, "ubuntu", 0 },
117+ { "unicode", "가나다라마", 0, 0, false, false, "ubuntu", 0, 0 },
118− { "no-response", "No Response", 0, 0, false, false, "ubuntu", 0 },
118+ { "no-response", "No Response", 0, 0, false, false, "ubuntu", 0, 0 },
119− { "empty-name", "", 0, 0, false, false, "ubuntu", 0 },
119+ { "empty-name", "", 0, 0, false, false, "ubuntu", 0, 0 },
120− { "active", "Active Account", 0, 0, true, false, "ubuntu", 0 },
120+ { "active", "Active Account", 0, 0, true, false, "ubuntu", 0, 0 },
121 };
121 };
122}
122}
123
123
tests/mocks/QtMultimedia/videooutput.h+1−1
@@ -31,7 +31,7 @@ public:
31 QObject *source() const { return m_source.data(); }
31 QObject *source() const { return m_source.data(); }
32 void setSource(QObject *source);
32 void setSource(QObject *source);
33
33
34− void itemChange(ItemChange change, const ItemChangeData & value);
34+ void itemChange(ItemChange change, const ItemChangeData & value) override;
35
35
36Q_SIGNALS:
36Q_SIGNALS:
37 void sourceChanged();
37 void sourceChanged();
tests/plugins/Utils/WindowInputMonitorTest.cpp+76−9
@@ -25,14 +25,15 @@ class FakeElapsedTimer : public AbstractElapsedTimer {
25public:
25public:
26 static qint64 msecsSinceEpoch;
26 static qint64 msecsSinceEpoch;
27
27
28− FakeElapsedTimer() { start(); }
28+ FakeElapsedTimer() {}
29
29
30− void start() override { m_msecsSinceReference = msecsSinceEpoch; }
30+ void start() override { m_msecsSinceReference = msecsSinceEpoch; m_valid = true; }
31 qint64 msecsSinceReference() const override { return m_msecsSinceReference; }
31 qint64 msecsSinceReference() const override { return m_msecsSinceReference; }
32− qint64 elapsed() const override { return msecsSinceEpoch - m_msecsSinceReference; }
32+ qint64 elapsed() const override { return m_valid ? msecsSinceEpoch - m_msecsSinceReference : qrand(); }
33
33
34private:
34private:
35 qint64 m_msecsSinceReference;
35 qint64 m_msecsSinceReference;
36+ bool m_valid{false};
36};
37};
37qint64 FakeElapsedTimer::msecsSinceEpoch = 0;
38qint64 FakeElapsedTimer::msecsSinceEpoch = 0;
38
39
@@ -48,11 +49,14 @@ public:
48 int interval() const override;
49 int interval() const override;
49 void setInterval(int msecs) override;
50 void setInterval(int msecs) override;
50 void start() override;
51 void start() override;
52+ void stop() override;
53+ bool isRunning() const override;
51 bool isSingleShot() const override;
54 bool isSingleShot() const override;
52 void setSingleShot(bool value) override;
55 void setSingleShot(bool value) override;
53private:
56private:
54 int m_interval;
57 int m_interval;
55 bool m_singleShot;
58 bool m_singleShot;
59+ bool m_isRunning;
56 qint64 m_nextTimeoutTime;
60 qint64 m_nextTimeoutTime;
57};
61};
58
62
@@ -85,6 +89,8 @@ private Q_SLOTS:
85 void tapWhileTouching();
89 void tapWhileTouching();
86 void multipleHomeKeys();
90 void multipleHomeKeys();
87
91
92+ void repeatedSuperPress();
93+
88private:
94private:
89 void passTime(qint64 timeSpanMs);
95 void passTime(qint64 timeSpanMs);
90
96
@@ -98,11 +104,10 @@ void WindowInputMonitorTest::init()
98
104
99void WindowInputMonitorTest::cleanup()
105void WindowInputMonitorTest::cleanup()
100{
106{
101− delete m_fakeTimerFactory;
107+ delete m_fakeTimerFactory;
102 m_fakeTimerFactory = nullptr;
108 m_fakeTimerFactory = nullptr;
103}
109}
104
110
105−
106void WindowInputMonitorTest::passTime(qint64 timeSpanMs)
111void WindowInputMonitorTest::passTime(qint64 timeSpanMs)
107{
112{
108 qint64 finalTime = FakeElapsedTimer::msecsSinceEpoch + timeSpanMs;
113 qint64 finalTime = FakeElapsedTimer::msecsSinceEpoch + timeSpanMs;
@@ -133,7 +138,7 @@ void WindowInputMonitorTest::touchTapTouch()
133 QFETCH(int, silenceAfterTap);
138 QFETCH(int, silenceAfterTap);
134 QFETCH(int, expectedActivatedCount);
139 QFETCH(int, expectedActivatedCount);
135 QFETCH(Qt::Key, key);
140 QFETCH(Qt::Key, key);
136− WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(), new FakeElapsedTimer);
141+ WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
137 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
142 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
138 QVERIFY(activatedSpy.isValid());
143 QVERIFY(activatedSpy.isValid());
139
144
@@ -194,7 +199,7 @@ void WindowInputMonitorTest::touchTapTouch()
194
199
195void WindowInputMonitorTest::tapWhileTouching()
200void WindowInputMonitorTest::tapWhileTouching()
196{
201{
197− WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(), new FakeElapsedTimer);
202+ WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
198 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
203 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
199 QVERIFY(activatedSpy.isValid());
204 QVERIFY(activatedSpy.isValid());
200
205
@@ -229,7 +234,7 @@ void WindowInputMonitorTest::tapWhileTouching()
229 */
234 */
230void WindowInputMonitorTest::multipleHomeKeys()
235void WindowInputMonitorTest::multipleHomeKeys()
231{
236{
232− WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(), new FakeElapsedTimer);
237+ WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
233 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
238 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
234 QVERIFY(activatedSpy.isValid());
239 QVERIFY(activatedSpy.isValid());
235
240
@@ -258,12 +263,64 @@ void WindowInputMonitorTest::multipleHomeKeys()
258 QCOMPARE(activatedSpy.count(), 1);
263 QCOMPARE(activatedSpy.count(), 1);
259}
264}
260
265
266+// regression test for lp:1607427
267+void WindowInputMonitorTest::repeatedSuperPress()
268+{
269+ WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
270+ QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
271+ QVERIFY(activatedSpy.isValid());
272+
273+ // 1st try
274+ passTime(1000);
275+ {
276+ QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier);
277+ homeKeyWatcher.update(&keyEvent);
278+ }
279+ passTime(50);
280+ {
281+ QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_Super_L, Qt::NoModifier);
282+ homeKeyWatcher.update(&keyEvent);
283+ }
284+ passTime(1000);
285+ QCOMPARE(activatedSpy.count(), 1);
286+
287+ // a touch event in between
288+ {
289+ QTouchEvent touchEvent(QEvent::TouchBegin);
290+ homeKeyWatcher.update(&touchEvent);
291+ }
292+ {
293+ QTouchEvent touchEvent(QEvent::TouchUpdate);
294+ homeKeyWatcher.update(&touchEvent);
295+ }
296+ {
297+ QTouchEvent touchEvent(QEvent::TouchEnd);
298+ homeKeyWatcher.update(&touchEvent);
299+ }
300+
301+ passTime(1000);
302+ // 2nd try
303+ activatedSpy.clear();
304+ {
305+ QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier);
306+ homeKeyWatcher.update(&keyEvent);
307+ }
308+ passTime(50);
309+ {
310+ QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_Super_L, Qt::NoModifier);
311+ homeKeyWatcher.update(&keyEvent);
312+ }
313+ passTime(1000);
314+ QCOMPARE(activatedSpy.count(), 1);
315+}
316+
261/////////////////////////////////// FakeTimer //////////////////////////////////
317/////////////////////////////////// FakeTimer //////////////////////////////////
262
318
263FakeTimer::FakeTimer(QObject *parent)
319FakeTimer::FakeTimer(QObject *parent)
264 : UnityUtil::AbstractTimer(parent)
320 : UnityUtil::AbstractTimer(parent)
265 , m_interval(0)
321 , m_interval(0)
266 , m_singleShot(true)
322 , m_singleShot(true)
323+ , m_isRunning(false)
267{
324{
268}
325}
269
326
@@ -285,10 +342,20 @@ void FakeTimer::update()
285
342
286void FakeTimer::start()
343void FakeTimer::start()
287{
344{
288− AbstractTimer::start();
345+ m_isRunning = true;
289 m_nextTimeoutTime = FakeElapsedTimer::msecsSinceEpoch + (qint64)interval();
346 m_nextTimeoutTime = FakeElapsedTimer::msecsSinceEpoch + (qint64)interval();
290}
347}
291
348
349+void FakeTimer::stop()
350+{
351+ m_isRunning = false;
352+}
353+
354+bool FakeTimer::isRunning() const
355+{
356+ return m_isRunning;
357+}
358+
292int FakeTimer::interval() const
359int FakeTimer::interval() const
293{
360{
294 return m_interval;
361 return m_interval;
tests/qmltests/Components/tst_VirtualTouchPad.qml+55−1
@@ -21,14 +21,20 @@ import UInput 0.1
21import "../../../qml/Components"
21import "../../../qml/Components"
22
22
23
23
24−Item {
24+Rectangle {
25 id: root
25 id: root
26 width: units.gu(70)
26 width: units.gu(70)
27 height: units.gu(70)
27 height: units.gu(70)
28+ color: "black"
28
29
29 VirtualTouchPad {
30 VirtualTouchPad {
30 id: touchScreenPad
31 id: touchScreenPad
31 anchors.fill: parent
32 anchors.fill: parent
33+ property int internalGu: units.gu(1)
34+ settings: QtObject {
35+ property bool touchpadTutorialHasRun: true
36+ property bool oskEnabled: true
37+ }
32 }
38 }
33
39
34 SignalSpy {
40 SignalSpy {
@@ -139,5 +145,53 @@ Item {
139 event.release(1, startX2, startY + units.gu(11));
145 event.release(1, startX2, startY + units.gu(11));
140 event.commit();
146 event.commit();
141 }
147 }
148+
149+ function test_tutorial() {
150+ var oskButton = findChild(touchScreenPad, "oskButton");
151+ var leftButton = findChild(touchScreenPad, "leftButton");
152+ var rightButton = findChild(touchScreenPad, "rightButton");
153+ var touchPad = findChild(touchScreenPad, "virtualTouchPad")
154+ var tutorial = findInvisibleChild(touchScreenPad, "tutorialAnimation")
155+ var tutorialFinger1 = findChild(touchScreenPad, "tutorialFinger1");
156+ var tutorialFinger2 = findChild(touchScreenPad, "tutorialFinger2");
157+ var tutorialLabel = findChild(touchScreenPad, "tutorialLabel");
158+ var tutorialImage = findChild(touchScreenPad, "tutorialImage");
159+
160+ mouseEventSpy1.signalName = "mousePressed"
161+ mouseEventSpy2.signalName = "mouseReleased"
162+
163+ mouseEventSpy1.clear();
164+ mouseEventSpy2.clear();
165+
166+ // run the tutorial
167+ touchScreenPad.runTutorial();
168+ tryCompare(tutorial, "running", true)
169+
170+ // Wait for it to pause
171+ tryCompare(tutorial, "paused", true)
172+
173+ // Click somewhere to make it continue
174+ mouseClick(root, root.width / 2, root.height /2)
175+ tryCompare(tutorial, "paused", false)
176+
177+ // Verify that the touchpad does NOT accept input while the tutorial is running
178+ mouseClick(root, root.width / 2, root.height /2)
179+ compare(mouseEventSpy1.count, 0)
180+ compare(mouseEventSpy2.count, 0)
181+
182+ // Wait for it to finish
183+ tryCompare(tutorial, "running", false, 60000)
184+
185+ // Make sure after the tutorial, all the visible states are proper
186+ tryCompare(oskButton, "visible", true)
187+ tryCompare(leftButton, "visible", true)
188+ tryCompare(rightButton, "visible", true)
189+
190+ tryCompare(tutorialFinger1, "visible", false)
191+ tryCompare(tutorialFinger2, "visible", false)
192+ tryCompare(tutorialImage, "visible", false)
193+ tryCompare(tutorialLabel, "visible", false)
194+ }
195+
142 }
196 }
143}
197}
tests/qmltests/Dash/Previews/tst_Preview.qml+63
@@ -37,6 +37,51 @@ Rectangle {
37 id: mockPreviewModel
37 id: mockPreviewModel
38 }
38 }
39
39
40+ ListModel {
41+ id: tracksModel
42+ Component.onCompleted:
43+ {
44+ tracksModel.append( { "type" : "audio1",
45+ "widgetId" : "audiopw1",
46+ "properties" : { "tracks" : [
47+ { title: "Some track name", length: "30", source: "/not/existinga/path/testsound1" }
48+ ]
49+ }
50+ }
51+ );
52+ tracksModel.append(
53+ { "type" : "audio",
54+ "widgetId" : "audiopw",
55+ "properties" : { "tracks" : [
56+ { title: "Some track name", length: "30", source: "/not/existing/path/testsound1" },
57+ { title: "Some other track name", subtitle: "Subtitle", length: "83", source: "/not/existing/path/testsound2" },
58+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound3" },
59+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound4" },
60+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound5" },
61+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound6" },
62+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound7" },
63+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound8" },
64+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound9" },
65+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound10" },
66+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound11" },
67+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound12" },
68+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound13" },
69+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound14" },
70+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound15" },
71+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound16" },
72+ { title: "And another one", length: "7425", source: "/not/existing/path/testsound17" }
73+ ]
74+ }
75+ } );
76+ }
77+ }
78+
79+ QtObject {
80+ id: audiosModel
81+ property int widgetColumnCount: 1
82+ property var columnModel: { tracksModel }
83+ }
84+
40 Preview {
85 Preview {
41 id: preview
86 id: preview
42 anchors.fill: parent
87 anchors.fill: parent
@@ -57,6 +102,7 @@ Rectangle {
57 function init() {
102 function init() {
58 var widget = findChild(preview, "previewListRow0");
103 var widget = findChild(preview, "previewListRow0");
59 widget.positionViewAtBeginning();
104 widget.positionViewAtBeginning();
105+ preview.previewModel = mockPreviewModel;
60 }
106 }
61
107
62 function test_triggered() {
108 function test_triggered() {
@@ -147,5 +193,22 @@ Rectangle {
147 skip("preview %1 or widget %2 have been destroyed, thus we can't safely continue this test".arg(preview_str).arg(widget_str))
193 skip("preview %1 or widget %2 have been destroyed, thus we can't safely continue this test".arg(preview_str).arg(widget_str))
148 }
194 }
149 }
195 }
196+
197+ function test_audios() {
198+ preview.previewModel = audiosModel;
199+ waitForRendering(preview);
200+
201+ // Scroll down
202+ var previewListRow0 = findChild(preview, "previewListRow0");
203+ flickToYEnd(previewListRow0);
204+
205+ var previewsContentY = previewListRow0.contentY;
206+
207+ var trackItem = findChild(preview, "trackItem16");
208+ mouseClick(findChild(trackItem, "playButton"));
209+
210+ expectFail("", "Clicking on a track should not change contentY.");
211+ tryCompareFunction(function () { return previewsContentY != previewListRow0.contentY; }, true);
212+ }
150 }
213 }
151}
214}
tests/qmltests/Dash/tst_Dash.qml+4−3
@@ -310,8 +310,9 @@ Item {
310 var stopX = units.gu(1)
310 var stopX = units.gu(1)
311 var stopY = startY;
311 var stopY = startY;
312 waitForRendering(dashContentList)
312 waitForRendering(dashContentList)
313− mouseFlick(dash, startX, startY, stopX, stopY);
313+ mouseFlick(dash, startX, startY, stopX, stopY, true, true, units.gu(200));
314+ compare(dashContentList.currentIndex, 1, "Could not flick to scope id 1");
314− mouseFlick(dash, startX, startY, stopX, stopY);
315+ mouseFlick(dash, startX, startY, stopX, stopY, units.gu(200));
315 compare(dashContentList.currentIndex, 2, "Could not flick to scope id 2");
316 compare(dashContentList.currentIndex, 2, "Could not flick to scope id 2");
316 var dashCommunicatorService = findInvisibleChild(dash, "dashCommunicatorService");
317 var dashCommunicatorService = findInvisibleChild(dash, "dashCommunicatorService");
317 dashCommunicatorService.mockSetCurrentScope(0, true, false);
318 dashCommunicatorService.mockSetCurrentScope(0, true, false);
@@ -411,7 +412,7 @@ Item {
411 compare(dashContentList.currentItem.scopeId, "clickscope");
412 compare(dashContentList.currentItem.scopeId, "clickscope");
412
413
413 // Move to second scope
414 // Move to second scope
414− mouseFlick(dash, dash.width / 2, units.gu(2), dash.width / 5, units.gu(2));
415+ mouseFlick(dash, dash.width / 2, units.gu(2), dash.width / 5, units.gu(2), true, true, units.gu(20));
415 tryCompare(dashContentList, "currentIndex", 1);
416 tryCompare(dashContentList, "currentIndex", 1);
416 compare(dashContentList.currentItem.scopeId, "MockScope1");
417 compare(dashContentList.currentItem.scopeId, "MockScope1");
417 }
418 }
tests/qmltests/Launcher/tst_Launcher.qml+3−3
@@ -824,7 +824,7 @@ Rectangle {
824
824
825 // Dragging a bit (> 1.5 gu)
825 // Dragging a bit (> 1.5 gu)
826 newMouseX -= units.gu(2)
826 newMouseX -= units.gu(2)
827− mouseFlick(launcher, currentMouseX, currentMouseY, newMouseX, newMouseY, false, false, 100)
827+ mouseFlick(launcher, currentMouseX, currentMouseY, newMouseX, newMouseY, false, false)
828 currentMouseX = newMouseX
828 currentMouseX = newMouseX
829
829
830 // Other items need to expand and become 0.6 opaque
830 // Other items need to expand and become 0.6 opaque
@@ -832,8 +832,8 @@ Rectangle {
832 tryCompare(item0, "itemOpacity", 0.6)
832 tryCompare(item0, "itemOpacity", 0.6)
833
833
834 // Dragging a bit more
834 // Dragging a bit more
835− newMouseY += initialItemHeight * 1.5
835+ newMouseY += initialItemHeight * 1.25
836− mouseFlick(launcher, currentMouseX, currentMouseY, newMouseX, newMouseY, false, false, 100)
836+ mouseFlick(launcher, currentMouseX, currentMouseY, newMouseX, newMouseY, false, false)
837 currentMouseY = newMouseY
837 currentMouseY = newMouseY
838
838
839 tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 1)
839 tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 1)
tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+6−1
@@ -370,7 +370,8 @@ Item {
370 function test_create_switchMenu_data() {
370 function test_create_switchMenu_data() {
371 return [
371 return [
372 {label: "testLabel1", enabled: true, checked: false, icon: "file:///testIcon1" },
372 {label: "testLabel1", enabled: true, checked: false, icon: "file:///testIcon1" },
373− {label: "testLabel2", enabled: false, checked: true, icon: "file:///testIcon2" },
373+ {label: "testLabel2", enabled: false, checked: true, icon: "file:///testIcon2", subtitleAction: "action::subtitle1" },
374+ {label: "testLabel2", enabled: false, checked: true, icon: "file:///testIcon3", subtitleAction: "action::subtitle2" },
374 ];
375 ];
375 }
376 }
376
377
@@ -380,6 +381,9 @@ Item {
380 menuData.sensitive = data.enabled;
381 menuData.sensitive = data.enabled;
381 menuData.icon = data.icon;
382 menuData.icon = data.icon;
382 menuData.isToggled = data.checked;
383 menuData.isToggled = data.checked;
384+ menuData.ext = {
385+ 'xCanonicalSubtitleAction': data.subtitleAction,
386+ };
383
387
384 loader.data = menuData;
388 loader.data = menuData;
385 loader.sourceComponent = factory.load(menuData);
389 loader.sourceComponent = factory.load(menuData);
@@ -390,6 +394,7 @@ Item {
390 compare(loader.item.iconSource, data.icon, "Icon does not match data");
394 compare(loader.item.iconSource, data.icon, "Icon does not match data");
391 compare(loader.item.checked, data.checked, "Checked does not match data");
395 compare(loader.item.checked, data.checked, "Checked does not match data");
392 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
396 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
397+ compare(loader.item.subtitleAction.name, data.subtitleAction === undefined ? "" : data.subtitleAction, "Subtitle action does not match data");
393 }
398 }
394
399
395 function test_create_alarmMenu_data() {
400 function test_create_alarmMenu_data() {
tests/qmltests/Stage/tst_ApplicationWindow.qml+2−20
@@ -53,6 +53,8 @@ Rectangle {
53 surfaceOrientationAngle: 0
53 surfaceOrientationAngle: 0
54 interactive: true
54 interactive: true
55 focus: true
55 focus: true
56+ requestedWidth: width
57+ requestedHeight: height
56 Component.onDestruction: {
58 Component.onDestruction: {
57 applicationWindowLoader.itemDestroyed = true;
59 applicationWindowLoader.itemDestroyed = true;
58 }
60 }
@@ -150,26 +152,6 @@ Rectangle {
150 }
152 }
151 }
153 }
152 }
154 }
153−
154− Button {
155− anchors { left: parent.left; right: parent.right }
156− activeFocusOnPress: false
157− text: "Rotate device \u27F3"
158− onClicked: {
159− var orientation = applicationWindowLoader.item.orientation
160− if (orientation == Qt.PortraitOrientation) {
161− orientation = Qt.LandscapeOrientation;
162− } else if (orientation == Qt.LandscapeOrientation) {
163− orientation = Qt.InvertedPortraitOrientation;
164− } else if (orientation == Qt.InvertedPortraitOrientation) {
165− orientation = Qt.InvertedLandscapeOrientation;
166− } else {
167− orientation = Qt.PortraitOrientation;
168− }
169− applicationWindowLoader.item.orientation = orientation;
170− }
171− }
172−
173 }
155 }
174 }
156 }
175
157
tests/qmltests/Stage/tst_DesktopStage.qml+71
@@ -798,6 +798,77 @@ Item {
798 tryCompare(dialerAppDelegate, "state", "maximized");
798 tryCompare(dialerAppDelegate, "state", "maximized");
799 }
799 }
800
800
801+ function test_saveRestoreSize() {
802+ var originalWindowCount = topSurfaceList.count;
803+ var appDelegate = startApplication("dialer-app");
804+ compare(topSurfaceList.count, originalWindowCount + 1);
805+
806+ var initialWindowX = appDelegate.windowedX;
807+ var initialWindowY = appDelegate.windowedY;
808+ var initialWindowWidth = appDelegate.width
809+ var initialWindowHeight = appDelegate.height
810+
811+ var resizeDelta = units.gu(5)
812+ var startDragX = initialWindowX + initialWindowWidth + 1
813+ var startDragY = initialWindowY + initialWindowHeight + 1
814+ mouseFlick(root, startDragX, startDragY, startDragX + resizeDelta, startDragY + resizeDelta, true, true, units.gu(.5), 10);
815+
816+ tryCompare(appDelegate, "width", initialWindowWidth + resizeDelta);
817+ tryCompare(appDelegate, "height", initialWindowHeight + resizeDelta);
818+
819+ // Close the window and restart the application
820+ var closeButton = findChild(appDelegate, "closeWindowButton");
821+ appDelegate = null;
822+ verify(closeButton);
823+ mouseClick(closeButton);
824+ tryCompare(topSurfaceList, "count", originalWindowCount);
825+ wait(100); // plus some spare room
826+ appDelegate = startApplication("dialer-app");
827+
828+ // Make sure its size is again the same as before
829+ tryCompare(appDelegate, "width", initialWindowWidth + resizeDelta);
830+ tryCompare(appDelegate, "height", initialWindowHeight + resizeDelta);
831+ }
832+
833+ function test_saveRestoreMaximized() {
834+ var originalWindowCount = topSurfaceList.count;
835+ var appDelegate = startApplication("dialer-app");
836+ compare(topSurfaceList.count, originalWindowCount + 1);
837+
838+ var initialWindowX = appDelegate.windowedX;
839+ var initialWindowY = appDelegate.windowedY;
840+
841+ var moveDelta = units.gu(5);
842+
843+ appDelegate.windowedX = initialWindowX + moveDelta
844+ appDelegate.windowedY = initialWindowY + moveDelta
845+
846+ // Now change the state to maximized. The window should not keep updating the stored values
847+ maximizeAppDelegate(appDelegate);
848+
849+ // Close the window and restart the application
850+ var closeButton = findChild(appDelegate, "closeWindowButton");
851+ appDelegate = null;
852+ verify(closeButton);
853+ mouseClick(closeButton);
854+ closeButton = null;
855+ tryCompare(topSurfaceList, "count", originalWindowCount);
856+ wait(100); // plus some spare room
857+ appDelegate = startApplication("dialer-app");
858+
859+ // Make sure it's again where we left it in normal state before destroying
860+ tryCompare(appDelegate, "windowedX", initialWindowX + moveDelta)
861+ tryCompare(appDelegate, "windowedY", initialWindowY + moveDelta)
862+
863+ // Make sure maximize() has been called after restoring
864+ tryCompare(appDelegate, "state", "maximized")
865+
866+ // clean up
867+ // click on restore button (same one as maximize)
868+ var maximizeButton = findChild(appDelegate, "maximizeWindowButton");
869+ mouseClick(maximizeButton);
870+ }
871+
801 function test_grabbingCursorOnDecorationPress() {
872 function test_grabbingCursorOnDecorationPress() {
802 var appDelegate = startApplication("dialer-app");
873 var appDelegate = startApplication("dialer-app");
803 verify(appDelegate);
874 verify(appDelegate);
tests/qmltests/Stage/tst_WindowResizeArea.qml+1−66
@@ -88,22 +88,11 @@ Item {
88
88
89 WindowResizeArea {
89 WindowResizeArea {
90 id: windowResizeArea
90 id: windowResizeArea
91+ anchors.fill: parent
91 target: fakeWindow
92 target: fakeWindow
92 borderThickness: units.gu(2)
93 borderThickness: units.gu(2)
93 minWidth: units.gu(15)
94 minWidth: units.gu(15)
94 minHeight: units.gu(10)
95 minHeight: units.gu(10)
95− defaultWidth: units.gu(20)
96− defaultHeight: units.gu(20)
97− windowId: "test-window-id"
98− screenWidth: root.width
99− screenHeight: root.height
100−
101− Component.onCompleted: {
102− loadWindowState();
103− }
104− Component.onDestruction: {
105− saveWindowState();
106− }
107 }
96 }
108
97
109 Rectangle {
98 Rectangle {
@@ -312,30 +301,6 @@ Item {
312 compare(fakeWindow.requestedY, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
301 compare(fakeWindow.requestedY, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
313 }
302 }
314
303
315− function test_saveRestoreSize() {
316− var initialWindowX = fakeWindow.windowedX;
317− var initialWindowY = fakeWindow.windowedY;
318− var initialWindowWidth = fakeWindow.width
319− var initialWindowHeight = fakeWindow.height
320−
321− var resizeDelta = units.gu(5)
322− var startDragX = initialWindowX + initialWindowWidth + 1
323− var startDragY = initialWindowY + initialWindowHeight + 1
324− mouseFlick(root, startDragX, startDragY, startDragX + resizeDelta, startDragY + resizeDelta, true, true, units.gu(.5), 10);
325−
326− tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + resizeDelta, fakeWindow.resizeAreaMinWidth));
327− tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + resizeDelta, fakeWindow.resizeAreaMinHeight));
328−
329− // This will destroy the window and recreate it
330− windowLoader.active = false;
331− waitForRendering(root);
332− windowLoader.active = true;
333−
334− // Make sure its size is again the same as before
335− tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + resizeDelta, fakeWindow.resizeAreaMinWidth));
336− tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + resizeDelta, fakeWindow.resizeAreaMinHeight));
337− }
338−
339 // This tests if dragging smaller than minSize and then larger again, will keep the edge sticking
304 // This tests if dragging smaller than minSize and then larger again, will keep the edge sticking
340 // to the mouse, instead of immediately making the window grow again when switching direction
305 // to the mouse, instead of immediately making the window grow again when switching direction
341 function test_resizeSmallerAndLarger_data() {
306 function test_resizeSmallerAndLarger_data() {
@@ -361,36 +326,6 @@ Item {
361 tryCompare(fakeWindow, "height", initialWindowHeight);
326 tryCompare(fakeWindow, "height", initialWindowHeight);
362 }
327 }
363
328
364− function test_saveRestoreMaximized() {
365− var initialWindowX = fakeWindow.windowedX;
366− var initialWindowY = fakeWindow.windowedY;
367−
368− var moveDelta = units.gu(5);
369−
370− fakeWindow.windowedX = initialWindowX + moveDelta
371− fakeWindow.windowedY = initialWindowY + moveDelta
372−
373− // Now change the state to maximized. The window should not keep updating the stored values
374− fakeWindow.maximize()
375− fakeWindow.windowedX = 31415 // 0 is too risky to pass the test even when broken
376− fakeWindow.windowedY = 31415
377−
378− // This will destroy the window and recreate it
379− windowLoader.active = false;
380− waitForRendering(root);
381− windowLoader.active = true;
382−
383− // Make sure it's again where we left it in normal state before destroying
384− tryCompare(fakeWindow, "x", initialWindowX + moveDelta)
385− tryCompare(fakeWindow, "y", initialWindowX + moveDelta)
386−
387− // Make sure maximize() has been called after restoring
388− tryCompare(fakeWindow, "state", "maximized")
389−
390− // clean up
391− fakeWindow.restoreFromMaximized()
392− }
393−
394 function test_restoreMovesIntoBounds_data() {
329 function test_restoreMovesIntoBounds_data() {
395 return [
330 return [
396 {tag: "left off", x: -units.gu(5), y: units.gu(5), w: units.gu(10), h: units.gu(10)},
331 {tag: "left off", x: -units.gu(5), y: units.gu(5), w: units.gu(10), h: units.gu(10)},
tests/qmltests/tst_DisabledScreenNotice.qml+23−9
@@ -32,6 +32,12 @@ Item {
32 anchors.fill: parent
32 anchors.fill: parent
33 anchors.rightMargin: units.gu(40)
33 anchors.rightMargin: units.gu(40)
34
34
35+ // Mock some things here
36+ property int internalGu: units.gu(1)
37+ property var applicationArguments: QtObject {
38+ property string deviceName: "mako"
39+ }
40+
35 screen: QtObject {
41 screen: QtObject {
36 property int orientation: {
42 property int orientation: {
37 switch (orientationSelector.selectedIndex) {
43 switch (orientationSelector.selectedIndex) {
@@ -52,8 +58,9 @@ Item {
52 }
58 }
53
59
54 Column {
60 Column {
55− anchors.fill: parent
61+ anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
56− anchors.leftMargin: units.gu(40)
62+ width: units.gu(40)
63+ spacing: units.gu(1)
57
64
58 ItemSelector {
65 ItemSelector {
59 id: orientationSelector
66 id: orientationSelector
@@ -71,6 +78,20 @@ Item {
71 Layout.fillWidth: true
78 Layout.fillWidth: true
72 }
79 }
73 }
80 }
81+ Button {
82+ text: "Reset first run settings"
83+ onClicked: {
84+ var settings = testCase.findInvisibleChild(touchScreenPad, "virtualTouchPadSettings");
85+ settings.touchpadTutorialHasRun = false;
86+ }
87+ }
88+ Button {
89+ text: "Run tutorial now"
90+ onClicked: {
91+ var touchPad = testCase.findChild(touchScreenPad, "virtualTouchPad")
92+ touchPad.runTutorial();
93+ }
94+ }
74 }
95 }
75
96
76 UnityTestCase {
97 UnityTestCase {
@@ -78,13 +99,6 @@ Item {
78 name: "DisabledScreenNotice"
99 name: "DisabledScreenNotice"
79 when: windowShown
100 when: windowShown
80
101
81− function test_mouseAreaHidesOnFirstTap() {
82− var noticeArea = findChild(touchScreenPad, "infoNoticeArea")
83− compare(noticeArea.visible, true)
84− tap(root)
85− tryCompare(noticeArea, "visible", false)
86− }
87−
88 function test_rotation_data() {
102 function test_rotation_data() {
89 return [
103 return [
90 {tag: "portrait", selectedOrientation: 0, expectedAngle: 0},
104 {tag: "portrait", selectedOrientation: 0, expectedAngle: 0},
tests/qmltests/tst_Shell.qml+4−7
@@ -1857,14 +1857,11 @@ Rectangle {
1857 var appRepeater = findChild(shell, "appRepeater");
1857 var appRepeater = findChild(shell, "appRepeater");
1858 var appDelegate = appRepeater.itemAt(0);
1858 var appDelegate = appRepeater.itemAt(0);
1859
1859
1860− var resizeArea = findChild(appDelegate, "windowResizeArea");
1861− var priv = findInvisibleChild(resizeArea, "priv");
1862−
1863 // Make sure windows are at 0,0 or greater and they have a size that's > 0
1860 // Make sure windows are at 0,0 or greater and they have a size that's > 0
1864− compare(priv.normalX >= 0, true)
1861+ compare(appDelegate.normalX >= 0, true)
1865− compare(priv.normalY >= 0, true)
1862+ compare(appDelegate.normalY >= 0, true)
1866− compare(priv.normalWidth > 0, true)
1863+ compare(appDelegate.normalWidth > 0, true)
1867− compare(priv.normalHeight > 0, true)
1864+ compare(appDelegate.normalHeight > 0, true)
1868 }
1865 }
1869
1866
1870 // bug http://pad.lv/1431566
1867 // bug http://pad.lv/1431566
tests/uqmlscene/CMakeLists.txt+5−1
@@ -9,10 +9,14 @@ pkg_check_modules(XCB REQUIRED xcb)
9include_directories(
9include_directories(
10 ${CMAKE_CURRENT_BINARY_DIR}
10 ${CMAKE_CURRENT_BINARY_DIR}
11 ${CMAKE_SOURCE_DIR}/src
11 ${CMAKE_SOURCE_DIR}/src
12− ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
13 ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
12 ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
14)
13)
15
14
15+include_directories(
16+ SYSTEM
17+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
18+)
19+
16if (NOT "${XCB_INCLUDE_DIRS}" STREQUAL "")
20if (NOT "${XCB_INCLUDE_DIRS}" STREQUAL "")
17 set_target_properties(uqmlscene PROPERTIES INCLUDE_DIRECTORIES ${XCB_INCLUDE_DIRS})
21 set_target_properties(uqmlscene PROPERTIES INCLUDE_DIRECTORIES ${XCB_INCLUDE_DIRS})
18endif()
22endif()
tests/utils/modules/Unity/Test/UnityTestCase.qml+4−4
@@ -163,9 +163,9 @@ TestCase {
163 if (i === iterations - 1) {
163 if (i === iterations - 1) {
164 // Avoid any rounding errors by making the last move be at precisely
164 // Avoid any rounding errors by making the last move be at precisely
165 // the point specified
165 // the point specified
166− mouseMove(item, toX, toY, iterations / speed)
166+ mouseMove(item, toX, toY, timeStep)
167 } else {
167 } else {
168− mouseMove(item, x + (i + 1) * diffX, y + (i + 1) * diffY, iterations / speed)
168+ mouseMove(item, x + (i + 1) * diffX, y + (i + 1) * diffY, timeStep)
169 }
169 }
170 }
170 }
171 if (releaseMouse) {
171 if (releaseMouse) {
@@ -346,12 +346,12 @@ TestCase {
346 if (i === iterations - 1) {
346 if (i === iterations - 1) {
347 // Avoid any rounding errors by making the last move be at precisely
347 // Avoid any rounding errors by making the last move be at precisely
348 // the point specified
348 // the point specified
349− wait(iterations / speed)
349+ wait(timeStep)
350 var event = touchEvent(item)
350 var event = touchEvent(item)
351 event.move(0 /* touchId */, rootTo.x, rootTo.y)
351 event.move(0 /* touchId */, rootTo.x, rootTo.y)
352 event.commit()
352 event.commit()
353 } else {
353 } else {
354− wait(iterations / speed)
354+ wait(timeStep)
355 var event = touchEvent(item)
355 var event = touchEvent(item)
356 event.move(0 /* touchId */, rootFrom.x + (i + 1) * diffX, rootFrom.y + (i + 1) * diffY)
356 event.move(0 /* touchId */, rootFrom.x + (i + 1) * diffX, rootFrom.y + (i + 1) * diffY)
357 event.commit()
357 event.commit()