Merge lp:~nick-dedekind/ubuntu-settings-components/1390136.laggy-backends into lp:~registry/ubuntu-settings-components/trunk

Proposed by Nick Dedekind
Status: Merged
Approved by: Michał Sawicz
Approved revision: 99
Merged at revision: 86
Proposed branch: lp:~nick-dedekind/ubuntu-settings-components/1390136.laggy-backends
Merge into: lp:~registry/ubuntu-settings-components/trunk
Diff against target: 1467 lines (+1200/-42)
19 files modified
CMakeLists.txt (+1/-1)
cmake/modules/QmlPlugins.cmake (+33/-26)
debian/changelog (+13/-0)
plugins/CMakeLists.txt (+8/-0)
plugins/Ubuntu/Settings/CMakeLists.txt (+0/-7)
plugins/Ubuntu/Settings/Components/CMakeLists.txt (+19/-1)
plugins/Ubuntu/Settings/Components/Components.qmltypes (+73/-0)
plugins/Ubuntu/Settings/Components/plugin.cpp (+27/-0)
plugins/Ubuntu/Settings/Components/plugin.h (+30/-0)
plugins/Ubuntu/Settings/Components/qmldir (+2/-0)
plugins/Ubuntu/Settings/Components/serverpropertysynchroniser.cpp (+382/-0)
plugins/Ubuntu/Settings/Components/serverpropertysynchroniser.h (+157/-0)
plugins/Ubuntu/Settings/Menus/Menus.qmltypes (+30/-0)
plugins/Ubuntu/Settings/Menus/plugin.cpp (+1/-1)
plugins/Ubuntu/Settings/Menus/plugin.h (+4/-4)
plugins/Ubuntu/Settings/Menus/qmldir (+1/-1)
tests/qmltests/CMakeLists.txt (+2/-1)
tests/qmltests/Components/tst_ServerPropertySynchroniser.qml (+398/-0)
tests/utils/modules/Ubuntu/Test/UbuntuTestCase.qml (+19/-0)
To merge this branch: bzr merge lp:~nick-dedekind/ubuntu-settings-components/1390136.laggy-backends
Reviewer Review Type Date Requested Status
Michał Sawicz (community) Abstain
PS Jenkins bot (community) continuous-integration Approve
Andrea Cimitan (community) Approve
Review via email: mp+252754@code.launchpad.net

Commit message

