on xenial help sign in
Menus for indicators is now created prioritised by distance from current item to speed up user experience.
dd94a66Nick Dedekind
Panel/Indicators.qml+2−1
@@ -265,7 +265,8 @@ Showable {
265 indicators.state = "initial"
265 indicators.state = "initial"
266 } else {
266 } else {
267 if (state == "initial") {
267 if (state == "initial") {
268− indicatorRow.setDefaultItem()
268+ indicatorRow.setDefaultItem();
269+ menuContent.releaseContent();
269 }
270 }
270 }
271 }
271 }
272 }
Panel/MenuContent.qml+12−2
@@ -63,7 +63,7 @@ Rectangle {
63
63
64 delegate: Loader {
64 delegate: Loader {
65 clip: true
65 clip: true
66− property bool contentActive: content.__contentActive
66+ property bool contentActive: content.__contentActive && menuActivator.content[index].active
67
67
68 onContentActiveChanged: {
68 onContentActiveChanged: {
69 if (contentActive && item) {
69 if (contentActive && item) {
@@ -137,6 +137,16 @@ Rectangle {
137 id: contentReleaseTimer
137 id: contentReleaseTimer
138
138
139 interval: contentReleaseInterval
139 interval: contentReleaseInterval
140+ onTriggered: {
140− onTriggered: __contentActive = false
141+ __contentActive = false;
142+ menuActivator.clear();
143+ }
144+ }
145+
146+ Indicators.MenuContentActivator {
147+ id: menuActivator
148+ running: content.__contentActive
149+ baseIndex: content.currentIndex
150+ count: indicatorsModel.count
141 }
151 }
142}
152}
plugins/Unity/Indicators/CMakeLists.txt+1
@@ -23,6 +23,7 @@ set(IndicatorsQML_SOURCES
23 indicators.h
23 indicators.h
24 indicatorsmanager.cpp
24 indicatorsmanager.cpp
25 indicatorsmodel.cpp
25 indicatorsmodel.cpp
26+ menucontentactivator.cpp
26 plugin.cpp
27 plugin.cpp
27)
28)
28add_definitions(-DUNITYINDICATORS_LIBRARY)
29add_definitions(-DUNITYINDICATORS_LIBRARY)
plugins/Unity/Indicators/menucontentactivator.cppnew file+345
@@ -0,0 +1,345 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+ #include "menucontentactivator.h"
20+
21+// Essentially a QTimer wrapper
22+class ContentTimer : public UnityIndicators::AbstractTimer
23+{
24+ Q_OBJECT
25+public:
26+ ContentTimer(QObject *parent) : UnityIndicators::AbstractTimer(parent) {
27+ m_timer.setSingleShot(false);
28+ connect(&m_timer, &QTimer::timeout,
29+ this, &UnityIndicators::AbstractTimer::timeout);
30+ }
31+ virtual int interval() const { return m_timer.interval(); }
32+ virtual void setInterval(int msecs) { m_timer.setInterval(msecs); }
33+ virtual void start() { m_timer.start(); UnityIndicators::AbstractTimer::start(); }
34+ virtual void stop() { m_timer.stop(); UnityIndicators::AbstractTimer::stop(); }
35+private:
36+ QTimer m_timer;
37+};
38+
39+class MenuContentActivatorPrivate : public QObject
40+{
41+ Q_OBJECT
42+public:
43+ MenuContentActivatorPrivate(MenuContentActivator* parent)
44+ : m_running(false),
45+ m_baseIndex(0),
46+ m_delta(0),
47+ m_count(0),
48+ m_timer(NULL),
49+ q(parent)
50+ {}
51+
52+ ~MenuContentActivatorPrivate()
53+ {
54+ qDeleteAll(m_content);
55+ m_content.clear();
56+ }
57+
58+ int findNextInactiveDelta(bool* finished = NULL);
59+
60+ static int content_count(QQmlListProperty<MenuContentState> *prop);
61+ static MenuContentState* content_at(QQmlListProperty<MenuContentState> *prop, int index);
62+
63+ bool m_running;
64+ int m_baseIndex;
65+ int m_delta;
66+ int m_count;
67+ UnityIndicators::AbstractTimer* m_timer;
68+ QMap<int, MenuContentState*> m_content;
69+ MenuContentActivator* q;
70+};
71+
72+MenuContentActivator::MenuContentActivator(QObject* parent)
73+ : QObject(parent),
74+ d(new MenuContentActivatorPrivate(this))
75+{
76+ qRegisterMetaType<QQmlListProperty<MenuContentState> > ("QQmlListProperty<MenuContentState>");
77+
78+ setContentTimer(new ContentTimer(this));
79+ d->m_timer->setInterval(75);
80+}
81+
82+MenuContentActivator::~MenuContentActivator()
83+{
84+ delete d;
85+}
86+
87+void MenuContentActivator::restart()
88+{
89+ // when we start, make sure we have the base index in the list.
90+ setMenuContentState(d->m_baseIndex, true);
91+ setDelta(0);
92+
93+ // check if we've finished before starting the timer.
94+ bool finished = false;
95+ d->findNextInactiveDelta(&finished);
96+ if (!finished) {
97+ d->m_timer->start();
98+ } else {
99+ d->m_timer->stop();
100+ }
101+
102+ if (!d->m_running) {
103+ d->m_running = true;
104+ Q_EMIT runningChanged(true);
105+ }
106+}
107+
108+void MenuContentActivator::stop()
109+{
110+ d->m_timer->stop();
111+ if (!d->m_running) {
112+ d->m_running = false;
113+ Q_EMIT runningChanged(false);
114+ }
115+}
116+
117+void MenuContentActivator::clear()
118+{
119+ qDeleteAll(d->m_content);
120+ d->m_content.clear();
121+
122+ setDelta(0);
123+ d->m_timer->stop();
124+
125+ Q_EMIT contentChanged();
126+}
127+
128+bool MenuContentActivator::isMenuContentActive(int index) const
129+{
130+ if (d->m_content.contains(index))
131+ return d->m_content[index]->isActive();
132+ return false;
133+}
134+
135+void MenuContentActivator::setRunning(bool running)
136+{
137+ if (running) {
138+ restart();
139+ } else {
140+ stop();
141+ }
142+}
143+
144+bool MenuContentActivator::isRunning() const
145+{
146+ return d->m_running;
147+}
148+
149+void MenuContentActivator::setBaseIndex(int index)
150+{
151+ if (d->m_baseIndex != index) {
152+ d->m_baseIndex = index;
153+
154+ if (isRunning()) {
155+ restart();
156+ }
157+
158+ Q_EMIT baseIndexChanged(index);
159+ }
160+}
161+
162+int MenuContentActivator::baseIndex() const
163+{
164+ return d->m_baseIndex;
165+}
166+
167+void MenuContentActivator::setCount(int count)
168+{
169+ if (d->m_count != count) {
170+ d->m_count = count;
171+ Q_EMIT countChanged(count);
172+
173+ if (isRunning()) {
174+ restart();
175+ }
176+ }
177+}
178+
179+int MenuContentActivator::count() const
180+{
181+ return d->m_count;
182+}
183+
184+void MenuContentActivator::setDelta(int delta)
185+{
186+ if (d->m_delta != delta) {
187+ d->m_delta = delta;
188+ Q_EMIT deltaChanged(d->m_delta);
189+ }
190+}
191+
192+int MenuContentActivator::delta() const
193+{
194+ return d->m_delta;
195+}
196+
197+QQmlListProperty<MenuContentState> MenuContentActivator::content()
198+{
199+ return QQmlListProperty<MenuContentState>(this,
200+ 0,
201+ MenuContentActivatorPrivate::content_count,
202+ MenuContentActivatorPrivate::content_at);
203+}
204+
205+void MenuContentActivator::onTimeout()
206+{
207+ bool finished = false;
208+ int tempDelta = d->findNextInactiveDelta(&finished);
209+ if (!finished) {
210+ setMenuContentState(d->m_baseIndex + tempDelta, true);
211+ setDelta(tempDelta);
212+ }
213+
214+ if (finished) {
215+ d->m_timer->stop();
216+ }
217+}
218+
219+void MenuContentActivator::setContentTimer(UnityIndicators::AbstractTimer *timer)
220+{
221+ int interval = 0;
222+ bool timerWasRunning = false;
223+
224+ // can be null when called from the constructor
225+ if (d->m_timer) {
226+ interval = d->m_timer->interval();
227+ timerWasRunning = d->m_timer->isRunning();
228+ if (d->m_timer->parent() == this) {
229+ delete d->m_timer;
230+ }
231+ }
232+
233+ d->m_timer = timer;
234+ timer->setInterval(interval);
235+ connect(timer, &UnityIndicators::AbstractTimer::timeout,
236+ this, &MenuContentActivator::onTimeout);
237+ if (timerWasRunning) {
238+ d->m_timer->start();
239+ }
240+}
241+
242+void MenuContentActivator::setMenuContentState(int index, bool active)
243+{
244+ if (d->m_content.contains(index)) {
245+ d->m_content[index]->setActive(active);
246+ } else {
247+ d->m_content[index] = new MenuContentState(active);
248+ Q_EMIT contentChanged();
249+ }
250+}
251+
252+int MenuContentActivatorPrivate::findNextInactiveDelta(bool* finished)
253+{
254+ if (m_count == 0 || m_baseIndex >= m_count) {
255+ if (finished) *finished = true;
256+ return 0;
257+ }
258+
259+ int tmpDelta = m_delta;
260+ bool topReached = false, bottomReached = false;
261+ while(true) {
262+
263+ // prechecks for bottom and top limits.
264+ if (tmpDelta > 0 && bottomReached) tmpDelta = -tmpDelta;
265+ if (tmpDelta < 0 && topReached) tmpDelta = (-tmpDelta) + 1;
266+
267+ if (tmpDelta > 0) {
268+ // negative of baseIndex
269+ tmpDelta = -tmpDelta;
270+ // reached the bottom?
271+ if (m_baseIndex + tmpDelta < 0) {
272+ bottomReached = true;
273+ // if we've reached the top as well, then we know we're done.
274+ if (topReached) {
275+ if (finished) *finished = true;
276+ return 0;
277+ }
278+ continue;
279+ }
280+ } else {
281+ // positive of baseIndex
282+ tmpDelta = (-tmpDelta) + 1;
283+ // reached the top?
284+ if (m_baseIndex + tmpDelta >= m_count) {
285+ topReached = true;
286+ // if we've reached the bottom as well, then we know we're done.
287+ if (bottomReached) {
288+ if (finished) *finished = true;
289+ return 0;
290+ }
291+ continue;
292+ }
293+ }
294+
295+ if (q->isMenuContentActive(m_baseIndex + tmpDelta)) {
296+ continue;
297+ }
298+ break;
299+ }
300+ if (finished) *finished = false;
301+ return tmpDelta;
302+}
303+
304+int MenuContentActivatorPrivate::content_count(QQmlListProperty<MenuContentState> *prop)
305+{
306+ MenuContentActivator *p = qobject_cast<MenuContentActivator*>(prop->object);
307+ // we'll create MenuContentState on demand.
308+ return p->count();
309+}
310+
311+MenuContentState* MenuContentActivatorPrivate::content_at(QQmlListProperty<MenuContentState> *prop, int index)
312+{
313+ MenuContentActivator *p = qobject_cast<MenuContentActivator*>(prop->object);
314+ MenuContentActivatorPrivate *d = p->d;
315+
316+ if (!d->m_content.contains(index)) {
317+ MenuContentState* content = new MenuContentState(false);
318+ d->m_content[index] = content;
319+ Q_EMIT p->contentChanged();
320+ return content;
321+ }
322+
323+ return d->m_content[index];
324+}
325+
326+MenuContentState::MenuContentState(bool active)
327+ : m_active(active)
328+{
329+}
330+
331+bool MenuContentState::isActive() const
332+{
333+ return m_active;
334+}
335+
336+void MenuContentState::setActive(bool active)
337+{
338+ if (m_active != active) {
339+ m_active = active;
340+ Q_EMIT activeChanged();
341+ }
342+}
343+
344+// Because we are defining a new QObject-based class (ContentTimer) here.
345+#include "menucontentactivator.moc"
plugins/Unity/Indicators/menucontentactivator.hnew file+117
@@ -0,0 +1,117 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+#ifndef MENUCONTENTACTIVATOR_H
20+#define MENUCONTENTACTIVATOR_H
21+
22+#include "unityindicatorsglobal.h"
23+
24+#include <QObject>
25+#include <QTimer>
26+#include <QQmlListProperty>
27+
28+
29+namespace UnityIndicators {
30+/* Defines an interface for a Timer. */
31+class UNITYINDICATORS_EXPORT AbstractTimer : public QObject {
32+ Q_OBJECT
33+public:
34+ AbstractTimer(QObject *parent) : QObject(parent), m_isRunning(false) {}
35+ virtual int interval() const = 0;
36+ virtual void setInterval(int msecs) = 0;
37+ virtual void start() { m_isRunning = true; };
38+ virtual void stop() { m_isRunning = false; }
39+ bool isRunning() const { return m_isRunning; }
40+Q_SIGNALS:
41+ void timeout();
42+private:
43+ bool m_isRunning;
44+};
45+}
46+
47+/* Defines a object to express the active state of a menu. */
48+class UNITYINDICATORS_EXPORT MenuContentState : public QObject
49+{
50+ Q_OBJECT
51+ Q_PROPERTY(bool active READ isActive NOTIFY activeChanged)
52+public:
53+ MenuContentState(bool active);
54+
55+ bool isActive() const;
56+ void setActive(bool active);
57+
58+Q_SIGNALS:
59+ void activeChanged();
60+private:
61+ bool m_active;
62+};
63+
64+class MenuContentActivatorPrivate;
65+
66+class UNITYINDICATORS_EXPORT MenuContentActivator : public QObject
67+{
68+ Q_OBJECT
69+ Q_PROPERTY(int baseIndex READ baseIndex WRITE setBaseIndex NOTIFY baseIndexChanged)
70+ Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged)
71+ Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged)
72+ Q_PROPERTY(QQmlListProperty<MenuContentState> content READ content NOTIFY contentChanged DESIGNABLE false)
73+public:
74+ MenuContentActivator(QObject* parent = NULL);
75+ ~MenuContentActivator();
76+
77+ Q_INVOKABLE void restart();
78+ Q_INVOKABLE void stop();
79+ Q_INVOKABLE void clear();
80+ Q_INVOKABLE bool isMenuContentActive(int index) const;
81+
82+ void setRunning(bool running);
83+ bool isRunning() const;
84+
85+ void setBaseIndex(int index);
86+ int baseIndex() const;
87+
88+ void setCount(int index);
89+ int count() const;
90+
91+ void setDelta(int index);
92+ int delta() const;
93+
94+ QQmlListProperty<MenuContentState> content();
95+
96+ // Replaces the existing Timer with the given one.
97+ //
98+ // Useful for providing a fake timer when testing.
99+ void setContentTimer(UnityIndicators::AbstractTimer *timer);
100+ void setMenuContentState(int index, bool active);
101+
102+Q_SIGNALS:
103+ void baseIndexChanged(int baseIndex);
104+ void deltaChanged(int delta);
105+ void runningChanged(bool running);
106+ void countChanged(int count);
107+ void contentChanged();
108+
109+private Q_SLOTS:
110+ void onTimeout();
111+
112+private:
113+ MenuContentActivatorPrivate* d;
114+ friend class MenuContentActivatorPrivate;
115+};
116+
117+#endif // MENUCONTENTACTIVATOR_H
plugins/Unity/Indicators/plugin.cpp+3
@@ -27,6 +27,7 @@
27#include "indicatorsmanager.h"
27#include "indicatorsmanager.h"
28#include "indicatorsmodel.h"
28#include "indicatorsmodel.h"
29#include "indicators.h"
29#include "indicators.h"
30+#include "menucontentactivator.h"
30
31
31void IndicatorsPlugin::registerTypes(const char *uri)
32void IndicatorsPlugin::registerTypes(const char *uri)
32{
33{
@@ -35,7 +36,9 @@ void IndicatorsPlugin::registerTypes(const char *uri)
35 qmlRegisterType<IndicatorsManager>(uri, 0, 1, "IndicatorsManager");
36 qmlRegisterType<IndicatorsManager>(uri, 0, 1, "IndicatorsManager");
36 qmlRegisterType<FlatMenuProxyModel>(uri, 0, 1, "FlatMenuProxyModel");
37 qmlRegisterType<FlatMenuProxyModel>(uri, 0, 1, "FlatMenuProxyModel");
37 qmlRegisterType<IndicatorsModel>(uri, 0, 1, "IndicatorsModel");
38 qmlRegisterType<IndicatorsModel>(uri, 0, 1, "IndicatorsModel");
39+ qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
38
40
41+ qmlRegisterUncreatableType<MenuContentState>(uri, 0, 1, "MenuContentState", "Can't create MenuContentState class");
39 qmlRegisterUncreatableType<ActionState>(uri, 0, 1, "ActionState", "Can't create ActionState class");
42 qmlRegisterUncreatableType<ActionState>(uri, 0, 1, "ActionState", "Can't create ActionState class");
40 qmlRegisterUncreatableType<NetworkActionState>(uri, 0, 1, "NetworkActionState", "Can't create NetworkActionState class");
43 qmlRegisterUncreatableType<NetworkActionState>(uri, 0, 1, "NetworkActionState", "Can't create NetworkActionState class");
41 qmlRegisterUncreatableType<NetworkConnection>(uri, 0, 1, "NetworkConnection", "Can't create NetworkConnection class");
44 qmlRegisterUncreatableType<NetworkConnection>(uri, 0, 1, "NetworkConnection", "Can't create NetworkConnection class");
plugins/Unity/Indicators/qml/IndicatorPage.qml−6
@@ -188,10 +188,4 @@ IndicatorBase {
188 mainMenu.currentIndex = -1;
188 mainMenu.currentIndex = -1;
189 mainMenu.positionViewAtBeginning();
189 mainMenu.positionViewAtBeginning();
190 }
190 }
191−
192− Component.onCompleted: {
193− if ((busType != 0) && (busName != "") && (deviceMenuObjectPath != "")) {
194− start();
195− }
196− }
197}
191}
tests/mocks/Unity/Indicators/CMakeLists.txt+1
@@ -12,6 +12,7 @@ include_directories(
12set(IndicatorsFakeQml_SOURCES
12set(IndicatorsFakeQml_SOURCES
13 fakeplugin.cpp
13 fakeplugin.cpp
14 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/indicators.h
14 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/indicators.h
15+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/menucontentactivator.cpp
15)
16)
16
17
17set(IndicatorsFakeQml_RESOURCES
18set(IndicatorsFakeQml_RESOURCES
tests/mocks/Unity/Indicators/fakeplugin.cpp+4
@@ -24,11 +24,15 @@
24
24
25// local
25// local
26#include "indicators.h"
26#include "indicators.h"
27+#include "menucontentactivator.h"
27
28
28void IndicatorsFakePlugin::registerTypes(const char * uri)
29void IndicatorsFakePlugin::registerTypes(const char * uri)
29{
30{
30 Q_INIT_RESOURCE(indicators_fake);
31 Q_INIT_RESOURCE(indicators_fake);
31
32
33+ qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
34+
35+ qmlRegisterUncreatableType<MenuContentState>(uri, 0, 1, "MenuContentState", "Can't create MenuContentState class");
32 qmlRegisterUncreatableType<ActionState>(uri, 0, 1, "ActionState", "Can't create ActionState class");
36 qmlRegisterUncreatableType<ActionState>(uri, 0, 1, "ActionState", "Can't create ActionState class");
33 qmlRegisterUncreatableType<NetworkActionState>(uri, 0, 1, "NetworkActionState", "Can't create NetworkActionState class");
37 qmlRegisterUncreatableType<NetworkActionState>(uri, 0, 1, "NetworkActionState", "Can't create NetworkActionState class");
34 qmlRegisterUncreatableType<NetworkConnection>(uri, 0, 1, "NetworkConnection", "Can't create NetworkConnection class");
38 qmlRegisterUncreatableType<NetworkConnection>(uri, 0, 1, "NetworkConnection", "Can't create NetworkConnection class");
tests/plugins/Unity/Indicators/CMakeLists.txt+1
@@ -24,4 +24,5 @@ endmacro(run_tests)
24run_tests(
24run_tests(
25 indicatorsmanagertest
25 indicatorsmanagertest
26 indicatorsmodeltest
26 indicatorsmodeltest
27+ menucontentactivatortest
27 )
28 )
tests/plugins/Unity/Indicators/menucontentactivatortest.cppnew file+303
@@ -0,0 +1,303 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#include "menucontentactivator.h"
21+#include "paths.h"
22+
23+#include <QtTest>
24+#include <QDebug>
25+
26+class FakeTimer : public UnityIndicators::AbstractTimer
27+{
28+ Q_OBJECT
29+public:
30+ FakeTimer(QObject *parent = 0)
31+ : UnityIndicators::AbstractTimer(parent)
32+ {}
33+
34+ virtual int interval() const { return m_duration; }
35+ virtual void setInterval(int msecs) { m_duration = msecs; }
36+
37+ void emitTimeout() {
38+ if (isRunning()) {
39+ Q_EMIT timeout();
40+ }
41+ }
42+private:
43+ int m_duration;
44+};
45+
46+
47+class MenuConentActivatorTest : public QObject
48+{
49+ Q_OBJECT
50+private Q_SLOTS:
51+ void init() // called right before each and every test function is executed
52+ {
53+ m_fakeTimeSource = new FakeTimer(this);
54+ m_deltas.clear();
55+ }
56+ void cleanup() // called right after each and every test function is executed
57+ {
58+ delete m_fakeTimeSource;
59+ m_fakeTimeSource = 0;
60+ }
61+
62+ /*
63+ * Tests the ordering of activation
64+ */
65+ void testContentActiveChange()
66+ {
67+ MenuContentActivator activator;
68+ activator.setContentTimer(m_fakeTimeSource);
69+ activator.setCount(10);
70+ activator.setBaseIndex(5);
71+ activator.restart();
72+
73+ QCOMPARE(activator.isMenuContentActive(4), false);
74+ QCOMPARE(activator.isMenuContentActive(5), true); // always active at base index.
75+ QCOMPARE(activator.isMenuContentActive(6), false);
76+
77+ m_fakeTimeSource->emitTimeout();
78+ QCOMPARE(activator.isMenuContentActive(4), false);
79+ QCOMPARE(activator.isMenuContentActive(5), true);
80+ QCOMPARE(activator.isMenuContentActive(6), true);
81+ QCOMPARE(activator.isMenuContentActive(7), false);
82+
83+ m_fakeTimeSource->emitTimeout();
84+ QCOMPARE(activator.isMenuContentActive(3), false);
85+ QCOMPARE(activator.isMenuContentActive(4), true);
86+ QCOMPARE(activator.isMenuContentActive(5), true);
87+ QCOMPARE(activator.isMenuContentActive(6), true);
88+ QCOMPARE(activator.isMenuContentActive(7), false);
89+
90+ m_fakeTimeSource->emitTimeout();
91+ QCOMPARE(activator.isMenuContentActive(3), false);
92+ QCOMPARE(activator.isMenuContentActive(4), true);
93+ QCOMPARE(activator.isMenuContentActive(5), true);
94+ QCOMPARE(activator.isMenuContentActive(6), true);
95+ QCOMPARE(activator.isMenuContentActive(7), true);
96+ QCOMPARE(activator.isMenuContentActive(8), false);
97+
98+ m_fakeTimeSource->emitTimeout();
99+ QCOMPARE(activator.isMenuContentActive(2), false);
100+ QCOMPARE(activator.isMenuContentActive(3), true);
101+ QCOMPARE(activator.isMenuContentActive(4), true);
102+ QCOMPARE(activator.isMenuContentActive(5), true);
103+ QCOMPARE(activator.isMenuContentActive(6), true);
104+ QCOMPARE(activator.isMenuContentActive(7), true);
105+ QCOMPARE(activator.isMenuContentActive(8), false);
106+
107+ m_fakeTimeSource->emitTimeout();
108+ QCOMPARE(activator.isMenuContentActive(2), false);
109+ QCOMPARE(activator.isMenuContentActive(3), true);
110+ QCOMPARE(activator.isMenuContentActive(4), true);
111+ QCOMPARE(activator.isMenuContentActive(5), true);
112+ QCOMPARE(activator.isMenuContentActive(6), true);
113+ QCOMPARE(activator.isMenuContentActive(7), true);
114+ QCOMPARE(activator.isMenuContentActive(8), true);
115+ QCOMPARE(activator.isMenuContentActive(9), false);
116+
117+ m_fakeTimeSource->emitTimeout();
118+ QCOMPARE(activator.isMenuContentActive(1), false);
119+ QCOMPARE(activator.isMenuContentActive(2), true);
120+ QCOMPARE(activator.isMenuContentActive(3), true);
121+ QCOMPARE(activator.isMenuContentActive(4), true);
122+ QCOMPARE(activator.isMenuContentActive(5), true);
123+ QCOMPARE(activator.isMenuContentActive(6), true);
124+ QCOMPARE(activator.isMenuContentActive(7), true);
125+ QCOMPARE(activator.isMenuContentActive(8), true);
126+ QCOMPARE(activator.isMenuContentActive(9), false);
127+
128+ m_fakeTimeSource->emitTimeout();
129+ QCOMPARE(activator.isMenuContentActive(1), false);
130+ QCOMPARE(activator.isMenuContentActive(2), true);
131+ QCOMPARE(activator.isMenuContentActive(3), true);
132+ QCOMPARE(activator.isMenuContentActive(4), true);
133+ QCOMPARE(activator.isMenuContentActive(5), true);
134+ QCOMPARE(activator.isMenuContentActive(6), true);
135+ QCOMPARE(activator.isMenuContentActive(7), true);
136+ QCOMPARE(activator.isMenuContentActive(8), true);
137+ QCOMPARE(activator.isMenuContentActive(9), true);
138+ QCOMPARE(activator.isMenuContentActive(10), false);
139+
140+ m_fakeTimeSource->emitTimeout();
141+ QCOMPARE(activator.isMenuContentActive(0), false);
142+ QCOMPARE(activator.isMenuContentActive(1), true);
143+ QCOMPARE(activator.isMenuContentActive(2), true);
144+ QCOMPARE(activator.isMenuContentActive(3), true);
145+ QCOMPARE(activator.isMenuContentActive(4), true);
146+ QCOMPARE(activator.isMenuContentActive(5), true);
147+ QCOMPARE(activator.isMenuContentActive(6), true);
148+ QCOMPARE(activator.isMenuContentActive(7), true);
149+ QCOMPARE(activator.isMenuContentActive(8), true);
150+ QCOMPARE(activator.isMenuContentActive(9), true);
151+ QCOMPARE(activator.isMenuContentActive(10), false);
152+
153+ m_fakeTimeSource->emitTimeout();
154+ QCOMPARE(activator.isMenuContentActive(-1), false);
155+ QCOMPARE(activator.isMenuContentActive(0), true);
156+ QCOMPARE(activator.isMenuContentActive(1), true);
157+ QCOMPARE(activator.isMenuContentActive(2), true);
158+ QCOMPARE(activator.isMenuContentActive(3), true);
159+ QCOMPARE(activator.isMenuContentActive(4), true);
160+ QCOMPARE(activator.isMenuContentActive(5), true);
161+ QCOMPARE(activator.isMenuContentActive(6), true);
162+ QCOMPARE(activator.isMenuContentActive(7), true);
163+ QCOMPARE(activator.isMenuContentActive(8), true);
164+ QCOMPARE(activator.isMenuContentActive(9), true);
165+ QCOMPARE(activator.isMenuContentActive(10), false);
166+ }
167+
168+ /*
169+ * Tests the delta calculation for each timeout.
170+ */
171+ void testDelta()
172+ {
173+ MenuContentActivator activator;
174+ activator.setContentTimer(m_fakeTimeSource);
175+ activator.setCount(9);
176+ activator.setBaseIndex(3);
177+
178+ connect(&activator, &MenuContentActivator::deltaChanged,
179+ this, &MenuConentActivatorTest::onDeltaChange);
180+ activator.restart();
181+
182+ QCOMPARE(m_deltas, QList<int>()); // empty
183+ QCOMPARE(getIndexList(&activator), QList<int>()); // empty
184+
185+ m_fakeTimeSource->emitTimeout();
186+ QCOMPARE(m_deltas, QList<int>() << 1);
187+ QCOMPARE(getIndexList(&activator), QList<int>() << 4);
188+
189+ m_fakeTimeSource->emitTimeout();
190+ QCOMPARE(m_deltas, QList<int>() << 1 << -1);
191+ QCOMPARE(getIndexList(&activator), QList<int>() << 4 << 2);
192+
193+ m_fakeTimeSource->emitTimeout();
194+ QCOMPARE(m_deltas, QList<int>() << 1 << -1 << 2);
195+ QCOMPARE(getIndexList(&activator), QList<int>() << 4 << 2 << 5);
196+
197+ m_fakeTimeSource->emitTimeout();
198+ QCOMPARE(m_deltas, QList<int>() << 1 << -1 << 2 << -2);
199+ QCOMPARE(getIndexList(&activator), QList<int>() << 4 << 2 << 5 << 1);
200+
201+ m_fakeTimeSource->emitTimeout();
202+ QCOMPARE(m_deltas, QList<int>() << 1 << -1 << 2 << -2 << 3);
203+ QCOMPARE(getIndexList(&activator), QList<int>() << 4 << 2 << 5 << 1 << 6);
204+
205+ m_fakeTimeSource->emitTimeout();
206+ QCOMPARE(m_deltas, QList<int>() << 1 << -1 << 2 << -2 << 3 << -3);
207+ QCOMPARE(getIndexList(&activator), QList<int>() << 4 << 2 << 5 << 1 << 6 << 0);
208+
209+ m_fakeTimeSource->emitTimeout();
210+ QCOMPARE(m_deltas, QList<int>() << 1 << -1 << 2 << -2 << 3 << -3 << 4);
211+ QCOMPARE(getIndexList(&activator), QList<int>() << 4 << 2 << 5 << 1 << 6 << 0 << 7);
212+
213+ m_fakeTimeSource->emitTimeout();
214+ QCOMPARE(m_deltas, QList<int>() << 1 << -1 << 2 << -2 << 3 << -3 << 4 << 5);
215+ QCOMPARE(getIndexList(&activator), QList<int>() << 4 << 2 << 5 << 1 << 6 << 0 << 7 << 8);
216+
217+ m_fakeTimeSource->emitTimeout();
218+ QCOMPARE(m_deltas, QList<int>() << 1 << -1 << 2 << -2 << 3 << -3 << 4 << 5);
219+ QCOMPARE(getIndexList(&activator), QList<int>() << 4 << 2 << 5 << 1 << 6 << 0 << 7 << 8);
220+ }
221+
222+ /*
223+ * Tests that changing the base index re-prioritizes the activation
224+ * around the base index.
225+ */
226+ void testBaseIndexChange()
227+ {
228+ MenuContentActivator activator;
229+ activator.setContentTimer(m_fakeTimeSource);
230+ activator.setCount(12);
231+ activator.setBaseIndex(5);
232+ activator.restart();
233+
234+ m_fakeTimeSource->emitTimeout();
235+ m_fakeTimeSource->emitTimeout();
236+ QCOMPARE(activator.isMenuContentActive(3), false);
237+ QCOMPARE(activator.isMenuContentActive(4), true);
238+ QCOMPARE(activator.isMenuContentActive(5), true); // always active at base index.
239+ QCOMPARE(activator.isMenuContentActive(6), true);
240+ QCOMPARE(activator.isMenuContentActive(7), false);
241+
242+ activator.setBaseIndex(8);
243+ QCOMPARE(activator.isMenuContentActive(3), false);
244+ QCOMPARE(activator.isMenuContentActive(4), true);
245+ QCOMPARE(activator.isMenuContentActive(5), true);
246+ QCOMPARE(activator.isMenuContentActive(6), true);
247+ QCOMPARE(activator.isMenuContentActive(7), false);
248+ QCOMPARE(activator.isMenuContentActive(8), true);
249+ QCOMPARE(activator.isMenuContentActive(9), false);
250+
251+ m_fakeTimeSource->emitTimeout();
252+ QCOMPARE(activator.isMenuContentActive(3), false);
253+ QCOMPARE(activator.isMenuContentActive(4), true);
254+ QCOMPARE(activator.isMenuContentActive(5), true);
255+ QCOMPARE(activator.isMenuContentActive(6), true);
256+ QCOMPARE(activator.isMenuContentActive(7), false);
257+ QCOMPARE(activator.isMenuContentActive(8), true);
258+ QCOMPARE(activator.isMenuContentActive(9), true);
259+ QCOMPARE(activator.isMenuContentActive(10), false);
260+
261+ m_fakeTimeSource->emitTimeout();
262+ QCOMPARE(activator.isMenuContentActive(3), false);
263+ QCOMPARE(activator.isMenuContentActive(4), true);
264+ QCOMPARE(activator.isMenuContentActive(5), true);
265+ QCOMPARE(activator.isMenuContentActive(6), true);
266+ QCOMPARE(activator.isMenuContentActive(7), true);
267+ QCOMPARE(activator.isMenuContentActive(8), true);
268+ QCOMPARE(activator.isMenuContentActive(9), true);
269+ QCOMPARE(activator.isMenuContentActive(10), false);
270+
271+ m_fakeTimeSource->emitTimeout();
272+ QCOMPARE(activator.isMenuContentActive(3), false);
273+ QCOMPARE(activator.isMenuContentActive(4), true);
274+ QCOMPARE(activator.isMenuContentActive(5), true);
275+ QCOMPARE(activator.isMenuContentActive(6), true);
276+ QCOMPARE(activator.isMenuContentActive(7), true);
277+ QCOMPARE(activator.isMenuContentActive(8), true);
278+ QCOMPARE(activator.isMenuContentActive(9), true);
279+ QCOMPARE(activator.isMenuContentActive(10), true);
280+ QCOMPARE(activator.isMenuContentActive(11), false);
281+ }
282+
283+ void onDeltaChange(int delta)
284+ {
285+ m_deltas << delta;
286+ }
287+
288+private:
289+ QList<int> getIndexList(MenuContentActivator* activator)
290+ {
291+ QList<int> list;
292+ Q_FOREACH(int delta, m_deltas) {
293+ list << (activator->baseIndex() + delta);
294+ }
295+ return list;
296+ }
297+
298+ FakeTimer* m_fakeTimeSource;
299+ QList<int> m_deltas;
300+};
301+
302+QTEST_MAIN(MenuConentActivatorTest)
303+#include "menucontentactivatortest.moc"