Added ServerActivationSync control for laggy backends (lp#1390136)

Description of the change

Added ServerActivationSync control for laggy backends (lp#1390136)

 * Are there any related MPs required for this MP to build/function as expected? Please list.
No

 * Did you perform an exploratory manual test run of your code change and any related functionality?
Yes

 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A

 * If you changed the UI, has there been a design review?
N/A

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Andrea Cimitan (cimi) wrote :

 * Did you perform an exploratory manual test run of the code change and any related functionality?
Yes
 * Did CI run pass? If not, please explain why.
Yes
 * Did you make sure that the branch does not contain spurious tags?
Yes

review: Approve
Revision history for this message
Michał Sawicz (saviq) wrote :

You need a bumped debian/changelog here.

review: Needs Fixing
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

> You need a bumped debian/changelog here.

Changelog is there already.

I've moved the plugins to a plugins folder. Mostly because it resolves the relative path issue without changing the QmlPlugins.cmake so that we keep in line with unity8, but also because they are actually plugins anyway.

Fixed other comments as well.

Revision history for this message
Michał Sawicz (saviq) wrote :

CMake Error at CMakeLists.txt:98 (add_subdirectory):
  The source directory

    /build/buildd/ubuntu-settings-components-0.6+15.04.20150407/plugins

  does not contain a CMakeLists.txt file.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
99. By Nick Dedekind

added missing files

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) :
review: Abstain

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-07-31 16:55:11 +0000
3+++ CMakeLists.txt 2015-04-09 12:58:32 +0000
4@@ -95,6 +95,6 @@
5 # Tests
6 enable_testing()
7
8-add_subdirectory(Ubuntu)
9+add_subdirectory(plugins)
10 add_subdirectory(examples)
11 add_subdirectory(tests)
12
13=== modified file 'cmake/modules/QmlPlugins.cmake'
14--- cmake/modules/QmlPlugins.cmake 2014-06-26 14:21:52 +0000
15+++ cmake/modules/QmlPlugins.cmake 2015-04-09 12:58:32 +0000
16@@ -12,6 +12,12 @@
17 set_target_properties(qmlplugindump PROPERTIES IMPORTED_LOCATION ${qmlplugindump_exe})
18 endif()
19
20+#
21+# A custom target for building the qmltypes files manually.
22+#
23+if (NOT TARGET qmltypes)
24+ add_custom_target(qmltypes)
25+endif()
26
27 # Creates a target for copying resource files into build dir and optionally installing them.
28 #
29@@ -33,7 +39,7 @@
30 macro(export_qmlfiles PLUGIN PATH)
31 set(single SEARCH_PATH BINARY_DIR DESTINATION TARGET_PREFIX)
32 cmake_parse_arguments(QMLFILES "" "${single}" "" ${ARGN})
33-
34+
35 if(NOT QMLFILES_SEARCH_PATH)
36 set(QMLFILES_SEARCH_PATH ${CMAKE_CURRENT_SOURCE_DIR})
37 endif()
38@@ -51,6 +57,7 @@
39 ${QMLFILES_SEARCH_PATH}/*.png
40 ${QMLFILES_SEARCH_PATH}/*.sci
41 ${QMLFILES_SEARCH_PATH}/*.svg
42+ ${QMLFILES_SEARCH_PATH}/*.qmltypes
43 ${QMLFILES_SEARCH_PATH}/qmldir
44 )
45
46@@ -69,14 +76,14 @@
47 DESTINATION ${QMLFILES_DESTINATION}/${PATH}
48 )
49 endif()
50-endmacro(export_qmlfiles)
51-
52-
53-# Creates a target for generating the typeinfo file for a QML plugin and optionally installs it
54-# and additional targets.
55+endmacro()
56+
57+
58+# Creates a target for generating the typeinfo file for a QML plugin and/or installs the plugin
59+# targets.
60 #
61 # Files will be copied into ${BINARY_DIR}/${path} or ${CMAKE_CURRENT_BINARY_DIR} and installed
62-# into ${DESTINATION}/${path}. If you don't pass BINARY_DIR, it's assumed that current source
63+# into ${DESTINATION}/${path}. If you don't pass BINARY_DIR, it's assumed that current source
64 # path ends with ${path}.
65 #
66 # The generated file will be named after the last segment of the plugin name, e.g. Foo.qmltypes.
67@@ -87,15 +94,18 @@
68 # [TARGET_PREFIX string] # Will be prefixed to the target name
69 # [ENVIRONMENT string] # Will be added to qmlplugindump's env
70 # [TARGETS target1 [target2 ...]] # Targets to depend on and install (e.g. the plugin shared object)
71+# [NO_TYPES] # Do not create the qmltypes target
72 # )
73 #
74 # Created target:
75-# - ${TARGET_PREFIX}${plugin}-qmltypes - Generates the qmltypes file in the binary dir.
76+# - ${TARGET_PREFIX}${plugin}-qmltypes - Generates the qmltypes file in the source dir.
77+# It will be made a dependency of the "qmltypes" target.
78
79 macro(export_qmlplugin PLUGIN VERSION PATH)
80+ set(options NO_TYPES)
81 set(single BINARY_DIR DESTINATION TARGET_PREFIX ENVIRONMENT)
82 set(multi TARGETS)
83- cmake_parse_arguments(QMLPLUGIN "" "${single}" "${multi}" ${ARGN})
84+ cmake_parse_arguments(QMLPLUGIN "${options}" "${single}" "${multi}" ${ARGN})
85
86 get_target_property(qmlplugindump_executable qmlplugindump LOCATION)
87
88@@ -103,29 +113,26 @@
89 set(qmlplugin_dir ${QMLPLUGIN_BINARY_DIR}/${PATH})
90 else()
91 # Find import path to point qmlplugindump at
92- string(REGEX REPLACE "${PATH}$" "" QMLPLUGIN_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
93+ string(REGEX REPLACE "/${PATH}$" "" QMLPLUGIN_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
94 set(qmlplugin_dir ${CMAKE_CURRENT_BINARY_DIR})
95 endif()
96
97- # Find the last segment of the plugin name to use as qmltypes basename
98- string(REGEX MATCH "[^.]+$" plugin_suffix ${PLUGIN})
99- set(target_prefix ${QMLPLUGIN_TARGET_PREFIX}${PLUGIN})
100- set(qmltypes_path ${qmlplugin_dir}/${plugin_suffix}.qmltypes)
101-
102- # Only generate typeinfo if not cross compiling
103- if(NOT CMAKE_CROSSCOMPILING)
104- add_custom_target(${target_prefix}-qmltypes ALL
105+ if(NOT QMLPLUGIN_NO_TYPES)
106+ # Relative path for the module
107+ string(REPLACE "${CMAKE_BINARY_DIR}/" "" QMLPLUGIN_MODULE_DIR "${QMLPLUGIN_BINARY_DIR}")
108+
109+ # Find the last segment of the plugin name to use as qmltypes basename
110+ string(REGEX MATCH "[^.]+$" plugin_suffix ${PLUGIN})
111+ set(target_prefix ${QMLPLUGIN_TARGET_PREFIX}${PLUGIN})
112+ set(qmltypes_path ${CMAKE_CURRENT_SOURCE_DIR}/${plugin_suffix}.qmltypes)
113+
114+ add_custom_target(${target_prefix}-qmltypes
115 COMMAND env ${QMLPLUGIN_ENVIRONMENT} ${qmlplugindump_executable} -notrelocatable
116- ${PLUGIN} ${VERSION} ${QMLPLUGIN_BINARY_DIR} > ${qmltypes_path}
117+ ${PLUGIN} ${VERSION} ${QMLPLUGIN_MODULE_DIR} > ${qmltypes_path}
118+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
119 )
120 add_dependencies(${target_prefix}-qmltypes ${target_prefix}-qmlfiles ${QMLPLUGIN_TARGETS})
121-
122- if (QMLPLUGIN_DESTINATION)
123- # Install the typeinfo file
124- install(FILES ${qmltypes_path}
125- DESTINATION ${QMLPLUGIN_DESTINATION}/${PATH}
126- )
127- endif()
128+ add_dependencies(qmltypes ${target_prefix}-qmltypes)
129 endif()
130
131 set_target_properties(${QMLPLUGIN_TARGETS} PROPERTIES
132
133=== modified file 'debian/changelog'
134--- debian/changelog 2015-01-23 12:17:25 +0000
135+++ debian/changelog 2015-04-09 12:58:32 +0000
136@@ -1,3 +1,10 @@
137+ubuntu-settings-components (0.6-0ubuntu1) UNRELEASED; urgency=medium
138+
139+ [ Nick Dedekind ]
140+ * Added ServerActivationSync
141+
142+ -- Nick Dedekind <nick.dedekind@canonical.com> Thu, 12 Mar 2015 14:04:34 +0000
143+
144 ubuntu-settings-components (0.5+15.04.20150123.1-0ubuntu1) vivid; urgency=medium
145
146 [ Sebastien Bacher ]
147@@ -5,6 +12,12 @@
148
149 -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 23 Jan 2015 12:17:25 +0000
150
151+ubuntu-settings-components (0.5-0ubuntu1) UNRELEASED; urgency=medium
152+
153+ * Added SyncSwitch & SyncCheckBox components
154+
155+ -- Nick Dedekind <nick.dedekind@canonical.com> Thu, 23 Oct 2014 17:09:21 -0400
156+
157 ubuntu-settings-components (0.4+15.04.20141105-0ubuntu1) vivid; urgency=low
158
159 [ Sebastien Bacher ]
160
161=== added directory 'plugins'
162=== added file 'plugins/CMakeLists.txt'
163--- plugins/CMakeLists.txt 1970-01-01 00:00:00 +0000
164+++ plugins/CMakeLists.txt 2015-04-09 12:58:32 +0000
165@@ -0,0 +1,8 @@
166+include(QmlPlugins)
167+
168+macro(add_usc_plugin PLUGIN VERSION PATH)
169+ export_qmlfiles(${PLUGIN} ${PATH} DESTINATION ${QT_IMPORTS_DIR} ${ARGN})
170+ export_qmlplugin(${PLUGIN} ${VERSION} ${PATH} DESTINATION ${QT_IMPORTS_DIR} ${ARGN})
171+endmacro()
172+
173+add_subdirectory(Ubuntu)
174
175=== renamed directory 'Ubuntu' => 'plugins/Ubuntu'
176=== modified file 'plugins/Ubuntu/Settings/CMakeLists.txt'
177--- Ubuntu/Settings/CMakeLists.txt 2014-06-26 14:21:52 +0000
178+++ plugins/Ubuntu/Settings/CMakeLists.txt 2015-04-09 12:58:32 +0000
179@@ -1,9 +1,2 @@
180-include(QmlPlugins)
181-
182-macro(add_usc_plugin PLUGIN VERSION PATH)
183- export_qmlfiles(${PLUGIN} ${PATH} DESTINATION ${QT_IMPORTS_DIR} ${ARGN})
184- export_qmlplugin(${PLUGIN} ${VERSION} ${PATH} DESTINATION ${QT_IMPORTS_DIR} ${ARGN})
185-endmacro()
186-
187 add_subdirectory(Components)
188 add_subdirectory(Menus)
189
190=== modified file 'plugins/Ubuntu/Settings/Components/CMakeLists.txt'
191--- Ubuntu/Settings/Components/CMakeLists.txt 2014-07-31 13:05:44 +0000
192+++ plugins/Ubuntu/Settings/Components/CMakeLists.txt 2015-04-09 12:58:32 +0000
193@@ -1,1 +1,19 @@
194-add_usc_plugin(Ubuntu.Settings.Components 0.1 Ubuntu/Settings/Components)
195+project(UbuntuSettingsComponentsQml)
196+
197+find_package(Qt5Core REQUIRED)
198+
199+include_directories(
200+ ${CMAKE_CURRENT_SOURCE_DIR}
201+ ${CMAKE_CURRENT_BINARY_DIR}
202+)
203+
204+add_definitions(-DUBUNTUSETTINGSCOMPONENTS_LIBRARY)
205+
206+add_library(UbuntuSettingsComponentsQml MODULE
207+ plugin.cpp
208+ serverpropertysynchroniser.cpp
209+)
210+
211+qt5_use_modules(UbuntuSettingsComponentsQml Core Qml Quick)
212+
213+add_usc_plugin(Ubuntu.Settings.Components 0.1 Ubuntu/Settings/Components TARGETS UbuntuSettingsComponentsQml)
214
215=== added file 'plugins/Ubuntu/Settings/Components/Components.qmltypes'
216--- plugins/Ubuntu/Settings/Components/Components.qmltypes 1970-01-01 00:00:00 +0000
217+++ plugins/Ubuntu/Settings/Components/Components.qmltypes 2015-04-09 12:58:32 +0000
218@@ -0,0 +1,73 @@
219+import QtQuick.tooling 1.1
220+
221+// This file describes the plugin-supplied types contained in the library.
222+// It is used for QML tooling purposes only.
223+//
224+// This file was auto-generated by:
225+// 'qmlplugindump -notrelocatable Ubuntu.Settings.Components 0.1 plugins'
226+
227+Module {
228+ Component {
229+ name: "ServerPropertySynchroniser"
230+ prototype: "QObject"
231+ exports: ["Ubuntu.Settings.Components/ServerPropertySynchroniser 0.1"]
232+ exportMetaObjectRevisions: [0]
233+ Property { name: "serverTarget"; type: "QObject"; isPointer: true }
234+ Property { name: "serverProperty"; type: "string" }
235+ Property { name: "userTarget"; type: "QObject"; isPointer: true }
236+ Property { name: "userProperty"; type: "string" }
237+ Property { name: "userTrigger"; type: "string" }
238+ Property { name: "syncTimeout"; type: "int" }
239+ Property { name: "useWaitBuffer"; type: "bool" }
240+ Property { name: "maximumWaitBufferInterval"; type: "int" }
241+ Property { name: "bufferedSyncTimeout"; type: "bool" }
242+ Property { name: "syncWaiting"; type: "bool"; isReadonly: true }
243+ Signal {
244+ name: "serverTargetChanged"
245+ Parameter { name: "serverTarget"; type: "QObject"; isPointer: true }
246+ }
247+ Signal {
248+ name: "serverPropertyChanged"
249+ Parameter { name: "serverProperty"; type: "string" }
250+ }
251+ Signal {
252+ name: "userTargetChanged"
253+ Parameter { name: "userTarget"; type: "QObject"; isPointer: true }
254+ }
255+ Signal {
256+ name: "userPropertyChanged"
257+ Parameter { name: "serverProperty"; type: "string" }
258+ }
259+ Signal {
260+ name: "userTriggerChanged"
261+ Parameter { name: "userTrigger"; type: "string" }
262+ }
263+ Signal {
264+ name: "syncTimeoutChanged"
265+ Parameter { name: "timeout"; type: "int" }
266+ }
267+ Signal {
268+ name: "syncWaitingChanged"
269+ Parameter { name: "waiting"; type: "bool" }
270+ }
271+ Signal {
272+ name: "bufferedSyncTimeoutChanged"
273+ Parameter { type: "bool" }
274+ }
275+ Signal {
276+ name: "useWaitBufferChanged"
277+ Parameter { name: "useWaitBuffer"; type: "bool" }
278+ }
279+ Signal {
280+ name: "maximumWaitBufferIntervalChanged"
281+ Parameter { name: "timeout"; type: "int" }
282+ }
283+ Signal {
284+ name: "syncTriggered"
285+ Parameter { name: "value"; type: "QVariant" }
286+ }
287+ Method { name: "updateUserValue" }
288+ Method { name: "activate" }
289+ Method { name: "reset" }
290+ }
291+}
292
293=== added file 'plugins/Ubuntu/Settings/Components/plugin.cpp'
294--- plugins/Ubuntu/Settings/Components/plugin.cpp 1970-01-01 00:00:00 +0000
295+++ plugins/Ubuntu/Settings/Components/plugin.cpp 2015-04-09 12:58:32 +0000
296@@ -0,0 +1,27 @@
297+/*
298+ * Copyright (C) 2015 Canonical, Ltd.
299+ *
300+ * This program is free software; you can redistribute it and/or modify
301+ * it under the terms of the GNU Lesser General Public License as published by
302+ * the Free Software Foundation; version 3.
303+ *
304+ * This program is distributed in the hope that it will be useful,
305+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
306+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
307+ * GNU Lesser General Public License for more details.
308+ *
309+ * You should have received a copy of the GNU Lesser General Public License
310+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
311+ */
312+
313+// local
314+#include "plugin.h"
315+#include "serverpropertysynchroniser.h"
316+
317+// Qt
318+#include <QtQml/qqml.h>
319+
320+void UbuntuSettingsComponentsPlugin::registerTypes(const char *uri)
321+{
322+ qmlRegisterType<ServerPropertySynchroniser>(uri, 0, 1, "ServerPropertySynchroniser");
323+}
324
325=== added file 'plugins/Ubuntu/Settings/Components/plugin.h'
326--- plugins/Ubuntu/Settings/Components/plugin.h 1970-01-01 00:00:00 +0000
327+++ plugins/Ubuntu/Settings/Components/plugin.h 2015-04-09 12:58:32 +0000
328@@ -0,0 +1,30 @@
329+/*
330+ * Copyright (C) 2015 Canonical, Ltd.
331+ *
332+ * This program is free software; you can redistribute it and/or modify
333+ * it under the terms of the GNU Lesser General Public License as published by
334+ * the Free Software Foundation; version 3.
335+ *
336+ * This program is distributed in the hope that it will be useful,
337+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
338+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
339+ * GNU Lesser General Public License for more details.
340+ *
341+ * You should have received a copy of the GNU Lesser General Public License
342+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
343+ */
344+
345+#ifndef UBUNTUSETTINGSCOMPONENTS_PLUGIN_H
346+#define UBUNTUSETTINGSCOMPONENTS_PLUGIN_H
347+
348+#include <QtQml/QQmlExtensionPlugin>
349+
350+class UbuntuSettingsComponentsPlugin : public QQmlExtensionPlugin
351+{
352+ Q_OBJECT
353+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
354+public:
355+ void registerTypes(const char *uri);
356+};
357+
358+#endif // UBUNTUSETTINGSCOMPONENTS_PLUGIN_H
359
360=== modified file 'plugins/Ubuntu/Settings/Components/qmldir'
361--- Ubuntu/Settings/Components/qmldir 2014-09-15 17:18:27 +0000
362+++ plugins/Ubuntu/Settings/Components/qmldir 2015-04-09 12:58:32 +0000
363@@ -1,4 +1,6 @@
364 module Ubuntu.Settings.Components
365+plugin UbuntuSettingsComponentsQml
366+typeinfo Components.qmltypes
367
368 ActionTextField 0.1 ActionTextField.qml
369 Calendar 0.1 Calendar.qml
370
371=== added file 'plugins/Ubuntu/Settings/Components/serverpropertysynchroniser.cpp'
372--- plugins/Ubuntu/Settings/Components/serverpropertysynchroniser.cpp 1970-01-01 00:00:00 +0000
373+++ plugins/Ubuntu/Settings/Components/serverpropertysynchroniser.cpp 2015-04-09 12:58:32 +0000
374@@ -0,0 +1,382 @@
375+/*
376+ * Copyright (C) 2015 Canonical, Ltd.
377+ *
378+ * This program is free software; you can redistribute it and/or modify
379+ * it under the terms of the GNU Lesser General Public License as published by
380+ * the Free Software Foundation; version 3.
381+ *
382+ * This program is distributed in the hope that it will be useful,
383+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
384+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
385+ * GNU Lesser General Public License for more details.
386+ *
387+ * You should have received a copy of the GNU Lesser General Public License
388+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
389+ */
390+
391+#include "serverpropertysynchroniser.h"
392+
393+#include <QQmlProperty>
394+#include <QTimer>
395+#include <QDebug>
396+
397+ServerPropertySynchroniser::ServerPropertySynchroniser(QObject* parent)
398+ : QObject(parent)
399+ , m_serverTarget(nullptr)
400+ , m_userTarget(nullptr)
401+ , m_classComplete(false)
402+ , m_busy(false)
403+ , m_connectedServerTarget(nullptr)
404+ , m_connectedUserTarget(nullptr)
405+ , m_serverSyncTimer(new QTimer(this))
406+ , m_bufferTimeout(nullptr)
407+ , m_useWaitBuffer(true)
408+ , m_buffering(false)
409+ , m_bufferedSyncTimeout(false)
410+{
411+ m_serverSyncTimer->setSingleShot(true);
412+ m_serverSyncTimer->setInterval(30000);
413+ connect(m_serverSyncTimer, &QTimer::timeout, this, &ServerPropertySynchroniser::serverSyncTimedOut);
414+}
415+
416+void ServerPropertySynchroniser::classBegin()
417+{
418+ m_classComplete = false;
419+}
420+
421+void ServerPropertySynchroniser::componentComplete()
422+{
423+ m_classComplete = true;
424+ connectServer();
425+ connectUser();
426+}
427+
428+void ServerPropertySynchroniser::reset()
429+{
430+ if (m_serverSyncTimer->isActive()) {
431+ m_serverSyncTimer->stop();
432+ Q_EMIT syncWaitingChanged(false);
433+ }
434+ if (m_bufferTimeout) m_bufferTimeout->stop();
435+ m_buffering = false;
436+}
437+
438+QObject *ServerPropertySynchroniser::serverTarget() const
439+{
440+ return m_serverTarget;
441+}
442+
443+void ServerPropertySynchroniser::setServerTarget(QObject *target)
444+{
445+ if (m_serverTarget != target) {
446+ m_serverTarget = target;
447+ Q_EMIT serverTargetChanged(m_serverTarget);
448+
449+ connectServer();
450+ }
451+}
452+
453+QString ServerPropertySynchroniser::serverProperty() const
454+{
455+ return m_serverProperty;
456+}
457+
458+void ServerPropertySynchroniser::setServerProperty(const QString &property)
459+{
460+ if (m_serverProperty != property) {
461+ m_serverProperty = property;
462+ Q_EMIT serverPropertyChanged(m_serverProperty);
463+
464+ connectServer();
465+ }
466+}
467+
468+QObject *ServerPropertySynchroniser::userTarget() const
469+{
470+ return m_userTarget;
471+}
472+
473+void ServerPropertySynchroniser::setUserTarget(QObject *target)
474+{
475+ if (m_userTarget != target) {
476+ m_userTarget = target;
477+ Q_EMIT userTargetChanged(m_userTarget);
478+
479+ connectUser();
480+ }
481+}
482+
483+QString ServerPropertySynchroniser::userProperty() const
484+{
485+ return m_userProperty;
486+}
487+
488+void ServerPropertySynchroniser::setUserProperty(const QString &property)
489+{
490+ if (m_userProperty != property) {
491+ m_userProperty = property;
492+ Q_EMIT userPropertyChanged(m_userProperty);
493+
494+ connectUser();
495+ }
496+}
497+
498+QString ServerPropertySynchroniser::userTrigger() const
499+{
500+ return m_userTrigger;
501+}
502+
503+void ServerPropertySynchroniser::setUserTrigger(const QString &trigger)
504+{
505+ if (m_userTrigger != trigger) {
506+ m_userTrigger = trigger;
507+ Q_EMIT userPropertyChanged(m_userTrigger);
508+
509+ connectUser();
510+ }
511+}
512+
513+int ServerPropertySynchroniser::syncTimeout() const
514+{
515+ return m_serverSyncTimer->interval();
516+}
517+
518+void ServerPropertySynchroniser::setSyncTimeout(int timeout)
519+{
520+ if (m_serverSyncTimer->interval() != timeout) {
521+ m_serverSyncTimer->setInterval(timeout);
522+ Q_EMIT syncTimeoutChanged(timeout);
523+ }
524+}
525+
526+bool ServerPropertySynchroniser::useWaitBuffer() const
527+{
528+ return m_useWaitBuffer;
529+}
530+
531+void ServerPropertySynchroniser::setUseWaitBuffer(bool value)
532+{
533+ if (m_useWaitBuffer != value) {
534+ m_useWaitBuffer = value;
535+ Q_EMIT useWaitBufferChanged(m_useWaitBuffer);
536+ }
537+}
538+
539+int ServerPropertySynchroniser::maximumWaitBufferInterval() const
540+{
541+ return m_bufferTimeout ? m_bufferTimeout->interval() : -1;
542+}
543+
544+void ServerPropertySynchroniser::setMaximumWaitBufferInterval(int timeout)
545+{
546+ if (timeout >= 0) {
547+ if (!m_bufferTimeout) {
548+ m_bufferTimeout = new QTimer(this);
549+ m_bufferTimeout->setInterval(timeout);
550+ m_bufferTimeout->setSingleShot(true);
551+ connect(m_bufferTimeout, &QTimer::timeout, this, &ServerPropertySynchroniser::bufferTimedOut);
552+
553+ Q_EMIT maximumWaitBufferIntervalChanged(timeout);
554+ }
555+ else if (timeout != m_bufferTimeout->interval()) {
556+ m_bufferTimeout->setInterval(timeout);
557+ Q_EMIT maximumWaitBufferIntervalChanged(timeout);
558+ }
559+
560+ } else if (m_bufferTimeout) {
561+ if (m_bufferTimeout->isActive()) {
562+ m_buffering = false;
563+ }
564+ delete m_bufferTimeout;
565+ m_bufferTimeout = nullptr;
566+ Q_EMIT maximumWaitBufferIntervalChanged(timeout);
567+ }
568+}
569+
570+bool ServerPropertySynchroniser::bufferedSyncTimeout() const
571+{
572+ return m_bufferedSyncTimeout;
573+}
574+
575+void ServerPropertySynchroniser::setBufferedSyncTimeout(bool value)
576+{
577+ if (m_bufferedSyncTimeout != value) {
578+ m_bufferedSyncTimeout = value;
579+ Q_EMIT bufferedSyncTimeoutChanged(value);
580+ }
581+}
582+
583+bool ServerPropertySynchroniser::syncWaiting() const
584+{
585+ return m_serverSyncTimer->isActive();
586+}
587+
588+void ServerPropertySynchroniser::activate()
589+{
590+ // Don't want any signals we fire to create binding loops.
591+ if (m_busy) return;
592+ m_busy = true;
593+
594+ if (m_useWaitBuffer) {
595+ // Dampen the activations? Buffer the change.
596+ if (m_bufferTimeout) {
597+ if (m_bufferTimeout->isActive()) {
598+ m_buffering = true;
599+ m_busy = false;
600+ return;
601+ }
602+ m_bufferTimeout->start();
603+ // Not using a buffer timer? Buffer the change till server timeout
604+ } else if (m_serverSyncTimer->isActive()) {
605+ m_buffering = true;
606+ m_busy = false;
607+ return;
608+ }
609+ }
610+
611+ m_serverSyncTimer->start();
612+ Q_EMIT syncWaitingChanged(true);
613+
614+ // Fire off a change to the server user property value
615+ QQmlProperty userProp(m_userTarget, m_userProperty);
616+ if (!userProp.isValid()) {
617+ Q_EMIT syncTriggered(QVariant());
618+ } else {
619+ Q_EMIT syncTriggered(userProp.read());
620+ }
621+ m_busy = false;
622+}
623+
624+void ServerPropertySynchroniser::connectServer()
625+{
626+ // if we havent finished constructing the class, then wait
627+ if (!m_classComplete) return;
628+
629+ if (m_connectedServerTarget) QObject::disconnect(m_connectedServerTarget, 0, this, 0);
630+ if (!m_serverTarget || m_serverProperty.isEmpty()) {
631+ return;
632+ }
633+
634+ // Connect to the server property change
635+ QQmlProperty prop(m_serverTarget, m_serverProperty);
636+ if (prop.isValid()) {
637+ if (prop.connectNotifySignal(this, SLOT(updateUserValue()))) {
638+ m_connectedServerTarget = m_serverTarget;
639+ }
640+ // once we're connected to the server property, we need to make sure the user target is
641+ // set to the server value
642+ updateUserValue();
643+ }
644+}
645+
646+void ServerPropertySynchroniser::connectUser()
647+{
648+ // if we havent finished constructing the class, then wait
649+ if (!m_classComplete) return;
650+
651+ if (m_connectedUserTarget) QObject::disconnect(m_connectedUserTarget, 0, this, 0);
652+ if (!m_userTarget) {
653+ if (!parent()) return;
654+ m_userTarget = parent();
655+ Q_EMIT userTargetChanged(m_userTarget);
656+ }
657+
658+ if (m_userTrigger.isEmpty()) {
659+ // Connect to the user property change
660+ QQmlProperty prop(m_userTarget, m_userProperty);
661+ if (prop.isValid()) {
662+ if (prop.connectNotifySignal(this, SLOT(activate()))) {
663+ m_connectedUserTarget = m_userTarget;
664+ }
665+ // once we're connected to the user property, we need to make sure the user target is
666+ // set to the server value
667+ updateUserValue();
668+ }
669+ } else {
670+ QQmlProperty prop(m_userTarget, m_userTrigger);
671+ if (prop.isValid() && prop.isSignalProperty()) {
672+ if (connect(m_userTarget, ("2" + prop.method().methodSignature()).constData(),
673+ this, SLOT(activate()))) {
674+ m_connectedUserTarget = m_userTarget;
675+ }
676+
677+ // once we're connected to the user signal, we need to make sure the user target is
678+ // set to the server value
679+ updateUserValue();
680+ }
681+ }
682+}
683+
684+void ServerPropertySynchroniser::updateUserValue()
685+{
686+ // Don't want any signals we fire to create binding loops.
687+ if (m_busy) return;
688+ m_busy = true;
689+
690+ bool waitingBufferedServerChange = m_bufferTimeout && m_bufferTimeout->isActive();
691+
692+ // If we've been waiting for a sync, stop the wait.
693+ if (m_serverSyncTimer->isActive()) {
694+ m_serverSyncTimer->stop();
695+ Q_EMIT syncWaitingChanged(false);
696+ }
697+
698+ QQmlProperty userProp(m_userTarget, m_userProperty);
699+ QQmlProperty serverProp(m_serverTarget, m_serverProperty);
700+ if (!userProp.isValid() || !serverProp.isValid()) {
701+ m_busy = false;
702+ return;
703+ }
704+
705+ // If we've been buffering changes since last change was send,
706+ // we verify that what the server gave us is what we want, and send another
707+ // activation if not.
708+ if (m_buffering) {
709+ m_buffering = false;
710+ m_busy = false;
711+ if (serverProp.read() != userProp.read()) {
712+ activate();
713+ }
714+ return;
715+ }
716+
717+ // Don't update until we hit the buffer timeout.
718+ if (waitingBufferedServerChange) {
719+ m_busy = false;
720+ return;
721+ }
722+
723+ // update the user target property.
724+ userProp.write(serverProp.read());
725+ m_busy = false;
726+}
727+
728+void ServerPropertySynchroniser::serverSyncTimedOut()
729+{
730+ if (m_buffering && !m_bufferedSyncTimeout) {
731+ m_buffering = false;
732+ }
733+ Q_EMIT syncWaitingChanged(false);
734+ updateUserValue();
735+}
736+
737+void ServerPropertySynchroniser::bufferTimedOut()
738+{
739+ if (m_buffering) {
740+ m_buffering = false;
741+ activate();
742+ } else {
743+ // Update the user value.
744+ if (m_busy) return;
745+ m_busy = true;
746+
747+ QQmlProperty userProp(m_userTarget, m_userProperty);
748+ QQmlProperty serverProp(m_serverTarget, m_serverProperty);
749+ if (!userProp.isValid() || !serverProp.isValid()) {
750+ m_busy = false;
751+ return;
752+ }
753+ userProp.write(serverProp.read());
754+ m_busy = false;
755+ }
756+}
757
758=== added file 'plugins/Ubuntu/Settings/Components/serverpropertysynchroniser.h'
759--- plugins/Ubuntu/Settings/Components/serverpropertysynchroniser.h 1970-01-01 00:00:00 +0000
760+++ plugins/Ubuntu/Settings/Components/serverpropertysynchroniser.h 2015-04-09 12:58:32 +0000
761@@ -0,0 +1,157 @@
762+/*
763+ * Copyright (C) 2015 Canonical, Ltd.
764+ *
765+ * This program is free software; you can redistribute it and/or modify
766+ * it under the terms of the GNU Lesser General Public License as published by
767+ * the Free Software Foundation; version 3.
768+ *
769+ * This program is distributed in the hope that it will be useful,
770+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
771+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
772+ * GNU Lesser General Public License for more details.
773+ *
774+ * You should have received a copy of the GNU Lesser General Public License
775+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
776+ */
777+
778+#ifndef SERVERPROPERTYSYNCHRONISER_H
779+#define SERVERPROPERTYSYNCHRONISER_H
780+
781+#include <QObject>
782+#include <QQmlParserStatus>
783+#include <QVariant>
784+
785+class QTimer;
786+
787+class ServerPropertySynchroniser : public QObject, public QQmlParserStatus
788+{
789+ Q_OBJECT
790+ Q_INTERFACES(QQmlParserStatus)
791+
792+ // Target object which contains the property to keep the user property in sync with.
793+ Q_PROPERTY(QObject* serverTarget READ serverTarget WRITE setServerTarget NOTIFY serverTargetChanged)
794+ // Server property to keep the user property in sync with.
795+ Q_PROPERTY(QString serverProperty READ serverProperty WRITE setServerProperty NOTIFY serverPropertyChanged)
796+
797+ // User object (control) which sources the property to update the server property.
798+ // Defaults to the object's parent if not set.
799+ Q_PROPERTY(QObject* userTarget READ userTarget WRITE setUserTarget NOTIFY userTargetChanged)
800+ // User property to update the server property.
801+ Q_PROPERTY(QString userProperty READ userProperty WRITE setUserProperty NOTIFY userPropertyChanged)
802+ // Trigger that causes an update. By default, the control will use the userProperty change notification.
803+ // eg. "onTriggered"
804+ Q_PROPERTY(QString userTrigger READ userTrigger WRITE setUserTrigger NOTIFY userTriggerChanged)
805+
806+ // Time to wait for a change verification before re-asserting the server value.
807+ Q_PROPERTY(int syncTimeout READ syncTimeout WRITE setSyncTimeout NOTIFY syncTimeoutChanged)
808+
809+ // Buffer user property changes until the previous change is verified
810+ Q_PROPERTY(bool useWaitBuffer
811+ READ useWaitBuffer
812+ WRITE setUseWaitBuffer
813+ NOTIFY useWaitBufferChanged)
814+
815+ // Maximum intervals between buffers for the server to respond. If we don't get a response within this interval,
816+ // the next buffer will be sent. Good for live sliders. Defaults to disabled.
817+ Q_PROPERTY(int maximumWaitBufferInterval
818+ READ maximumWaitBufferInterval
819+ WRITE setMaximumWaitBufferInterval
820+ NOTIFY maximumWaitBufferIntervalChanged)
821+
822+ // Resend the buffered value if we timeout waiting for a change from the server. Defaults to false
823+ Q_PROPERTY(bool bufferedSyncTimeout
824+ READ bufferedSyncTimeout
825+ WRITE setBufferedSyncTimeout
826+ NOTIFY bufferedSyncTimeoutChanged)
827+
828+ // True if we're waiting for a change verification from the server
829+ Q_PROPERTY(bool syncWaiting READ syncWaiting NOTIFY syncWaitingChanged)
830+
831+public:
832+ ServerPropertySynchroniser(QObject* parent = nullptr);
833+
834+ QObject* serverTarget() const;
835+ void setServerTarget(QObject* target);
836+
837+ QString serverProperty() const;
838+ void setServerProperty(const QString& property);
839+
840+ QObject* userTarget() const;
841+ void setUserTarget(QObject* target);
842+
843+ QString userProperty() const;
844+ void setUserProperty(const QString& property);
845+
846+ QString userTrigger() const;
847+ void setUserTrigger(const QString& trigger);
848+
849+ int syncTimeout() const;
850+ void setSyncTimeout(int timeout);
851+
852+ bool useWaitBuffer() const;
853+ void setUseWaitBuffer(bool value);
854+
855+ int maximumWaitBufferInterval() const;
856+ void setMaximumWaitBufferInterval(int timeout);
857+
858+ bool bufferedSyncTimeout() const;
859+ void setBufferedSyncTimeout(bool);
860+
861+ bool syncWaiting() const;
862+
863+ void classBegin() override;
864+ void componentComplete() override;
865+
866+ Q_INVOKABLE void reset();
867+
868+public Q_SLOTS:
869+ void updateUserValue();
870+ void activate();
871+
872+Q_SIGNALS:
873+ void serverTargetChanged(QObject* serverTarget);
874+ void serverPropertyChanged(QString serverProperty);
875+
876+ void userTargetChanged(QObject* userTarget);
877+ void userPropertyChanged(QString serverProperty);
878+ void userTriggerChanged(QString userTrigger);
879+
880+ void syncTimeoutChanged(int timeout);
881+ void syncWaitingChanged(bool waiting);
882+ void bufferedSyncTimeoutChanged(bool);
883+
884+ void useWaitBufferChanged(bool useWaitBuffer);
885+ void maximumWaitBufferIntervalChanged(int timeout);
886+
887+ // Emitted when we want to update the backend.
888+ void syncTriggered(const QVariant& value);
889+
890+private Q_SLOTS:
891+ void serverSyncTimedOut();
892+ void bufferTimedOut();
893+
894+private:
895+ void connectServer();
896+ void connectUser();
897+
898+ QObject* m_serverTarget;
899+ QString m_serverProperty;
900+
901+ QObject* m_userTarget;
902+ QString m_userProperty;
903+ QString m_userTrigger;
904+
905+ bool m_classComplete;
906+ bool m_busy;
907+
908+ QObject* m_connectedServerTarget;
909+ QObject* m_connectedUserTarget;
910+
911+ QTimer* m_serverSyncTimer;
912+ QTimer* m_bufferTimeout;
913+ bool m_useWaitBuffer;
914+ bool m_buffering;
915+ bool m_bufferedSyncTimeout;
916+};
917+
918+#endif // SERVERPROPERTYSYNCHRONISER_H
919
920=== added file 'plugins/Ubuntu/Settings/Menus/Menus.qmltypes'
921--- plugins/Ubuntu/Settings/Menus/Menus.qmltypes 1970-01-01 00:00:00 +0000
922+++ plugins/Ubuntu/Settings/Menus/Menus.qmltypes 2015-04-09 12:58:32 +0000
923@@ -0,0 +1,30 @@
924+import QtQuick.tooling 1.1
925+
926+// This file describes the plugin-supplied types contained in the library.
927+// It is used for QML tooling purposes only.
928+//
929+// This file was auto-generated by:
930+// 'qmlplugindump -notrelocatable Ubuntu.Settings.Menus 0.1 plugins'
931+
932+Module {
933+ Component {
934+ name: "TransferState"
935+ prototype: "QObject"
936+ exports: ["Ubuntu.Settings.Menus/TransferState 0.1"]
937+ isCreatable: false
938+ exportMetaObjectRevisions: [0]
939+ Enum {
940+ name: "TransferStates"
941+ values: {
942+ "Queued": 0,
943+ "Running": 1,
944+ "Paused": 2,
945+ "Canceled": 3,
946+ "Hashing": 4,
947+ "Processing": 5,
948+ "Finished": 6,
949+ "Error": 7
950+ }
951+ }
952+ }
953+}
954
955=== modified file 'plugins/Ubuntu/Settings/Menus/plugin.cpp'
956--- Ubuntu/Settings/Menus/plugin.cpp 2014-06-27 13:56:26 +0000
957+++ plugins/Ubuntu/Settings/Menus/plugin.cpp 2015-04-09 12:58:32 +0000
958@@ -21,7 +21,7 @@
959 // Qt
960 #include <QtQml/qqml.h>
961
962-void UbuntuSettingsComponentsPlugin::registerTypes(const char *uri)
963+void UbuntuSettingsMenusPlugin::registerTypes(const char *uri)
964 {
965 qmlRegisterUncreatableType<TransferState>(uri, 0, 1, "TransferState", "Can't create TransferState class");
966 }
967
968=== modified file 'plugins/Ubuntu/Settings/Menus/plugin.h'
969--- Ubuntu/Settings/Menus/plugin.h 2014-06-27 09:39:18 +0000
970+++ plugins/Ubuntu/Settings/Menus/plugin.h 2015-04-09 12:58:32 +0000
971@@ -14,12 +14,12 @@
972 * along with this program. If not, see <http://www.gnu.org/licenses/>.
973 */
974
975-#ifndef UBUNTUSETTINGSCOMPOENNTS_PLUGIN_H
976-#define UBUNTUSETTINGSCOMPOENNTS_PLUGIN_H
977+#ifndef UBUNTUSETTINGSMENUS_PLUGIN_H
978+#define UBUNTUSETTINGSMENUS_PLUGIN_H
979
980 #include <QtQml/QQmlExtensionPlugin>
981
982-class UbuntuSettingsComponentsPlugin : public QQmlExtensionPlugin
983+class UbuntuSettingsMenusPlugin : public QQmlExtensionPlugin
984 {
985 Q_OBJECT
986 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
987@@ -27,4 +27,4 @@
988 void registerTypes(const char *uri);
989 };
990
991-#endif // UBUNTUSETTINGSCOMPOENNTS_PLUGIN_H
992+#endif // UBUNTUSETTINGSMENUS_PLUGIN_H
993
994=== modified file 'plugins/Ubuntu/Settings/Menus/qmldir'
995--- Ubuntu/Settings/Menus/qmldir 2014-07-25 17:53:08 +0000
996+++ plugins/Ubuntu/Settings/Menus/qmldir 2015-04-09 12:58:32 +0000
997@@ -1,6 +1,6 @@
998 module Ubuntu.Settings.Menus
999 plugin UbuntuSettingsMenusQml
1000-typeinfo plugin.qmltypes
1001+typeinfo Menus.qmltypes
1002
1003 AccessPointMenu 0.1 AccessPointMenu.qml
1004 ButtonMenu 0.1 ButtonMenu.qml
1005
1006=== modified file 'tests/qmltests/CMakeLists.txt'
1007--- tests/qmltests/CMakeLists.txt 2014-07-31 11:29:03 +0000
1008+++ tests/qmltests/CMakeLists.txt 2015-04-09 12:58:32 +0000
1009@@ -6,11 +6,12 @@
1010 set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
1011
1012 set(qmltest_DEFAULT_IMPORT_PATHS
1013- ${CMAKE_BINARY_DIR}
1014+ ${CMAKE_BINARY_DIR}/plugins
1015 ${CMAKE_BINARY_DIR}/tests/utils/modules
1016 )
1017
1018 add_qml_test(Components Calendar)
1019+add_qml_test(Components ServerPropertySynchroniser)
1020 add_qml_test(Components StatusIcon)
1021
1022 add_qml_test(Menus AccessPointMenu)
1023
1024=== added file 'tests/qmltests/Components/tst_ServerPropertySynchroniser.qml'
1025--- tests/qmltests/Components/tst_ServerPropertySynchroniser.qml 1970-01-01 00:00:00 +0000
1026+++ tests/qmltests/Components/tst_ServerPropertySynchroniser.qml 2015-04-09 12:58:32 +0000
1027@@ -0,0 +1,398 @@
1028+/*
1029+ * Copyright 2015 Canonical Ltd.
1030+ *
1031+ * This program is free software; you can redistribute it and/or modify
1032+ * it under the terms of the GNU General Public License as published by
1033+ * the Free Software Foundation; version 3.
1034+ *
1035+ * This program is distributed in the hope that it will be useful,
1036+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1037+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1038+ * GNU General Public License for more details.
1039+ *
1040+ * You should have received a copy of the GNU General Public License
1041+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1042+ */
1043+
1044+import QtQuick 2.3
1045+import QtTest 1.0
1046+import Ubuntu.Test 0.1
1047+import Ubuntu.Settings.Components 0.1 as USC
1048+import Ubuntu.Settings.Menus 0.1 as USM
1049+import Ubuntu.Components 0.1
1050+
1051+Item {
1052+ id: root
1053+ width: units.gu(60)
1054+ height: units.gu(70)
1055+
1056+ QtObject {
1057+ id: switchBackend
1058+
1059+ property bool checked: false
1060+ property bool inSync: checked === switchControl.checked
1061+
1062+ property Timer timer: Timer {
1063+ interval: 2000
1064+ onTriggered: switchBackend.checked = !switchBackend.checked
1065+ }
1066+ }
1067+
1068+ QtObject {
1069+ id: checkBackend
1070+
1071+ property bool checked: false
1072+ property bool inSync: checked === checkControl.checked
1073+
1074+ property Timer timer: Timer {
1075+ interval: 2000
1076+ onTriggered: checkBackend.checked = !checkBackend.checked
1077+ }
1078+ }
1079+
1080+ QtObject {
1081+ id: sliderBackend
1082+
1083+ property real value: 50
1084+ property bool inSync: value === slider.value
1085+ property var changeToValue: undefined
1086+
1087+ property Timer timer: Timer {
1088+ interval: 200
1089+
1090+ onTriggered: {
1091+ sliderBackend.value = sliderBackend.changeToValue;
1092+ }
1093+ }
1094+ }
1095+
1096+ QtObject {
1097+ id: apBackend
1098+
1099+ property bool active: false
1100+ property bool inSync: active === apMenu.active
1101+
1102+ property Timer timer: Timer {
1103+ interval: 2000
1104+ onTriggered: apBackend.active = !apBackend.active
1105+ }
1106+ }
1107+
1108+
1109+ Column {
1110+ anchors.fill: parent
1111+ anchors.margins: units.gu(1)
1112+
1113+ spacing: units.gu(2)
1114+
1115+ Row {
1116+ spacing: units.gu(3)
1117+
1118+ Switch {
1119+ id: switchControl
1120+ anchors.verticalCenter: parent.verticalCenter
1121+
1122+ USC.ServerPropertySynchroniser {
1123+ id: switchSync
1124+ objectName: "switchSync"
1125+
1126+ syncTimeout: 3000
1127+
1128+ userTarget: switchControl
1129+ userProperty: "checked"
1130+
1131+ serverTarget: switchBackend
1132+ serverProperty: "checked"
1133+
1134+ onSyncTriggered: switchBackend.timer.start()
1135+ onSyncWaitingChanged: switchSyncSpy.clear()
1136+ }
1137+ }
1138+
1139+ Column {
1140+ Label { text: switchBackend.inSync ? "synced" : "out of sync" }
1141+ Label { text: switchSync.syncWaiting ? "syncWait" : "no syncWait" }
1142+ Label { text: "activates: " + switchSyncSpy.count }
1143+ }
1144+ }
1145+
1146+ Row {
1147+ spacing: units.gu(3)
1148+
1149+ CheckBox {
1150+ id: checkControl
1151+ anchors.verticalCenter: parent.verticalCenter
1152+
1153+ USC.ServerPropertySynchroniser {
1154+ id: checkSync
1155+ objectName: "checkSync"
1156+
1157+ syncTimeout: 3000
1158+
1159+ userTarget: checkControl
1160+ userProperty: "checked"
1161+
1162+ serverTarget: checkBackend
1163+ serverProperty: "checked"
1164+
1165+ onSyncTriggered: checkBackend.timer.start()
1166+
1167+ onSyncWaitingChanged: checkSyncSpy.clear()
1168+ }
1169+ }
1170+
1171+ Column {
1172+ Label { text: checkBackend.inSync ? "synced" : "out of sync" }
1173+ Label { text: checkSync.syncWaiting ? "syncWait" : "no syncWait" }
1174+ Label { text: "activates: " + checkSyncSpy.count }
1175+ }
1176+ }
1177+
1178+ Row {
1179+ id: sliderRoot
1180+ spacing: units.gu(3)
1181+
1182+ Slider {
1183+ id: slider
1184+ anchors.verticalCenter: parent.verticalCenter
1185+ live: true
1186+ minimumValue: 0.0
1187+ maximumValue: 100.0
1188+
1189+ property real serverValue: sliderBackend.value
1190+ USC.ServerPropertySynchroniser {
1191+ id: sliderSync
1192+ objectName: "sliderSync"
1193+
1194+ syncTimeout: 3000
1195+
1196+ userTarget: slider
1197+ userProperty: "value"
1198+
1199+ serverTarget: slider
1200+ serverProperty: "serverValue"
1201+
1202+ onSyncTriggered: {
1203+ sliderBackend.changeToValue = value;
1204+ sliderBackend.timer.start();
1205+ }
1206+
1207+ onSyncWaitingChanged: sliderSyncSpy.clear()
1208+ }
1209+ }
1210+
1211+ Column {
1212+ Label { text: sliderBackend.inSync ? "synced" : "out of sync" }
1213+ Label { text: sliderSync.syncWaiting ? "syncWait" : "no syncWait" }
1214+ Label { text: "activates: " + sliderSyncSpy.count }
1215+ }
1216+ }
1217+
1218+ Row {
1219+ spacing: units.gu(3)
1220+ height: childrenRect.height
1221+ anchors.left: parent.left
1222+ anchors.right: parent.right
1223+
1224+ USM.AccessPointMenu {
1225+ id: apMenu
1226+ width: units.gu(30)
1227+
1228+ text: "Test Check Menu"
1229+
1230+ USC.ServerPropertySynchroniser {
1231+ id: apMenuSync
1232+ objectName: "apMenuSync"
1233+
1234+ syncTimeout: 3000
1235+
1236+ userTarget: apMenu
1237+ userProperty: "active"
1238+ userTrigger: "onTriggered"
1239+
1240+ serverTarget: apBackend
1241+ serverProperty: "active"
1242+
1243+ onSyncTriggered: apBackend.timer.start()
1244+
1245+ onSyncWaitingChanged: switchSyncSpy.clear()
1246+ }
1247+ }
1248+
1249+ Column {
1250+ Label { text: apBackend.inSync ? "synced" : "out of sync" }
1251+ Label { text: apMenuSync.syncWaiting ? "syncWait" : "no syncWait" }
1252+ Label { text: "activates: " + apSyncSpy.count }
1253+ }
1254+ }
1255+ }
1256+
1257+ SignalSpy {
1258+ id: switchSyncSpy
1259+ target: switchControl
1260+ signalName: "triggered"
1261+ }
1262+ SignalSpy {
1263+ id: checkSyncSpy
1264+ target: checkControl
1265+ signalName: "triggered"
1266+ }
1267+ SignalSpy {
1268+ id: sliderSyncSpy
1269+ target: slider
1270+ signalName: "valueChanged"
1271+ }
1272+ SignalSpy {
1273+ id: apSyncSpy
1274+ target: apMenu
1275+ signalName: "triggered"
1276+ }
1277+ SignalSpy {
1278+ id: sliderSyncActivatedSpy
1279+ target: sliderSync
1280+ signalName: "syncTriggered"
1281+ }
1282+ SignalSpy {
1283+ id:apSyncActivatedSpy
1284+ target: apMenuSync
1285+ signalName: "syncTriggered"
1286+ }
1287+
1288+ QtObject {
1289+ id: switchBackend2
1290+
1291+ property bool checked2: false
1292+ property bool inSync: checked2 === switchControl.checked
1293+
1294+ property Timer switchTimer: Timer {
1295+ interval: 1000
1296+ onTriggered: switchBackend2.checked2 = !switchBackend2.checked2
1297+ }
1298+ }
1299+
1300+ UbuntuTestCase {
1301+ name: "ServerActivationSync"
1302+ when: windowShown
1303+
1304+ function init() {
1305+ waitForRendering(root);
1306+ switchBackend.timer.interval = 100;
1307+ checkBackend.timer.interval = 100;
1308+ sliderBackend.timer.interval = 100;
1309+ apBackend.timer.interval = 100;
1310+
1311+ switchSync.syncTimeout = 200;
1312+ checkSync.syncTimeout = 200;
1313+ sliderSync.syncTimeout = 200;
1314+ apMenuSync.syncTimeout = 200;
1315+
1316+ sliderSyncActivatedSpy.clear();
1317+ apSyncActivatedSpy.clear();
1318+ }
1319+
1320+ function cleanup() {
1321+ switchBackend.timer.stop();
1322+ checkBackend.timer.stop();
1323+ sliderBackend.timer.stop();
1324+
1325+ switchBackend.checked = false;
1326+ checkBackend.checked = false;
1327+ sliderBackend.value = 50;
1328+ apBackend.active = false;
1329+
1330+ switchSync.reset();
1331+ checkSync.reset();
1332+ sliderSync.reset();
1333+ apMenuSync.reset();
1334+ sliderSync.maximumWaitBufferInterval = -1
1335+
1336+ tryCompare(switchBackend, "inSync", true);
1337+ tryCompare(checkBackend, "inSync", true);
1338+ tryCompare(sliderBackend, "inSync", true);
1339+
1340+ switchSync.serverTarget = switchBackend;
1341+ switchSync.serverProperty = "checked";
1342+ }
1343+
1344+ function test_backend_change() {
1345+ switchBackend.checked = true;
1346+ compare(switchControl.checked, true, "Switch should have been toggled");
1347+ switchBackend.checked = false;
1348+ compare(switchControl.checked, false, "Switch should have been toggled");
1349+ }
1350+
1351+ function test_frontend_change() {
1352+ switchControl.clicked();
1353+ tryCompare(switchBackend, "checked", true);
1354+ }
1355+
1356+ function test_frontend_change_with_value() {
1357+ slider.value = 60;
1358+ tryCompare(sliderBackend, "value", 60);
1359+ }
1360+
1361+ function test_break_binding_change() {
1362+ switchControl.checked = true;
1363+ switchBackend.checked = true;
1364+ switchBackend.checked = false;
1365+ compare(switchControl.checked, false, "Switch should have been toggled");
1366+ }
1367+
1368+ function test_buffered_change_with_value() {
1369+ slider.value = 60;
1370+ compare(sliderSyncActivatedSpy.count, 1, "activated signal should have been sent")
1371+ slider.value = 70;
1372+ slider.value = 80;
1373+ slider.value = 90;
1374+ compare(sliderSyncActivatedSpy.count, 1, "activated signals should have been buffered")
1375+ tryCompare(sliderBackend, "value", 90);
1376+ tryCompare(sliderSyncActivatedSpy, "count", 2)
1377+ }
1378+
1379+ function test_buffered_change_with_maximum_interval() {
1380+ sliderSync.maximumWaitBufferInterval = 50;
1381+ sliderSync.syncTimeout = 5000;
1382+
1383+ slider.value = 60;
1384+ compare(sliderSyncActivatedSpy.count, 1, "activated signal should have been sent")
1385+ slider.value = 70;
1386+ slider.value = 80;
1387+ tryCompare(sliderSyncActivatedSpy, "count", 2, 500, "activated signal should have been sent after max interval")
1388+ slider.value = 90;
1389+ tryCompare(sliderSyncActivatedSpy, "count", 3, 500, "activated signal should have been sent after max interval")
1390+ slider.value = 100;
1391+ tryCompare(sliderSyncActivatedSpy, "count", 4, 500);
1392+ tryCompare(sliderBackend, "value", 100);
1393+ }
1394+
1395+ function test_connect_to_another_object() {
1396+ switchSync.serverTarget = switchBackend2;
1397+ switchSync.serverProperty = "checked2";
1398+
1399+ switchBackend2.checked2 = true;
1400+ compare(switchControl.checked, true, "Switch should have been toggled");
1401+ switchBackend2.checked2 = false;
1402+ compare(switchControl.checked, false, "Switch should have been toggled");
1403+ }
1404+
1405+ function test_client_revert() {
1406+ switchBackend.timer.interval = 500;
1407+ switchControl.clicked();
1408+ compare(switchControl.checked, true);
1409+ tryCompare(switchControl, "checked", false);
1410+ }
1411+
1412+ function test_user_trigger() {
1413+ apMenu.trigger();
1414+
1415+ compare(apSyncActivatedSpy.count, 1, "Triggering should have caused signal to be emitted");
1416+ tryCompare(apBackend, "active", true);
1417+ compare(apMenu.active, true, "User value should have updated to match server");
1418+ }
1419+
1420+ function test_user_trigger_doesnt_activate_on_user_property_change() {
1421+ apMenu.active = true;
1422+ compare(apSyncActivatedSpy.count, 0);
1423+ }
1424+ }
1425+}
1426
1427=== modified file 'tests/utils/modules/Ubuntu/Test/UbuntuTestCase.qml'
1428--- tests/utils/modules/Ubuntu/Test/UbuntuTestCase.qml 2014-08-14 11:51:14 +0000
1429+++ tests/utils/modules/Ubuntu/Test/UbuntuTestCase.qml 2015-04-09 12:58:32 +0000
1430@@ -14,13 +14,23 @@
1431 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1432 */
1433
1434+import QtQuick 2.0
1435 import QtTest 1.0
1436 import Ubuntu.Components 1.1
1437 import Ubuntu.Test 0.1 as UT
1438
1439 TestCase {
1440+ id: testCase
1441 TestUtil {id:util}
1442
1443+ ActivityIndicator {
1444+ visible: testCase.running
1445+ anchors.centerIn: parent
1446+ Component.onCompleted: parent = testCase.parent
1447+ z: 100
1448+ running: visible
1449+ }
1450+
1451 // Fake implementation to be provided to items under test
1452 property var fakeDateTime: new function() {
1453 this.currentTimeMs = 0
1454@@ -297,4 +307,13 @@
1455 event.release(0 /* touchId */, x, y)
1456 event.commit()
1457 }
1458+
1459+ // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
1460+ function waitForRendering(item, timeout) {
1461+ if (timeout === undefined)
1462+ timeout = 5000;
1463+ if (!item)
1464+ qtest_fail("No item given to waitForRendering", 1);
1465+ return qtest_results.waitForRendering(item, timeout);
1466+ }
1467 }

Subscribers

People subscribed via source and target branches

to all changes: