on xenial help sign in
merge trunk
commit8c220a2133bae7651ba4e4e5cf08ff0c7a19e98f authorMichael Zanetti <michael.zanetti@canonical.com> dateFri 10 Jan 2014, 11:29 (13 years ago) parent2b5e5ca040e106
115 files changed+3139−1387
CMakeLists.txt+2 data/CMakeLists.txt+3−2 data/unity-scope-tool.desktop.in+8 debian/changelog+45 debian/control+9 debian/unity-scope-tool.install+3 plugins/DashViews/CMakeLists.txt+2 plugins/DashViews/abstractjournal.cpp+388 plugins/DashViews/abstractjournal.h+145 plugins/DashViews/horizontaljournal.cpp+253 plugins/DashViews/horizontaljournal.h+67 plugins/DashViews/plugin.cpp+2 plugins/DashViews/verticaljournal.cpp+77−406 plugins/DashViews/verticaljournal.h+13−91 plugins/Utils/qsortfilterproxymodelqml.cpp+12 plugins/Utils/qsortfilterproxymodelqml.h+1 po/ar.po+2−2 po/ast.po+2−2 po/az.po+2−2 po/bg.po+2−2 po/ca.po+2−2 po/cs.po+9−7 po/de.po+2−2 po/el.po+2−2 po/en_AU.po+2−2 po/en_GB.po+2−2 po/eo.po+2−2 po/es.po+3−3 po/fa.po+2−2 po/fi.po+2−2 po/fo.po+2−2 po/fr.po+2−2 po/gd.po+2−2 po/gl.po+2−2 po/he.po+2−2 po/hr.po+2−2 po/hu.po+32−28 po/hy.po+2−2 po/id.po+2−2 po/is.po+2−2 po/it.po+2−2 po/ja.po+2−2 po/km.po+2−2 po/kn.po+2−2 po/ko.po+2−2 po/ky.po+2−2 po/lo.po+2−2 po/lv.po+2−2 po/ml.po+2−2 po/my.po+2−2 po/nb.po+2−2 po/nl.po+2−2 po/pl.po+2−2 po/pt_BR.po+2−2 po/ro.po+2−2 po/ru.po+9−8 po/shn.po+2−2 po/si.po+2−2 po/sl.po+2−2 po/sr.po+2−2 po/sv.po+2−2 po/ta.po+2−2 po/te.po+2−2 po/tr.po+2−2 po/ug.po+2−2 po/uk.po+2−2 po/xh.po+2−2 po/zh_CN.po+2−2 po/zh_TW.po+2−2 qml/Components/PageHeader.qml+14−39 qml/Components/PageHeaderLabel.qml+41 qml/Dash/Dash.qml−18 qml/Dash/DashApps.qml−3 qml/Dash/DashBar.qml−128 qml/Dash/DashContent.qml+123−62 qml/Dash/DashContentOpenEffect.qml+57 qml/Dash/DashContentTabBarStyle.qml+21 qml/Dash/GenericScopeView.qml+28−228 qml/Dash/PreviewListView.qml+212 qml/Dash/graphics/tabbarchevron@27.png qml/Hud/HudParametrizedActionsPage.qml+1−1 qml/Notifications/Notification.qml+14−4 qml/Notifications/NotificationMenuItemFactory.qml+4−5 qml/Notifications/Notifications.qml+2−1 qml/Panel/IndicatorItem.qml−13 qml/Panel/IndicatorRow.qml+39−8 qml/Panel/Indicators.qml+22−11 qml/Panel/MenuContent.qml+3−1 qml/Panel/Panel.qml+1−1 qml/ScopeTool.qml+177 qml/Shell.qml−1 src/CMakeLists.txt+12−1 tests/mocks/Unity/fake_scopes.cpp+3 tests/mocks/Unity/fake_scopes.h+2−1 tests/plugins/DashViews/CMakeLists.txt+37−8 tests/plugins/DashViews/horizontaljournaltest.cpp+465 tests/plugins/DashViews/horizontaljournaltest.qml+43 tests/plugins/DashViews/horizontaljournaltry.cpp+90 tests/plugins/DashViews/horizontaljournaltry.qml+99 tests/plugins/DashViews/verticaljournaltest.cpp+37−5 tests/qmltests/CMakeLists.txt+1−2 tests/qmltests/Components/tst_PageHeader.qml → tests/qmltests/Components/tst_PageHeaderLabel.qml+2−2 tests/qmltests/Dash/qml/FakeScopeView.qml+1 tests/qmltests/Dash/tst_Dash.qml+6−8 tests/qmltests/Dash/tst_DashBar.qml−111 tests/qmltests/Dash/tst_DashContent.qml+67−4 tests/qmltests/Dash/tst_GenericScopeView.qml+57−27 tests/qmltests/Notifications/tst_Notifications.qml+4−11 tests/qmltests/Panel/tst_IndicatorItem.qml−11 tests/qmltests/Panel/tst_IndicatorRow.qml+18−4 tools/CMakeLists.txt+11 tools/registry-tracker.cpp+116 tools/registry-tracker.h+44 tools/scopetool.cpp+84 unity8.qmlproject−25
CMakeLists.txt+2
@@ -96,6 +96,7 @@ include(autopilot)
9696declare_autopilot_test(shell unity8.shell ${CMAKE_SOURCE_DIR}/tests/autopilot/)
9797
9898set(SHELL_APP unity8)
99+set(SCOPE_TOOL unity-scope-tool)
99100
100101include_directories(
101102 ${CMAKE_SOURCE_DIR}/include
@@ -105,6 +106,7 @@ include_directories(
105106# add subdirectories to build
106107add_subdirectory(include)
107108add_subdirectory(src)
109+add_subdirectory(tools)
108110add_subdirectory(qml)
109111add_subdirectory(tests)
110112add_subdirectory(plugins)
data/CMakeLists.txt+3−2
@@ -1,7 +1,8 @@
11# generate desktop file
22configure_file(${SHELL_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop @ONLY)
3+configure_file(${SCOPE_TOOL}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${SCOPE_TOOL}.desktop @ONLY)
34
4−# install desktop file
5−install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop
5+# install desktop files
6+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop ${CMAKE_CURRENT_BINARY_DIR}/${SCOPE_TOOL}.desktop
67 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
78 )
data/unity-scope-tool.desktop.innew file+8
@@ -0,0 +1,8 @@
1+[Desktop Entry]
2+Type=Application
3+Name=Unity Scope Tool
4+Comment=Developer tool for Unity scopes.
5+Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/unity-scope-tool
6+Terminal=false
7+Icon=unity-scope-tool
8+X-Ubuntu-Touch=true
debian/changelog+45
@@ -1,3 +1,48 @@
1+unity8 (7.84+14.04.20131220-0ubuntu1) trusty; urgency=low
2+
3+ [ Michał Sawicz ]
4+ * Clean up root project file. Introduce include/ and qml/ and move
5+ files around to be where they fit, also adapt everything else to
6+ match.
7+
8+ [ Michael Hall ]
9+ * Update CODING to reflect the fact that only 14.04 is supported
10+ currently.
11+
12+ [ Christopher Lee ]
13+ * Make use of helpers in all tests. (LP: #1260860). (LP: #1260860)
14+
15+ [ Nic ]
16+ * Added kill time 30 to unity8 override. Added install path. (LP:
17+ #1260379)
18+
19+ [ Nick Dedekind ]
20+ * Added parser for strftime in TimeFormatter. Moved TimeFormatter to
21+ Utils plugin.
22+
23+ [ Mirco Müller ]
24+ * Support fullscreen for special-case extended snap-decision of the
25+ pin-unlock dialog.
26+
27+ [ Michael Zanetti ]
28+ * Change the default behaviour of the Lockscreen to have a variable
29+ PIN length, requiring the user to confirm with OK.
30+
31+ [ Daniel d'Andrada ]
32+ * Update CODING with instructions on how to run tests.
33+
34+ [ Dimitri John Ledkov ]
35+ * Fix cross-compilation.
36+
37+ [ Albert Astals ]
38+ * Vertical journal Comes from lp:~aacid/+junk/verticalJournal.
39+ * Add code and tests for incremental inserting/removing from the end.
40+
41+ [ Ubuntu daily release ]
42+ * Automatic snapshot from revision 603
43+
44+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 20 Dec 2013 03:23:08 +0000
45+
146unity8 (7.84+14.04.20131212-0ubuntu1) trusty; urgency=low
247
348 [ Michał Sawicz ]
debian/control+9
@@ -59,6 +59,15 @@ Depends: qmenumodel-qml (>= 0.2.7),
5959Description: Indicators client test application
6060 This package contains the client application for indicators used by autopilot
6161
62+Package: unity-scope-tool
63+Architecture: any
64+Depends: unity8 (= ${binary:Version}),
65+ ${misc:Depends},
66+ ${shlibs:Depends},
67+Description: Development tool for Unity scopes
68+ This package contains the development tool for new generation Unity scopes, based
69+ on Unity8.
70+
6271Package: unity8
6372Architecture: any
6473Provides: indicator-renderer,
debian/unity-scope-tool.installnew file+3
@@ -0,0 +1,3 @@
1+usr/bin/unity-scope-tool
2+usr/share/applications/unity-scope-tool.desktop
3+usr/share/unity8/ScopeTool.qml
plugins/DashViews/CMakeLists.txt+2
@@ -24,7 +24,9 @@ remove_definitions(-DQT_NO_KEYWORDS)
2424set(QMLPLUGIN_SRC
2525 plugin.cpp
2626 listviewwithpageheader.cpp
27+ abstractjournal.cpp
2728 verticaljournal.cpp
29+ horizontaljournal.cpp
2830 )
2931
3032add_library(DashViews-qml MODULE
plugins/DashViews/abstractjournal.cppnew file+388
@@ -0,0 +1,388 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "abstractjournal.h"
18+
19+static const qreal bufferRatio = 0.5;
20+
21+AbstractJournal::AbstractJournal()
22+ : m_delegateModel(nullptr)
23+ , m_asyncRequestedIndex(-1)
24+ , m_columnSpacing(0)
25+ , m_rowSpacing(0)
26+ , m_delegateCreationBegin(0)
27+ , m_delegateCreationEnd(0)
28+ , m_delegateCreationBeginValid(false)
29+ , m_delegateCreationEndValid(false)
30+ , m_needsRelayout(false)
31+ , m_delegateValidated(false)
32+ , m_implicitHeightDirty(false)
33+{
34+ connect(this, SIGNAL(widthChanged()), this, SLOT(relayout()));
35+ connect(this, SIGNAL(heightChanged()), this, SLOT(onHeightChanged()));
36+}
37+
38+QAbstractItemModel *AbstractJournal::model() const
39+{
40+ return m_delegateModel ? m_delegateModel->model().value<QAbstractItemModel *>() : nullptr;
41+}
42+
43+void AbstractJournal::setModel(QAbstractItemModel *model)
44+{
45+ if (model != this->model()) {
46+ if (!m_delegateModel) {
47+ createDelegateModel();
48+ } else {
49+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
50+ disconnect(m_delegateModel, SIGNAL(modelUpdated(QQuickChangeSet,bool)), this, SLOT(onModelUpdated(QQuickChangeSet,bool)));
51+ }
52+ m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
53+ connect(m_delegateModel, SIGNAL(modelUpdated(QQuickChangeSet,bool)), this, SLOT(onModelUpdated(QQuickChangeSet,bool)));
54+#else
55+ disconnect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(onModelUpdated(QQmlChangeSet,bool)));
56+ }
57+ m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
58+ connect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(onModelUpdated(QQmlChangeSet,bool)));
59+#endif
60+
61+ cleanupExistingItems();
62+
63+ Q_EMIT modelChanged();
64+ polish();
65+ }
66+}
67+
68+QQmlComponent *AbstractJournal::delegate() const
69+{
70+ return m_delegateModel ? m_delegateModel->delegate() : nullptr;
71+}
72+
73+void AbstractJournal::setDelegate(QQmlComponent *delegate)
74+{
75+ if (delegate != this->delegate()) {
76+ if (!m_delegateModel) {
77+ createDelegateModel();
78+ }
79+
80+ cleanupExistingItems();
81+
82+ m_delegateModel->setDelegate(delegate);
83+
84+ Q_EMIT delegateChanged();
85+ m_delegateValidated = false;
86+ polish();
87+ }
88+}
89+
90+qreal AbstractJournal::columnSpacing() const
91+{
92+ return m_columnSpacing;
93+}
94+
95+void AbstractJournal::setColumnSpacing(qreal columnSpacing)
96+{
97+ if (columnSpacing != m_columnSpacing) {
98+ m_columnSpacing = columnSpacing;
99+ Q_EMIT columnSpacingChanged();
100+
101+ if (isComponentComplete()) {
102+ relayout();
103+ }
104+ }
105+}
106+
107+qreal AbstractJournal::rowSpacing() const
108+{
109+ return m_rowSpacing;
110+}
111+
112+void AbstractJournal::setRowSpacing(qreal rowSpacing)
113+{
114+ if (rowSpacing != m_rowSpacing) {
115+ m_rowSpacing = rowSpacing;
116+ Q_EMIT rowSpacingChanged();
117+
118+ if (isComponentComplete()) {
119+ relayout();
120+ }
121+ }
122+}
123+
124+qreal AbstractJournal::delegateCreationBegin() const
125+{
126+ return m_delegateCreationBegin;
127+}
128+
129+void AbstractJournal::setDelegateCreationBegin(qreal begin)
130+{
131+ m_delegateCreationBeginValid = true;
132+ if (m_delegateCreationBegin == begin)
133+ return;
134+ m_delegateCreationBegin = begin;
135+ if (isComponentComplete()) {
136+ polish();
137+ }
138+ emit delegateCreationBeginChanged();
139+}
140+
141+void AbstractJournal::resetDelegateCreationBegin()
142+{
143+ m_delegateCreationBeginValid = false;
144+ if (m_delegateCreationBegin == 0)
145+ return;
146+ m_delegateCreationBegin = 0;
147+ if (isComponentComplete()) {
148+ polish();
149+ }
150+ emit delegateCreationBeginChanged();
151+}
152+
153+qreal AbstractJournal::delegateCreationEnd() const
154+{
155+ return m_delegateCreationEnd;
156+}
157+
158+void AbstractJournal::setDelegateCreationEnd(qreal end)
159+{
160+ m_delegateCreationEndValid = true;
161+ if (m_delegateCreationEnd == end)
162+ return;
163+ m_delegateCreationEnd = end;
164+ if (isComponentComplete()) {
165+ polish();
166+ }
167+ emit delegateCreationEndChanged();
168+}
169+
170+void AbstractJournal::resetDelegateCreationEnd()
171+{
172+ m_delegateCreationEndValid = false;
173+ if (m_delegateCreationEnd == 0)
174+ return;
175+ m_delegateCreationEnd = 0;
176+ if (isComponentComplete()) {
177+ polish();
178+ }
179+ emit delegateCreationEndChanged();
180+}
181+
182+void AbstractJournal::createDelegateModel()
183+{
184+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
185+ m_delegateModel = new QQuickVisualDataModel(qmlContext(this), this);
186+ connect(m_delegateModel, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(itemCreated(int,QQuickItem*)));
187+#else
188+ m_delegateModel = new QQmlDelegateModel(qmlContext(this), this);
189+ connect(m_delegateModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(itemCreated(int,QObject*)));
190+#endif
191+ if (isComponentComplete())
192+ m_delegateModel->componentComplete();
193+}
194+
195+void AbstractJournal::refill()
196+{
197+ if (!isComponentComplete()) {
198+ return;
199+ }
200+
201+ const bool delegateRangesValid = m_delegateCreationBeginValid && m_delegateCreationEndValid;
202+ const qreal from = delegateRangesValid ? m_delegateCreationBegin : 0;
203+ const qreal to = delegateRangesValid ? m_delegateCreationEnd : from + height();
204+ const qreal buffer = (to - from) * bufferRatio;
205+ const qreal bufferFrom = from - buffer;
206+ const qreal bufferTo = to + buffer;
207+
208+ bool added = addVisibleItems(from, to, false);
209+ bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
210+ added |= addVisibleItems(bufferFrom, bufferTo, true);
211+
212+ if (added || removed) {
213+ m_implicitHeightDirty = true;
214+ polish();
215+ }
216+}
217+
218+bool AbstractJournal::addVisibleItems(qreal fillFromY, qreal fillToY, bool asynchronous)
219+{
220+ if (!delegate())
221+ return false;
222+
223+ if (m_delegateModel->count() == 0)
224+ return false;
225+
226+ int modelIndex;
227+ qreal yPos;
228+ findBottomModelIndexToAdd(&modelIndex, &yPos);
229+ bool changed = false;
230+ while (modelIndex < m_delegateModel->count() && yPos <= fillToY) {
231+ if (!createItem(modelIndex, asynchronous))
232+ break;
233+
234+ changed = true;
235+ findBottomModelIndexToAdd(&modelIndex, &yPos);
236+ }
237+
238+ findTopModelIndexToAdd(&modelIndex, &yPos);
239+ while (modelIndex >= 0 && yPos > fillFromY) {
240+ if (!createItem(modelIndex, asynchronous))
241+ break;
242+
243+ changed = true;
244+ findTopModelIndexToAdd(&modelIndex, &yPos);
245+ }
246+
247+ return changed;
248+}
249+
250+QQuickItem *AbstractJournal::createItem(int modelIndex, bool asynchronous)
251+{
252+ if (asynchronous && m_asyncRequestedIndex != -1)
253+ return nullptr;
254+
255+ m_asyncRequestedIndex = -1;
256+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
257+ QQuickItem *item = m_delegateModel->item(modelIndex, asynchronous);
258+#else
259+ QObject* object = m_delegateModel->object(modelIndex, asynchronous);
260+ QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
261+#endif
262+ if (!item) {
263+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
264+ m_asyncRequestedIndex = modelIndex;
265+#else
266+ if (object) {
267+ m_delegateModel->release(object);
268+ if (!m_delegateValidated) {
269+ m_delegateValidated = true;
270+ QObject* delegateObj = delegate();
271+ qmlInfo(delegateObj ? delegateObj : this) << "Delegate must be of Item type";
272+ }
273+ } else {
274+ m_asyncRequestedIndex = modelIndex;
275+ }
276+#endif
277+ return nullptr;
278+ } else {
279+ positionItem(modelIndex, item);
280+ return item;
281+ }
282+}
283+
284+void AbstractJournal::releaseItem(QQuickItem *item)
285+{
286+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
287+ QQuickVisualModel::ReleaseFlags flags = m_delegateModel->release(item);
288+ if (flags & QQuickVisualModel::Destroyed) {
289+#else
290+ QQmlDelegateModel::ReleaseFlags flags = m_delegateModel->release(item);
291+ if (flags & QQmlDelegateModel::Destroyed) {
292+#endif
293+ item->setParentItem(nullptr);
294+ }
295+}
296+
297+void AbstractJournal::setImplicitHeightDirty()
298+{
299+ m_implicitHeightDirty = true;
300+}
301+
302+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
303+void AbstractJournal::itemCreated(int modelIndex, QQuickItem *item)
304+{
305+#else
306+void AbstractJournal::itemCreated(int modelIndex, QObject *object)
307+{
308+ QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
309+ if (!item) {
310+ qWarning() << "AbstractJournal::itemCreated got a non item for index" << modelIndex;
311+ return;
312+ }
313+#endif
314+ item->setParentItem(this);
315+
316+ // We only need to call createItem if we are here because of an asynchronous generation
317+ // otherwise we are in this slot because createItem is creating the item sync
318+ // and thus we don't need to call createItem again, nor need to set m_implicitHeightDirty
319+ // and call polish because the sync createItem was called from addVisibleItems that
320+ // is called from refill that will already do those if an item was added
321+ if (modelIndex == m_asyncRequestedIndex) {
322+ createItem(modelIndex, false);
323+ m_implicitHeightDirty = true;
324+ polish();
325+ }
326+}
327+
328+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
329+void AbstractJournal::onModelUpdated(const QQuickChangeSet &changeSet, bool reset)
330+#else
331+void AbstractJournal::onModelUpdated(const QQmlChangeSet &changeSet, bool reset)
332+#endif
333+{
334+ if (reset) {
335+ cleanupExistingItems();
336+ } else {
337+ processModelRemoves(changeSet.removes());
338+ }
339+ polish();
340+}
341+
342+
343+void AbstractJournal::relayout()
344+{
345+ m_needsRelayout = true;
346+ polish();
347+}
348+
349+void AbstractJournal::onHeightChanged()
350+{
351+ polish();
352+}
353+
354+void AbstractJournal::updatePolish()
355+{
356+ if (!model())
357+ return;
358+
359+ if (m_needsRelayout) {
360+ doRelayout();
361+ m_needsRelayout = false;
362+ m_implicitHeightDirty = true;
363+ }
364+
365+ refill();
366+
367+ const bool delegateRangesValid = m_delegateCreationBeginValid && m_delegateCreationEndValid;
368+ const qreal from = delegateRangesValid ? m_delegateCreationBegin : 0;
369+ const qreal to = delegateRangesValid ? m_delegateCreationEnd : from + height();
370+ updateItemCulling(from, to);
371+
372+ if (m_implicitHeightDirty) {
373+ calculateImplicitHeight();
374+ m_implicitHeightDirty = false;
375+ }
376+}
377+
378+void AbstractJournal::componentComplete()
379+{
380+ if (m_delegateModel)
381+ m_delegateModel->componentComplete();
382+
383+ QQuickItem::componentComplete();
384+
385+ m_needsRelayout = true;
386+
387+ polish();
388+}
plugins/DashViews/abstractjournal.hnew file+145
@@ -0,0 +1,145 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef ABSTRACTJOURNAL_H
18+#define ABSTRACTJOURNAL_H
19+
20+#include <QQuickItem>
21+
22+class QAbstractItemModel;
23+class QQmlComponent;
24+#pragma GCC diagnostic push
25+#pragma GCC diagnostic ignored "-pedantic"
26+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
27+#include <private/qquickvisualdatamodel_p.h>
28+#else
29+#include <private/qqmldelegatemodel_p.h>
30+#include <qqmlinfo.h>
31+#endif
32+#pragma GCC diagnostic pop
33+
34+class AbstractJournal : public QQuickItem
35+{
36+ Q_OBJECT
37+
38+ Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged)
39+ Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
40+ Q_PROPERTY(qreal columnSpacing READ columnSpacing WRITE setColumnSpacing NOTIFY columnSpacingChanged)
41+ Q_PROPERTY(qreal rowSpacing READ rowSpacing WRITE setRowSpacing NOTIFY rowSpacingChanged)
42+ Q_PROPERTY(qreal delegateCreationBegin READ delegateCreationBegin
43+ WRITE setDelegateCreationBegin
44+ NOTIFY delegateCreationBeginChanged
45+ RESET resetDelegateCreationBegin)
46+ Q_PROPERTY(qreal delegateCreationEnd READ delegateCreationEnd
47+ WRITE setDelegateCreationEnd
48+ NOTIFY delegateCreationEndChanged
49+ RESET resetDelegateCreationEnd)
50+
51+friend class VerticalJournalTest;
52+friend class HorizontalJournalTest;
53+
54+public:
55+ AbstractJournal();
56+
57+ QAbstractItemModel *model() const;
58+ void setModel(QAbstractItemModel *model);
59+
60+ QQmlComponent *delegate() const;
61+ void setDelegate(QQmlComponent *delegate);
62+
63+ qreal columnSpacing() const;
64+ void setColumnSpacing(qreal columnSpacing);
65+
66+ qreal rowSpacing() const;
67+ void setRowSpacing(qreal rowSpacing);
68+
69+ qreal delegateCreationBegin() const;
70+ void setDelegateCreationBegin(qreal);
71+ void resetDelegateCreationBegin();
72+
73+ qreal delegateCreationEnd() const;
74+ void setDelegateCreationEnd(qreal);
75+ void resetDelegateCreationEnd();
76+
77+Q_SIGNALS:
78+ void modelChanged();
79+ void delegateChanged();
80+ void columnSpacingChanged();
81+ void rowSpacingChanged();
82+ void delegateCreationBeginChanged();
83+ void delegateCreationEndChanged();
84+
85+protected Q_SLOTS:
86+ void relayout();
87+
88+protected:
89+ void updatePolish() override;
90+ void componentComplete() override;
91+
92+ void releaseItem(QQuickItem *item);
93+ void setImplicitHeightDirty();
94+
95+private Q_SLOTS:
96+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
97+ void itemCreated(int modelIndex, QQuickItem *item);
98+ void onModelUpdated(const QQuickChangeSet &changeSet, bool reset);
99+#else
100+ void itemCreated(int modelIndex, QObject *object);
101+ void onModelUpdated(const QQmlChangeSet &changeSet, bool reset);
102+#endif
103+ void onHeightChanged();
104+
105+private:
106+ void createDelegateModel();
107+ void refill();
108+ bool addVisibleItems(qreal fillFromY, qreal fillToY, bool asynchronous);
109+ QQuickItem *createItem(int modelIndex, bool asynchronous);
110+
111+ virtual void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) = 0;
112+ virtual void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) = 0;
113+ virtual void positionItem(int modelIndex, QQuickItem *item) = 0;
114+ virtual bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) = 0;
115+ virtual void cleanupExistingItems() = 0;
116+ virtual void doRelayout() = 0;
117+ virtual void updateItemCulling(qreal visibleFromY, qreal visibleToY) = 0;
118+ virtual void calculateImplicitHeight() = 0;
119+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
120+ virtual void processModelRemoves(const QVector<QQuickChangeSet::Remove> &removes) = 0;
121+#else
122+ virtual void processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes) = 0;
123+#endif
124+
125+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
126+ QQuickVisualDataModel *m_delegateModel;
127+#else
128+ QQmlDelegateModel *m_delegateModel;
129+#endif
130+
131+ // Index we are waiting because we requested it asynchronously
132+ int m_asyncRequestedIndex;
133+
134+ int m_columnSpacing;
135+ int m_rowSpacing;
136+ qreal m_delegateCreationBegin;
137+ qreal m_delegateCreationEnd;
138+ bool m_delegateCreationBeginValid;
139+ bool m_delegateCreationEndValid;
140+ bool m_needsRelayout;
141+ bool m_delegateValidated;
142+ bool m_implicitHeightDirty;
143+};
144+
145+#endif
plugins/DashViews/horizontaljournal.cppnew file+253
@@ -0,0 +1,253 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+/*
18+ * The implementation is centered around m_visibleItems
19+ * that a list for each of the items in the view.
20+ * m_firstVisibleIndex is the index of the first item in m_visibleItems
21+ * m_lastInRowIndexPosition is a map that contains the x position
22+ * of items that are the last ones of a row so we can reconstruct the rows
23+ * when building back
24+ */
25+
26+#include "horizontaljournal.h"
27+
28+#include <qqmlengine.h>
29+#pragma GCC diagnostic push
30+#pragma GCC diagnostic ignored "-pedantic"
31+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
32+#include <private/qquickvisualdatamodel_p.h>
33+#else
34+#include <private/qqmldelegatemodel_p.h>
35+#include <qqmlinfo.h>
36+#endif
37+#include <private/qquickitem_p.h>
38+#pragma GCC diagnostic pop
39+
40+static const qreal bufferRatio = 0.5;
41+
42+HorizontalJournal::HorizontalJournal()
43+ : m_firstVisibleIndex(-1)
44+ , m_rowHeight(0)
45+{
46+}
47+
48+qreal HorizontalJournal::rowHeight() const
49+{
50+ return m_rowHeight;
51+}
52+
53+void HorizontalJournal::setRowHeight(qreal rowHeight)
54+{
55+ if (rowHeight != m_rowHeight) {
56+ m_rowHeight = rowHeight;
57+ Q_EMIT rowHeightChanged();
58+
59+ if (isComponentComplete()) {
60+ Q_FOREACH(QQuickItem *item, m_visibleItems) {
61+ item->setHeight(rowHeight);
62+ }
63+ relayout();
64+ }
65+ }
66+}
67+
68+void HorizontalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
69+{
70+ if (m_visibleItems.isEmpty()) {
71+ *modelIndex = 0;
72+ *yPos = 0;
73+ } else {
74+ *modelIndex = m_firstVisibleIndex + m_visibleItems.count();
75+ if (m_lastInRowIndexPosition.contains(*modelIndex - 1)) {
76+ *yPos = m_visibleItems.last()->y() + m_rowHeight + rowSpacing();
77+ } else {
78+ *yPos = m_visibleItems.last()->y();
79+ }
80+ }
81+}
82+
83+void HorizontalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
84+{
85+ if (m_visibleItems.isEmpty()) {
86+ *modelIndex = -1;
87+ *yPos = INT_MIN;
88+ } else {
89+ *modelIndex = m_firstVisibleIndex - 1;
90+ if (m_lastInRowIndexPosition.contains(*modelIndex)) {
91+ *yPos = m_visibleItems.first()->y() - rowSpacing() - m_rowHeight;
92+ } else {
93+ *yPos = m_visibleItems.first()->y();
94+ }
95+ }
96+}
97+
98+bool HorizontalJournal::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
99+{
100+ bool changed = false;
101+
102+ while (!m_visibleItems.isEmpty() && m_visibleItems.first()->y() + m_rowHeight < bufferFromY) {
103+ releaseItem(m_visibleItems.takeFirst());
104+ changed = true;
105+ m_firstVisibleIndex++;
106+ }
107+
108+ while (!m_visibleItems.isEmpty() && m_visibleItems.last()->y() > bufferToY) {
109+ releaseItem(m_visibleItems.takeLast());
110+ changed = true;
111+ m_lastInRowIndexPosition.remove(m_firstVisibleIndex + m_visibleItems.count());
112+ }
113+
114+ if (m_visibleItems.isEmpty()) {
115+ m_firstVisibleIndex = -1;
116+ }
117+
118+ return changed;
119+}
120+
121+void HorizontalJournal::positionItem(int modelIndex, QQuickItem *item)
122+{
123+ if (item->height() != m_rowHeight) {
124+ qWarning() << "Item" << modelIndex << "height is not the one that the rowHeight mandates, resetting it";
125+ item->setHeight(m_rowHeight);
126+ }
127+
128+ if (m_visibleItems.isEmpty()) {
129+ Q_ASSERT(modelIndex == 0);
130+ item->setY(0);
131+ item->setX(0);
132+ m_visibleItems << item;
133+ m_firstVisibleIndex = 0;
134+ } else {
135+ // modelIndex has to be either m_firstVisibleIndex - 1 or m_firstVisibleIndex + m_visibleItems.count()
136+ if (modelIndex == m_firstVisibleIndex + m_visibleItems.count()) {
137+ QQuickItem *lastItem = m_visibleItems.last();
138+ if (lastItem->x() + lastItem->width() + columnSpacing() + item->width() <= width()) {
139+ // Fits in the row
140+ item->setY(lastItem->y());
141+ item->setX(lastItem->x() + lastItem->width() + columnSpacing());
142+ } else {
143+ // Starts a new row
144+ item->setY(lastItem->y() + m_rowHeight + rowSpacing());
145+ item->setX(0);
146+ m_lastInRowIndexPosition[modelIndex - 1] = lastItem->x();
147+ }
148+ m_visibleItems << item;
149+ } else if (modelIndex == m_firstVisibleIndex - 1) {
150+ QQuickItem *firstItem = m_visibleItems.first();
151+ if (m_lastInRowIndexPosition.contains(modelIndex)) {
152+ // It is the last item of its row, so start a new one since we're going back
153+ item->setY(firstItem->y() - rowSpacing() - m_rowHeight);
154+ item->setX(m_lastInRowIndexPosition[modelIndex]);
155+ } else {
156+ item->setY(firstItem->y());
157+ item->setX(firstItem->x() - columnSpacing() - item->width());
158+ }
159+ m_firstVisibleIndex = modelIndex;
160+ m_visibleItems.prepend(item);
161+ } else {
162+ qWarning() << "HorizontalJournal::positionItem - Got unexpected modelIndex"
163+ << modelIndex << m_firstVisibleIndex << m_visibleItems.count();
164+ }
165+ }
166+}
167+
168+void HorizontalJournal::cleanupExistingItems()
169+{
170+ // Cleanup the existing items
171+ Q_FOREACH(QQuickItem *item, m_visibleItems)
172+ releaseItem(item);
173+ m_visibleItems.clear();
174+ m_lastInRowIndexPosition.clear();
175+ m_firstVisibleIndex = 0;
176+}
177+
178+void HorizontalJournal::calculateImplicitHeight()
179+{
180+ if (m_firstVisibleIndex >= 0) {
181+ const int nIndexes = m_firstVisibleIndex + m_visibleItems.count();
182+ const double bottomMostY = m_visibleItems.last()->y() + m_rowHeight;
183+ const double averageHeight = bottomMostY / nIndexes;
184+ setImplicitHeight(bottomMostY + averageHeight * (model()->rowCount() - nIndexes));
185+ } else {
186+ setImplicitHeight(0);
187+ }
188+}
189+
190+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
191+void HorizontalJournal::processModelRemoves(const QVector<QQuickChangeSet::Remove> &removes)
192+#else
193+void HorizontalJournal::processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes)
194+#endif
195+{
196+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
197+ Q_FOREACH(const QQuickChangeSet::Remove &remove, removes) {
198+#else
199+ Q_FOREACH(const QQmlChangeSet::Remove &remove, removes) {
200+#endif
201+ for (int i = remove.count - 1; i >= 0; --i) {
202+ const int indexToRemove = remove.index + i;
203+ // We only support removing from the end so
204+ // any of the last items of a column has to be indexToRemove
205+ const int lastIndex = m_firstVisibleIndex + m_visibleItems.count() - 1;
206+ if (indexToRemove == lastIndex) {
207+ releaseItem(m_visibleItems.takeLast());
208+ m_lastInRowIndexPosition.remove(indexToRemove);
209+ } else {
210+ if (indexToRemove < lastIndex) {
211+ qFatal("HorizontalJournal only supports removal from the end of the model");
212+ } else {
213+ setImplicitHeightDirty();
214+ }
215+ }
216+ }
217+ }
218+ if (m_visibleItems.isEmpty()) {
219+ m_firstVisibleIndex = -1;
220+ }
221+}
222+
223+
224+void HorizontalJournal::doRelayout()
225+{
226+ // If m_firstVisibleIndex is not 0 we need to drop all the delegates
227+ // since we can't consistently relayout without the first item being there
228+
229+ if (m_firstVisibleIndex == 0) {
230+ int i = 0;
231+ const QList<QQuickItem*> allItems = m_visibleItems;
232+ m_visibleItems.clear();
233+ m_lastInRowIndexPosition.clear();
234+ Q_FOREACH(QQuickItem *item, allItems) {
235+ positionItem(i, item);
236+ ++i;
237+ }
238+ } else {
239+ Q_FOREACH(QQuickItem *item, m_visibleItems) {
240+ releaseItem(item);
241+ }
242+ m_visibleItems.clear();
243+ m_lastInRowIndexPosition.clear();
244+ m_firstVisibleIndex = 0;
245+ }
246+}
247+
248+void HorizontalJournal::updateItemCulling(qreal visibleFromY, qreal visibleToY)
249+{
250+ Q_FOREACH(QQuickItem *item, m_visibleItems) {
251+ QQuickItemPrivate::get(item)->setCulled(item->y() + m_rowHeight <= visibleFromY || item->y() >= visibleToY);
252+ }
253+}
plugins/DashViews/horizontaljournal.hnew file+67
@@ -0,0 +1,67 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef HORIZONTALJOURNAL_H
18+#define HORIZONTALJOURNAL_H
19+
20+#include "abstractjournal.h"
21+
22+ /** A horizontal journal is a view that creates delegates
23+ * based on a model and layouts them one after the other
24+ * in the same row until there is no more free space for the next item
25+ * and so that item is layouted in the next row
26+ *
27+ * All delegates are forced to rowHeight if they don't have it.
28+ */
29+ class HorizontalJournal : public AbstractJournal
30+{
31+ Q_OBJECT
32+
33+ Q_PROPERTY(qreal rowHeight READ rowHeight WRITE setRowHeight NOTIFY rowHeightChanged)
34+
35+friend class HorizontalJournalTest;
36+
37+public:
38+ HorizontalJournal();
39+
40+ qreal rowHeight() const;
41+ void setRowHeight(qreal rowHeight);
42+
43+Q_SIGNALS:
44+ void rowHeightChanged();
45+
46+private:
47+ void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) override;
48+ void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) override;
49+ bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) override;
50+ void positionItem(int modelIndex, QQuickItem *item) override;
51+ void cleanupExistingItems() override;
52+ void calculateImplicitHeight() override;
53+ void doRelayout() override;
54+ void updateItemCulling(qreal visibleFromY, qreal visibleToY) override;
55+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
56+ void processModelRemoves(const QVector<QQuickChangeSet::Remove> &removes) override;
57+#else
58+ void processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes) override;
59+#endif
60+
61+ int m_firstVisibleIndex;
62+ QList<QQuickItem*> m_visibleItems;
63+ QMap<int, double> m_lastInRowIndexPosition;
64+ int m_rowHeight;
65+};
66+
67+#endif
plugins/DashViews/plugin.cpp+2
@@ -17,6 +17,7 @@
1717
1818#include "plugin.h"
1919
20+#include "horizontaljournal.h"
2021#include "listviewwithpageheader.h"
2122#include "verticaljournal.h"
2223
@@ -26,6 +27,7 @@ void DashViewsPlugin::registerTypes(const char *uri)
2627{
2728 Q_ASSERT(uri == QLatin1String("DashViews"));
2829 qmlRegisterType<QAbstractItemModel>();
30+ qmlRegisterType<HorizontalJournal>(uri, 0, 1, "HorizontalJournal");
2931 qmlRegisterType<ListViewWithPageHeader>(uri, 0, 1, "ListViewWithPageHeader");
3032 qmlRegisterType<VerticalJournal>(uri, 0, 1, "VerticalJournal");
3133}
plugins/DashViews/verticaljournal.cpp+77−406
@@ -27,88 +27,16 @@
2727 */
2828#include "verticaljournal.h"
2929
30−#include <qqmlengine.h>
3130#pragma GCC diagnostic push
3231#pragma GCC diagnostic ignored "-pedantic"
33−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
34−#include <private/qquickvisualdatamodel_p.h>
35−#else
36−#include <private/qqmldelegatemodel_p.h>
37−#include <qqmlinfo.h>
38−#endif
3932#include <private/qquickitem_p.h>
4033#pragma GCC diagnostic pop
4134
4235static const qreal bufferRatio = 0.5;
4336
4437VerticalJournal::VerticalJournal()
45− : m_delegateModel(nullptr)
46− , m_asyncRequestedIndex(-1)
47− , m_columnWidth(0)
48− , m_columnSpacing(0)
49− , m_rowSpacing(0)
50− , m_delegateCreationBegin(0)
51− , m_delegateCreationEnd(0)
52− , m_delegateCreationBeginValid(false)
53− , m_delegateCreationEndValid(false)
54− , m_needsRelayout(false)
55− , m_delegateValidated(false)
56− , m_implicitHeightDirty(false)
57−{
58− connect(this, SIGNAL(widthChanged()), this, SLOT(relayout()));
59− connect(this, SIGNAL(heightChanged()), this, SLOT(onHeightChanged()));
60−}
61−
62−QAbstractItemModel *VerticalJournal::model() const
63−{
64− return m_delegateModel ? m_delegateModel->model().value<QAbstractItemModel *>() : nullptr;
65−}
66−
67−void VerticalJournal::setModel(QAbstractItemModel *model)
68−{
69− if (model != this->model()) {
70− if (!m_delegateModel) {
71− createDelegateModel();
72− } else {
73−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
74− disconnect(m_delegateModel, SIGNAL(modelUpdated(QQuickChangeSet,bool)), this, SLOT(onModelUpdated(QQuickChangeSet,bool)));
75− }
76− m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
77− connect(m_delegateModel, SIGNAL(modelUpdated(QQuickChangeSet,bool)), this, SLOT(onModelUpdated(QQuickChangeSet,bool)));
78−#else
79− disconnect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(onModelUpdated(QQmlChangeSet,bool)));
80− }
81− m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
82− connect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(onModelUpdated(QQmlChangeSet,bool)));
83−#endif
84−
85− cleanupExistingItems();
86−
87− Q_EMIT modelChanged();
88− polish();
89− }
90−}
91−
92−QQmlComponent *VerticalJournal::delegate() const
93−{
94− return m_delegateModel ? m_delegateModel->delegate() : nullptr;
95−}
96−
97−void VerticalJournal::setDelegate(QQmlComponent *delegate)
38+ : m_columnWidth(0)
9839{
99− if (delegate != this->delegate()) {
100− if (!m_delegateModel) {
101− createDelegateModel();
102− }
103−
104− cleanupExistingItems();
105−
106− m_delegateModel->setDelegate(delegate);
107−
108− Q_EMIT delegateChanged();
109− m_delegateValidated = false;
110− polish();
111− }
11240}
11341
11442qreal VerticalJournal::columnWidth() const
@@ -133,133 +61,6 @@ void VerticalJournal::setColumnWidth(qreal columnWidth)
13361 }
13462}
13563
136−qreal VerticalJournal::columnSpacing() const
137−{
138− return m_columnSpacing;
139−}
140−
141−void VerticalJournal::setColumnSpacing(qreal columnSpacing)
142−{
143− if (columnSpacing != m_columnSpacing) {
144− m_columnSpacing = columnSpacing;
145− Q_EMIT columnSpacingChanged();
146−
147− if (isComponentComplete()) {
148− relayout();
149− }
150− }
151−}
152−
153−qreal VerticalJournal::rowSpacing() const
154−{
155− return m_rowSpacing;
156−}
157−
158−void VerticalJournal::setRowSpacing(qreal rowSpacing)
159−{
160− if (rowSpacing != m_rowSpacing) {
161− m_rowSpacing = rowSpacing;
162− Q_EMIT rowSpacingChanged();
163−
164− if (isComponentComplete()) {
165− relayout();
166− }
167− }
168−}
169−
170−qreal VerticalJournal::delegateCreationBegin() const
171−{
172− return m_delegateCreationBegin;
173−}
174−
175−void VerticalJournal::setDelegateCreationBegin(qreal begin)
176−{
177− m_delegateCreationBeginValid = true;
178− if (m_delegateCreationBegin == begin)
179− return;
180− m_delegateCreationBegin = begin;
181− if (isComponentComplete()) {
182− polish();
183− }
184− emit delegateCreationBeginChanged();
185−}
186−
187−void VerticalJournal::resetDelegateCreationBegin()
188−{
189− m_delegateCreationBeginValid = false;
190− if (m_delegateCreationBegin == 0)
191− return;
192− m_delegateCreationBegin = 0;
193− if (isComponentComplete()) {
194− polish();
195− }
196− emit delegateCreationBeginChanged();
197−}
198−
199−qreal VerticalJournal::delegateCreationEnd() const
200−{
201− return m_delegateCreationEnd;
202−}
203−
204−void VerticalJournal::setDelegateCreationEnd(qreal end)
205−{
206− m_delegateCreationEndValid = true;
207− if (m_delegateCreationEnd == end)
208− return;
209− m_delegateCreationEnd = end;
210− if (isComponentComplete()) {
211− polish();
212− }
213− emit delegateCreationEndChanged();
214−}
215−
216−void VerticalJournal::resetDelegateCreationEnd()
217−{
218− m_delegateCreationEndValid = false;
219− if (m_delegateCreationEnd == 0)
220− return;
221− m_delegateCreationEnd = 0;
222− if (isComponentComplete()) {
223− polish();
224− }
225− emit delegateCreationEndChanged();
226−}
227−
228−void VerticalJournal::createDelegateModel()
229−{
230−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
231− m_delegateModel = new QQuickVisualDataModel(qmlContext(this), this);
232− connect(m_delegateModel, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(itemCreated(int,QQuickItem*)));
233−#else
234− m_delegateModel = new QQmlDelegateModel(qmlContext(this), this);
235− connect(m_delegateModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(itemCreated(int,QObject*)));
236−#endif
237− if (isComponentComplete())
238− m_delegateModel->componentComplete();
239−}
240−
241−void VerticalJournal::refill()
242−{
243− if (!isComponentComplete()) {
244− return;
245− }
246−
247− const bool delegateRangesValid = m_delegateCreationBeginValid && m_delegateCreationEndValid;
248− const qreal from = delegateRangesValid ? m_delegateCreationBegin : 0;
249− const qreal to = delegateRangesValid ? m_delegateCreationEnd : from + height();
250− const qreal buffer = (to - from) * bufferRatio;
251− const qreal bufferFrom = from - buffer;
252− const qreal bufferTo = to + buffer;
253−
254− bool added = addVisibleItems(from, to, false);
255− bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
256− added |= addVisibleItems(bufferFrom, bufferTo, true);
257−
258− if (added || removed) {
259− m_implicitHeightDirty = true;
260− }
261−}
262−
26364void VerticalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
26465{
26566 *modelIndex = 0;
@@ -268,7 +69,7 @@ void VerticalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
26869 Q_FOREACH(const auto &column, m_columnVisibleItems) {
26970 if (!column.isEmpty()) {
27071 const ViewItem &item = column.last();
271− *yPos = qMin(*yPos, item.y() + item.height() + m_rowSpacing);
72+ *yPos = qMin(*yPos, item.y() + item.height() + rowSpacing());
27273 *modelIndex = qMax(*modelIndex, item.m_modelIndex + 1);
27374 } else {
27475 *yPos = 0;
@@ -287,7 +88,7 @@ void VerticalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
28788 const auto &column = m_columnVisibleItems[i];
28889 if (!column.isEmpty()) {
28990 const ViewItem &item = column.first();
290− const auto itemTopPos = item.y() - m_rowSpacing;
91+ const auto itemTopPos = item.y() - rowSpacing();
29192 if (itemTopPos > *yPos) {
29293 *yPos = itemTopPos;
29394 *modelIndex = item.m_modelIndex - 1;
@@ -309,51 +110,19 @@ void VerticalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
309110 }
310111}
311112
312−bool VerticalJournal::addVisibleItems(qreal fillFrom, qreal fillTo, bool asynchronous)
313−{
314− if (!delegate())
315− return false;
316−
317− if (m_delegateModel->count() == 0)
318− return false;
319−
320− int modelIndex;
321− qreal yPos;
322− findBottomModelIndexToAdd(&modelIndex, &yPos);
323− bool changed = false;
324− while (modelIndex < m_delegateModel->count() && yPos <= fillTo) {
325− if (!createItem(modelIndex, asynchronous))
326− break;
327−
328− changed = true;
329− findBottomModelIndexToAdd(&modelIndex, &yPos);
330− }
331−
332− findTopModelIndexToAdd(&modelIndex, &yPos);
333− while (modelIndex >= 0 && yPos > fillFrom) {
334− if (!createItem(modelIndex, asynchronous))
335− break;
336−
337− changed = true;
338− findTopModelIndexToAdd(&modelIndex, &yPos);
339− }
340−
341− return changed;
342−}
343−
344−bool VerticalJournal::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
113+bool VerticalJournal::removeNonVisibleItems(qreal bufferFromY, qreal bufferToY)
345114{
346115 bool changed = false;
347116
348117 for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
349118 QList<ViewItem> &column = m_columnVisibleItems[i];
350− while (!column.isEmpty() && column.first().y() + column.first().height() < bufferFrom) {
351− releaseItem(column.takeFirst());
119+ while (!column.isEmpty() && column.first().y() + column.first().height() < bufferFromY) {
120+ releaseItem(column.takeFirst().m_item);
352121 changed = true;
353122 }
354123
355− while (!column.isEmpty() && column.last().y() > bufferTo) {
356− releaseItem(column.takeLast());
124+ while (!column.isEmpty() && column.last().y() > bufferToY) {
125+ releaseItem(column.takeLast().m_item);
357126 changed = true;
358127 }
359128 }
@@ -361,53 +130,20 @@ bool VerticalJournal::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
361130 return changed;
362131}
363132
364−QQuickItem *VerticalJournal::createItem(int modelIndex, bool asynchronous)
133+void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
365134{
366− if (asynchronous && m_asyncRequestedIndex != -1)
367− return nullptr;
368−
369− m_asyncRequestedIndex = -1;
370−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
371− QQuickItem *item = m_delegateModel->item(modelIndex, asynchronous);
372−#else
373− QObject* object = m_delegateModel->object(modelIndex, asynchronous);
374− QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
375−#endif
376− if (!item) {
377−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
378− m_asyncRequestedIndex = modelIndex;
379−#else
380− if (object) {
381− m_delegateModel->release(object);
382− if (!m_delegateValidated) {
383− m_delegateValidated = true;
384− QObject* delegateObj = delegate();
385− qmlInfo(delegateObj ? delegateObj : this) << "Delegate must be of Item type";
386− }
387− } else {
388− m_asyncRequestedIndex = modelIndex;
389− }
390−#endif
391− return nullptr;
392− } else {
393− if (item->width() != m_columnWidth) {
394− qWarning() << "Item" << modelIndex << "width is not the one that the columnWidth mandates, resetting it";
395− item->setWidth(m_columnWidth);
396− }
397− positionItem(modelIndex, item);
398− return item;
135+ if (item->width() != m_columnWidth) {
136+ qWarning() << "Item" << modelIndex << "width is not the one that the columnWidth mandates, resetting it";
137+ item->setWidth(m_columnWidth);
399138 }
400−}
401139
402−void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
403−{
404140 // Check if we add it to the bottom of existing column items
405141 const QList<ViewItem> &firstColumn = m_columnVisibleItems[0];
406− qreal columnToAddY = !firstColumn.isEmpty() ? firstColumn.last().y() + firstColumn.last().height() : -m_rowSpacing;
142+ qreal columnToAddY = !firstColumn.isEmpty() ? firstColumn.last().y() + firstColumn.last().height() : -rowSpacing();
407143 int columnToAddTo = 0;
408144 for (int i = 1; i < m_columnVisibleItems.count(); ++i) {
409145 const QList<ViewItem> &column = m_columnVisibleItems[i];
410− const qreal iY = !column.isEmpty() ? column.last().y() + column.last().height() : -m_rowSpacing;
146+ const qreal iY = !column.isEmpty() ? column.last().y() + column.last().height() : -rowSpacing();
411147 if (iY < columnToAddY) {
412148 columnToAddTo = i;
413149 columnToAddY = iY;
@@ -416,8 +152,8 @@ void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
416152
417153 const QList<ViewItem> &columnToAdd = m_columnVisibleItems[columnToAddTo];
418154 if (columnToAdd.isEmpty() || columnToAdd.last().m_modelIndex < modelIndex) {
419− item->setX(columnToAddTo * (m_columnWidth + m_columnSpacing));
420− item->setY(columnToAddY + m_rowSpacing);
155+ item->setX(columnToAddTo * (m_columnWidth + columnSpacing()));
156+ item->setY(columnToAddY + rowSpacing());
421157
422158 m_columnVisibleItems[columnToAddTo] << ViewItem(item, modelIndex);
423159 m_indexColumnMap[modelIndex] = columnToAddTo;
@@ -426,8 +162,8 @@ void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
426162 columnToAddTo = m_indexColumnMap[modelIndex];
427163 columnToAddY = m_columnVisibleItems[columnToAddTo].first().y();
428164
429− item->setX(columnToAddTo * (m_columnWidth + m_columnSpacing));
430− item->setY(columnToAddY - m_rowSpacing - item->height());
165+ item->setX(columnToAddTo * (m_columnWidth + columnSpacing()));
166+ item->setY(columnToAddY - rowSpacing() - item->height());
431167
432168 m_columnVisibleItems[columnToAddTo].prepend(ViewItem(item, modelIndex));
433169 }
@@ -439,29 +175,14 @@ void VerticalJournal::cleanupExistingItems()
439175 for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
440176 QList<ViewItem> &column = m_columnVisibleItems[i];
441177 Q_FOREACH(const ViewItem &item, column)
442− releaseItem(item);
178+ releaseItem(item.m_item);
443179 column.clear();
444180 }
445181 m_indexColumnMap.clear();
446182}
447183
448−void VerticalJournal::releaseItem(const ViewItem &item)
449−{
450−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
451− QQuickVisualModel::ReleaseFlags flags = m_delegateModel->release(item.m_item);
452− if (flags & QQuickVisualModel::Destroyed) {
453−#else
454− QQmlDelegateModel::ReleaseFlags flags = m_delegateModel->release(item.m_item);
455− if (flags & QQmlDelegateModel::Destroyed) {
456−#endif
457− item.m_item->setParentItem(nullptr);
458− }
459−}
460−
461184void VerticalJournal::calculateImplicitHeight()
462185{
463− m_implicitHeightDirty = false;
464−
465186 int lastModelIndex = -1;
466187 qreal bottomMostY = 0;
467188 Q_FOREACH(const auto &column, m_columnVisibleItems) {
@@ -479,131 +200,81 @@ void VerticalJournal::calculateImplicitHeight()
479200 }
480201}
481202
482−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
483−void VerticalJournal::itemCreated(int modelIndex, QQuickItem *item)
484−{
485−#else
486−void VerticalJournal::itemCreated(int modelIndex, QObject *object)
203+void VerticalJournal::doRelayout()
487204{
488− QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
489− if (!item) {
490− qWarning() << "VerticalJournal::itemCreated got a non item for index" << modelIndex;
491− return;
205+ QList<ViewItem> allItems;
206+ Q_FOREACH(const auto &column, m_columnVisibleItems)
207+ allItems << column;
208+
209+ qSort(allItems);
210+
211+ const int nColumns = qMax(1., floor((double)(width() + columnSpacing()) / (m_columnWidth + columnSpacing())));
212+ m_columnVisibleItems.resize(nColumns);
213+ m_indexColumnMap.clear();
214+ for (int i = 0; i < nColumns; ++i)
215+ m_columnVisibleItems[i].clear();
216+
217+ // If the first of allItems doesn't contain index 0 we need to drop them
218+ // all since we can't consistently relayout without the first item being there
219+
220+ if (!allItems.isEmpty()) {
221+ if (allItems.first().m_modelIndex == 0) {
222+ Q_FOREACH(const ViewItem &item, allItems)
223+ positionItem(item.m_modelIndex, item.m_item);
224+ } else {
225+ Q_FOREACH(const ViewItem &item, allItems)
226+ releaseItem(item.m_item);
227+ }
492228 }
493−#endif
494− item->setParentItem(this);
495− if (modelIndex == m_asyncRequestedIndex) {
496− createItem(modelIndex, false);
497− m_implicitHeightDirty = true;
498− polish();
229+}
230+
231+void VerticalJournal::updateItemCulling(qreal visibleFromY, qreal visibleToY)
232+{
233+ Q_FOREACH(const auto &column, m_columnVisibleItems) {
234+ Q_FOREACH(const ViewItem &item, column) {
235+ const bool cull = item.y() + item.height() <= visibleFromY || item.y() >= visibleToY;
236+ QQuickItemPrivate::get(item.m_item)->setCulled(cull);
237+ }
499238 }
500239}
501240
502241#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
503−void VerticalJournal::onModelUpdated(const QQuickChangeSet &changeSet, bool reset)
242+void VerticalJournal::processModelRemoves(const QVector<QQuickChangeSet::Remove> &removes)
504243#else
505−void VerticalJournal::onModelUpdated(const QQmlChangeSet &changeSet, bool reset)
244+void VerticalJournal::processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes)
506245#endif
507246{
508− if (reset) {
509− cleanupExistingItems();
510− } else {
511247#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
512− Q_FOREACH(const QQuickChangeSet::Remove &remove, changeSet.removes()) {
248+ Q_FOREACH(const QQuickChangeSet::Remove &remove, removes) {
513249#else
514− Q_FOREACH(const QQmlChangeSet::Remove &remove, changeSet.removes()) {
250+ Q_FOREACH(const QQmlChangeSet::Remove &remove, removes) {
515251#endif
516− for (int i = remove.count - 1; i >= 0; --i) {
517− const int indexToRemove = remove.index + i;
518− // Since we only support removing from the end, indexToRemove
519− // must refer to the last item of one of the columns.
520− bool found = false;
521− for (int i = 0; !found && i < m_columnVisibleItems.count(); ++i) {
522− QList<ViewItem> &column = m_columnVisibleItems[i];
523− if (!column.isEmpty() && column.last().m_modelIndex == indexToRemove) {
524− releaseItem(column.takeLast());
252+ for (int i = remove.count - 1; i >= 0; --i) {
253+ const int indexToRemove = remove.index + i;
254+ // Since we only support removing from the end, indexToRemove
255+ // must refer to the last item of one of the columns or
256+ // be bigger than them (because it's not in the viewport and
257+ // thus we have not created a delegate for it)
258+ bool found = false;
259+ int lastCreatedIndex = INT_MIN;
260+ for (int i = 0; !found && i < m_columnVisibleItems.count(); ++i) {
261+ QList<ViewItem> &column = m_columnVisibleItems[i];
262+ if (!column.isEmpty()) {
263+ const int lastColumnIndex = column.last().m_modelIndex;
264+ if (lastColumnIndex == indexToRemove) {
265+ releaseItem(column.takeLast().m_item);
525266 found = true;
526267 }
268+ lastCreatedIndex = qMax(lastCreatedIndex, lastColumnIndex);
527269 }
528− Q_ASSERT(found);
529270 }
530− }
531− }
532− polish();
533−}
534−
535−
536−void VerticalJournal::relayout()
537−{
538− m_needsRelayout = true;
539− polish();
540−}
541−
542−void VerticalJournal::onHeightChanged()
543−{
544− polish();
545−}
546−
547−void VerticalJournal::updatePolish()
548−{
549− if (!model())
550− return;
551−
552− if (m_needsRelayout) {
553− m_implicitHeightDirty = true;
554−
555− QList<ViewItem> allItems;
556− Q_FOREACH(const auto &column, m_columnVisibleItems)
557− allItems << column;
558−
559− qSort(allItems);
560−
561− const int nColumns = qMax(1., floor((double)(width() + m_columnSpacing) / (m_columnWidth + m_columnSpacing)));
562− m_columnVisibleItems.resize(nColumns);
563− m_indexColumnMap.clear();
564− for (int i = 0; i < nColumns; ++i)
565− m_columnVisibleItems[i].clear();
566−
567− // If the first of allItems doesn't contain index 0 we need to drop them
568− // all since we can't consistently relayout without the first item being there
569−
570− if (!allItems.isEmpty()) {
571− if (allItems.first().m_modelIndex == 0) {
572− Q_FOREACH(const ViewItem &item, allItems)
573− positionItem(item.m_modelIndex, item.m_item);
574− } else {
575− Q_FOREACH(const ViewItem &item, allItems)
576− releaseItem(item);
271+ if (!found) {
272+ if (indexToRemove < lastCreatedIndex) {
273+ qFatal("VerticalJournal only supports removal from the end of the model");
274+ } else {
275+ setImplicitHeightDirty();
276+ }
577277 }
578278 }
579−
580− m_needsRelayout = false;
581− }
582−
583− refill();
584−
585− const bool delegateRangesValid = m_delegateCreationBeginValid && m_delegateCreationEndValid;
586− const qreal from = delegateRangesValid ? m_delegateCreationBegin : 0;
587− const qreal to = delegateRangesValid ? m_delegateCreationEnd : from + height();
588− Q_FOREACH(const auto &column, m_columnVisibleItems) {
589− Q_FOREACH(const ViewItem &item, column) {
590− QQuickItemPrivate::get(item.m_item)->setCulled(item.y() + item.height() <= from || item.y() >= to);
591− }
592279 }
593−
594− if (m_implicitHeightDirty) {
595− calculateImplicitHeight();
596− }
597−}
598−
599−void VerticalJournal::componentComplete()
600−{
601− if (m_delegateModel)
602− m_delegateModel->componentComplete();
603−
604− QQuickItem::componentComplete();
605−
606− m_needsRelayout = true;
607−
608− polish();
609280}
plugins/DashViews/verticaljournal.h+13−91
@@ -17,17 +17,7 @@
1717#ifndef VERTICALJOURNAL_H
1818#define VERTICALJOURNAL_H
1919
20−#include <QQuickItem>
21−
22−class QAbstractItemModel;
23−class QQmlComponent;
24−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
25−class QQuickChangeSet;
26−class QQuickVisualDataModel;
27−#else
28−class QQmlChangeSet;
29−class QQmlDelegateModel;
30−#endif
20+#include "abstractjournal.h"
3121
3222 /** A vertical journal is a view that creates delegates
3323 * based on a model and layouts them in columns following
@@ -60,75 +50,23 @@ class QQmlDelegateModel;
6050 * +-----+
6151 *
6252 */
63− class VerticalJournal : public QQuickItem
53+
54+class VerticalJournal : public AbstractJournal
6455{
6556 Q_OBJECT
6657
67− Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged)
68− Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
6958 Q_PROPERTY(qreal columnWidth READ columnWidth WRITE setColumnWidth NOTIFY columnWidthChanged)
70− Q_PROPERTY(qreal columnSpacing READ columnSpacing WRITE setColumnSpacing NOTIFY columnSpacingChanged)
71− Q_PROPERTY(qreal rowSpacing READ rowSpacing WRITE setRowSpacing NOTIFY rowSpacingChanged)
72− Q_PROPERTY(qreal delegateCreationBegin READ delegateCreationBegin
73− WRITE setDelegateCreationBegin
74− NOTIFY delegateCreationBeginChanged
75− RESET resetDelegateCreationBegin)
76− Q_PROPERTY(qreal delegateCreationEnd READ delegateCreationEnd
77− WRITE setDelegateCreationEnd
78− NOTIFY delegateCreationEndChanged
79− RESET resetDelegateCreationEnd)
8059
8160friend class VerticalJournalTest;
8261
8362public:
8463 VerticalJournal();
8564
86− QAbstractItemModel *model() const;
87− void setModel(QAbstractItemModel *model);
88−
89− QQmlComponent *delegate() const;
90− void setDelegate(QQmlComponent *delegate);
91−
9265 qreal columnWidth() const;
9366 void setColumnWidth(qreal columnWidth);
9467
95− qreal columnSpacing() const;
96− void setColumnSpacing(qreal columnSpacing);
97−
98− qreal rowSpacing() const;
99− void setRowSpacing(qreal rowSpacing);
100−
101− qreal delegateCreationBegin() const;
102− void setDelegateCreationBegin(qreal);
103− void resetDelegateCreationBegin();
104−
105− qreal delegateCreationEnd() const;
106− void setDelegateCreationEnd(qreal);
107− void resetDelegateCreationEnd();
108−
10968Q_SIGNALS:
110− void modelChanged();
111− void delegateChanged();
11269 void columnWidthChanged();
113− void columnSpacingChanged();
114− void rowSpacingChanged();
115− void delegateCreationBeginChanged();
116− void delegateCreationEndChanged();
117−
118−protected:
119− void updatePolish();
120− void componentComplete();
121−
122−private Q_SLOTS:
123−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
124− void itemCreated(int modelIndex, QQuickItem *item);
125− void onModelUpdated(const QQuickChangeSet &changeSet, bool reset);
126−#else
127− void itemCreated(int modelIndex, QObject *object);
128− void onModelUpdated(const QQmlChangeSet &changeSet, bool reset);
129−#endif
130− void relayout();
131− void onHeightChanged();
13270
13371private:
13472 class ViewItem
@@ -144,39 +82,23 @@ private:
14482 int m_modelIndex;
14583 };
14684
147− void createDelegateModel();
148− void refill();
149− void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos);
150− void findTopModelIndexToAdd(int *modelIndex, qreal *yPos);
151− bool addVisibleItems(qreal fillFrom, qreal fillTo, bool asynchronous);
152− bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
153− QQuickItem *createItem(int modelIndex, bool asynchronous);
154− void positionItem(int modelIndex, QQuickItem *item);
155− void cleanupExistingItems();
156− void releaseItem(const ViewItem &item);
157− void calculateImplicitHeight();
158−
85+ void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) override;
86+ void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) override;
87+ bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) override;
88+ void positionItem(int modelIndex, QQuickItem *item) override;
89+ void cleanupExistingItems() override;
90+ void calculateImplicitHeight() override;
91+ void doRelayout() override;
92+ void updateItemCulling(qreal visibleFromY, qreal visibleToY) override;
15993#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
160− QQuickVisualDataModel *m_delegateModel;
94+ void processModelRemoves(const QVector<QQuickChangeSet::Remove> &removes) override;
16195#else
162− QQmlDelegateModel *m_delegateModel;
96+ void processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes) override;
16397#endif
16498
165− // Index we are waiting because we requested it asynchronously
166− int m_asyncRequestedIndex;
167−
16899 QVector<QList<ViewItem>> m_columnVisibleItems;
169100 QHash<int, int> m_indexColumnMap;
170101 int m_columnWidth;
171− int m_columnSpacing;
172− int m_rowSpacing;
173− qreal m_delegateCreationBegin;
174− qreal m_delegateCreationEnd;
175− bool m_delegateCreationBeginValid;
176− bool m_delegateCreationEndValid;
177− bool m_needsRelayout;
178− bool m_delegateValidated;
179− bool m_implicitHeightDirty;
180102};
181103
182104#endif
plugins/Utils/qsortfilterproxymodelqml.cpp+12
@@ -68,6 +68,18 @@ QSortFilterProxyModelQML::setModel(QAbstractItemModel *itemModel)
6868 }
6969}
7070
71+QVariantMap
72+QSortFilterProxyModelQML::get(int row)
73+{
74+ QVariantMap res;
75+ const QHash<int, QByteArray> roles = roleNames();
76+ auto it = roles.begin();
77+ for ( ; it != roles.end(); ++it) {
78+ res[*it] = data(row, it.key());
79+ }
80+ return res;
81+}
82+
7183QVariant
7284QSortFilterProxyModelQML::data(int row, int role)
7385{
plugins/Utils/qsortfilterproxymodelqml.h+1
@@ -31,6 +31,7 @@ class QSortFilterProxyModelQML : public QSortFilterProxyModel
3131public:
3232 explicit QSortFilterProxyModelQML(QObject *parent = 0);
3333
34+ Q_INVOKABLE QVariantMap get(int row); // Use with caution, it can be slow to query all the roles
3435 Q_INVOKABLE QVariant data(int row, int role);
3536 Q_INVOKABLE int count();
3637 Q_INVOKABLE int findFirst(int role, const QVariant& value) const;
po/ar.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
1818"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n"
19−"X-Launchpad-Export-Date: 2013-12-06 06:30+0000\n"
20−"X-Generator: Launchpad (build 16863)\n"
19+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
20+"X-Generator: Launchpad (build 16877)\n"
2121
2222#: Components/EdgeDemoOverlay.qml:151
2323msgid "Skip intro"
po/ast.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-12-12 06:02+0000\n"
19−"X-Generator: Launchpad (build 16869)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/az.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/bg.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Type: text/plain; charset=UTF-8\n"
1717"Content-Transfer-Encoding: 8bit\n"
1818"Plural-Forms: nplurals=2; plural=n != 1;\n"
19−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
20−"X-Generator: Launchpad (build 16856)\n"
19+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
20+"X-Generator: Launchpad (build 16877)\n"
2121
2222#: Components/EdgeDemoOverlay.qml:151
2323msgid "Skip intro"
po/ca.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/cs.po+9−7
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11−"PO-Revision-Date: 2013-10-08 08:09+0000\n"
11+"PO-Revision-Date: 2014-01-08 10:11+0000\n"
1212"Last-Translator: Tadeáš Pařík <tadeas.parik@gmail.com>\n"
1313"Language-Team: Czech <cs@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2014-01-09 05:41+0000\n"
19+"X-Generator: Launchpad (build 16884)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
@@ -44,7 +44,7 @@ msgstr "Uzavřít"
4444
4545#: Components/EdgeDemo.qml:171
4646msgid "Swipe up again to close the settings screen"
47−msgstr ""
47+msgstr "Přetažením nahoru dojde k uzavření obrazovky nastavení"
4848
4949#: Components/EdgeDemo.qml:198
5050msgid "Left edge"
@@ -63,6 +63,8 @@ msgid ""
6363"You have now mastered the edge gestures and can start using the "
6464"phone<br><br>Tap anywhere to start"
6565msgstr ""
66+"Nyní jste si osvojili gesta potřebná k ovládání telefonu, můžete ho začít "
67+"používat <br><br>Klepnutím kdekoli začnete"
6668
6769#: Components/PageHeader.qml:364
6870msgid "Recent searches"
@@ -138,7 +140,7 @@ msgstr "Mluvte..."
138140
139141#: Notifications/NotificationMenuItemFactory.qml:89
140142msgid "Show password"
141−msgstr ""
143+msgstr "Zobrazit heslo"
142144
143145#: Panel/SearchIndicator.qml:27
144146msgid "Search"
@@ -147,11 +149,11 @@ msgstr "Hledat"
147149#: plugins/Unity/Launcher/launcheritem.cpp:43
148150#: plugins/Unity/Launcher/launcheritem.cpp:73
149151msgid "Lock to Launcher"
150−msgstr ""
152+msgstr "Přidat do Launcheru"
151153
152154#: plugins/Unity/Launcher/launcheritem.cpp:73
153155msgid "Unlock from Launcher"
154−msgstr ""
156+msgstr "Odebrat z Launcheru"
155157
156158#: Shell.qml:439
157159#, qt-format
po/de.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/el.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/en_AU.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/en_GB.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/eo.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/es.po+3−3
@@ -9,14 +9,14 @@ msgstr ""
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-11-29 09:21+0100\n"
1111"PO-Revision-Date: 2013-11-20 08:46+0000\n"
12−"Last-Translator: Adolfo Jayme Barrientos <fitoschido@gmail.com>\n"
12+"Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
1313"Language-Team: Spanish <es@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/fa.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/fi.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/fo.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/fr.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/gd.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
1818"(n > 2 && n < 20) ? 2 : 3;\n"
19−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
20−"X-Generator: Launchpad (build 16856)\n"
19+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
20+"X-Generator: Launchpad (build 16877)\n"
2121
2222#: Components/EdgeDemoOverlay.qml:151
2323msgid "Skip intro"
po/gl.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-12-07 06:35+0000\n"
19−"X-Generator: Launchpad (build 16869)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/he.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/hr.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
20−"X-Generator: Launchpad (build 16856)\n"
19+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
20+"X-Generator: Launchpad (build 16877)\n"
2121
2222#: Components/EdgeDemoOverlay.qml:151
2323msgid "Skip intro"
po/hu.po+32−28
@@ -8,61 +8,65 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11−"PO-Revision-Date: 2013-06-27 04:28+0000\n"
12−"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
11+"PO-Revision-Date: 2014-01-07 18:02+0000\n"
12+"Last-Translator: Gabor Kelemen <kelemeng@gnome.hu>\n"
1313"Language-Team: Hungarian <hu@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2014-01-08 05:43+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
23−msgstr ""
23+msgstr "A bemutató kihagyása"
2424
2525#: Components/EdgeDemo.qml:112
2626msgid "Right edge"
27−msgstr ""
27+msgstr "Jobb szél"
2828
2929#: Components/EdgeDemo.qml:113
3030msgid "Try swiping from the right edge to unlock the phone"
31−msgstr ""
31+msgstr "A telefon feloldásához csúsztassa ujját a jobb szélről"
3232
3333#: Components/EdgeDemo.qml:144
3434msgid "Top edge"
35−msgstr ""
35+msgstr "Felső szél"
3636
3737#: Components/EdgeDemo.qml:145
3838msgid "Try swiping from the top edge to access the indicators"
39−msgstr ""
39+msgstr "A jelzők eléréséhez csúsztassa ujját a felső szélről"
4040
4141#: Components/EdgeDemo.qml:170
4242msgid "Close"
43−msgstr ""
43+msgstr "Bezárás"
4444
4545#: Components/EdgeDemo.qml:171
4646msgid "Swipe up again to close the settings screen"
47−msgstr ""
47+msgstr "A beállítások bezárásához csúsztassa ujját újra felfelé"
4848
4949#: Components/EdgeDemo.qml:198
5050msgid "Left edge"
51−msgstr ""
51+msgstr "Bal szél"
5252
5353#: Components/EdgeDemo.qml:199
5454msgid "Swipe from the left to reveal the launcher for quick access to apps"
5555msgstr ""
56+"Csúsztassa ujját a bal szélről az indítón található alkalmazások gyors "
57+"eléréséhez"
5658
5759#: Components/EdgeDemo.qml:226
5860msgid "Well done"
59−msgstr ""
61+msgstr "Rendben"
6062
6163#: Components/EdgeDemo.qml:227
6264msgid ""
6365"You have now mastered the edge gestures and can start using the "
6466"phone<br><br>Tap anywhere to start"
6567msgstr ""
68+"Miután megismerte a szélek használatát, nincs más hátra...<br><br>Koppintson "
69+"a folytatáshoz!"
6670
6771#: Components/PageHeader.qml:364
6872msgid "Recent searches"
@@ -70,50 +74,50 @@ msgstr "Legutóbbi keresések"
7074
7175#: Dash/Apps/AppPreview.qml:135
7276msgid "Rate this"
73−msgstr ""
77+msgstr "Osztályzás"
7478
7579#. TRANSLATORS: Number of persons who rated this app/video/whatever
7680#: Dash/Previews/Header.qml:93
7781#, qt-format
7882msgid "(%1)"
79−msgstr ""
83+msgstr "(%1)"
8084
8185#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
8286#: Dash/Previews/Header.qml:105
8387#, qt-format
8488msgid "%1 review"
8589msgid_plural "%1 reviews"
86−msgstr[0] ""
87−msgstr[1] ""
90+msgstr[0] "%1 értékelés"
91+msgstr[1] "%1 értékelés"
8892
8993#: Dash/Previews/Reviews.qml:74
9094msgid "Add a review"
91−msgstr ""
95+msgstr "Értékelés hozzáadása"
9296
9397#: Dash/Previews/Reviews.qml:86
9498msgid "Review"
95−msgstr ""
99+msgstr "Értékelés"
96100
97101#: Dash/Previews/Reviews.qml:121
98102msgid "Send"
99−msgstr ""
103+msgstr "Küldés"
100104
101105#: Dash/Previews/Reviews.qml:139
102106msgid "Comments:"
103−msgstr ""
107+msgstr "Hozzászólások:"
104108
105109#: Greeter/Lockscreen.qml:149
106110msgid "Emergency Call"
107−msgstr ""
111+msgstr "Vészhívás"
108112
109113#: Greeter/PassphraseLockscreen.qml:59
110114#, qt-format
111115msgid "Hello %1"
112−msgstr ""
116+msgstr "Szia %1"
113117
114118#: Greeter/PassphraseLockscreen.qml:59
115119msgid "Hello"
116−msgstr ""
120+msgstr "Szia"
117121
118122#: Hud/HudParametrizedActionsPage.qml:132
119123msgid "Confirm"
@@ -137,7 +141,7 @@ msgstr "Beszéd…"
137141
138142#: Notifications/NotificationMenuItemFactory.qml:89
139143msgid "Show password"
140−msgstr ""
144+msgstr "Jelszó megjelenítése"
141145
142146#: Panel/SearchIndicator.qml:27
143147msgid "Search"
@@ -146,13 +150,13 @@ msgstr "Keresés"
146150#: plugins/Unity/Launcher/launcheritem.cpp:43
147151#: plugins/Unity/Launcher/launcheritem.cpp:73
148152msgid "Lock to Launcher"
149−msgstr ""
153+msgstr "Indítóban tartás"
150154
151155#: plugins/Unity/Launcher/launcheritem.cpp:73
152156msgid "Unlock from Launcher"
153−msgstr ""
157+msgstr "Eltávolítás az Indítóból"
154158
155159#: Shell.qml:439
156160#, qt-format
157161msgid "Please enter %1"
158−msgstr ""
162+msgstr "Adja meg a következőt: %1"
po/hy.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/id.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/is.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/it.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/ja.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/km.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/kn.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/ko.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/ky.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/lo.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/lv.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/ml.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/my.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-12-13 06:46+0000\n"
19−"X-Generator: Launchpad (build 16869)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/nb.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/nl.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/pl.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
1818"|| n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
20−"X-Generator: Launchpad (build 16856)\n"
19+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
20+"X-Generator: Launchpad (build 16877)\n"
2121
2222#: Components/EdgeDemoOverlay.qml:151
2323msgid "Skip intro"
po/pt_BR.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/ro.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n == 1 ? 0: (((n % 100 > 19) || ((n % 100 "
1818"== 0) && (n != 0))) ? 2: 1));\n"
19−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
20−"X-Generator: Launchpad (build 16856)\n"
19+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
20+"X-Generator: Launchpad (build 16877)\n"
2121
2222#: Components/EdgeDemoOverlay.qml:151
2323msgid "Skip intro"
po/ru.po+9−8
@@ -8,16 +8,16 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11−"PO-Revision-Date: 2013-10-01 05:55+0000\n"
12−"Last-Translator: Zonov Roman <Unknown>\n"
11+"PO-Revision-Date: 2014-01-07 05:21+0000\n"
12+"Last-Translator: Ivlev Denis <Unknown>\n"
1313"Language-Team: Russian <ru@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
20−"X-Generator: Launchpad (build 16856)\n"
19+"X-Launchpad-Export-Date: 2014-01-08 05:43+0000\n"
20+"X-Generator: Launchpad (build 16877)\n"
2121
2222#: Components/EdgeDemoOverlay.qml:151
2323msgid "Skip intro"
@@ -89,7 +89,8 @@ msgstr "(%1)"
8989#, qt-format
9090msgid "%1 review"
9191msgid_plural "%1 reviews"
92−msgstr[0] "%1 отзыв"
92+msgstr[0] "%1 отзыва"
93+msgstr[1] "%1 отзывов"
9394
9495#: Dash/Previews/Reviews.qml:74
9596msgid "Add a review"
@@ -142,7 +143,7 @@ msgstr "Говорите..."
142143
143144#: Notifications/NotificationMenuItemFactory.qml:89
144145msgid "Show password"
145−msgstr ""
146+msgstr "Показать пароль"
146147
147148#: Panel/SearchIndicator.qml:27
148149msgid "Search"
@@ -151,11 +152,11 @@ msgstr "Поиск"
151152#: plugins/Unity/Launcher/launcheritem.cpp:43
152153#: plugins/Unity/Launcher/launcheritem.cpp:73
153154msgid "Lock to Launcher"
154−msgstr ""
155+msgstr "Закрепить на панели"
155156
156157#: plugins/Unity/Launcher/launcheritem.cpp:73
157158msgid "Unlock from Launcher"
158−msgstr ""
159+msgstr "Убрать с панели"
159160
160161#: Shell.qml:439
161162#, qt-format
po/shn.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=(n == 1) ? 0 : 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/si.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/sl.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
1818"n%100==4 ? 3 : 0);\n"
19−"X-Launchpad-Export-Date: 2013-12-01 05:47+0000\n"
20−"X-Generator: Launchpad (build 16856)\n"
19+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
20+"X-Generator: Launchpad (build 16877)\n"
2121
2222#: Components/EdgeDemoOverlay.qml:151
2323msgid "Skip intro"
po/sr.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
20−"X-Generator: Launchpad (build 16856)\n"
19+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
20+"X-Generator: Launchpad (build 16877)\n"
2121
2222#: Components/EdgeDemoOverlay.qml:151
2323msgid "Skip intro"
po/sv.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/ta.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020"Language: ta\n"
2121
2222#: Components/EdgeDemoOverlay.qml:151
po/te.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/tr.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-12-13 06:46+0000\n"
19−"X-Generator: Launchpad (build 16869)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/ug.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/uk.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
20−"X-Generator: Launchpad (build 16856)\n"
19+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
20+"X-Generator: Launchpad (build 16877)\n"
2121
2222#: Components/EdgeDemoOverlay.qml:151
2323msgid "Skip intro"
po/xh.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/zh_CN.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
po/zh_TW.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
18+"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Generator: Launchpad (build 16877)\n"
2020
2121#: Components/EdgeDemoOverlay.qml:151
2222msgid "Skip intro"
qml/Components/PageHeader.qml+14−39
@@ -21,17 +21,12 @@ import Ubuntu.Components.ListItems 0.1 as ListItem
2121import Unity 0.1
2222
2323Item {
24− /*!
25− \preliminary
26− The text that is shown inside the Page Header
27− \qmlproperty string text
28− */
29− property alias text: label.text
30−
24+ id: root
3125 property bool searchEntryEnabled: false
3226 property alias searchQuery: searchField.text
3327 property ListModel searchHistory
3428 property Scope scope
29+ property alias childItem: itemContainer.children
3530
3631 height: units.gu(8.5)
3732 implicitHeight: units.gu(8.5)
@@ -82,29 +77,10 @@ Item {
8277 height: childrenRect.height
8378
8479 Item {
85− id: textContainer
80+ id: itemContainer
8681
87− width: header.width
82+ width: searchContainer.narrowMode ? header.width : header.width - searchContainer.width
8883 height: header.height
89−
90− Label {
91− id: label
92− anchors {
93− left: parent.left
94− leftMargin: units.gu(2)
95− right: parent.right
96− verticalCenter: parent.verticalCenter
97− }
98−
99− color: Theme.palette.selected.backgroundText
100− opacity: 0.8
101− font.family: "Ubuntu"
102− font.weight: Font.Light
103− fontSize: "x-large"
104− elide: Text.ElideRight
105− style: Text.Raised
106− styleColor: "black"
107− }
10884 }
10985
11086 Item {
@@ -115,12 +91,12 @@ Item {
11591 property bool popoverShouldOpen: false
11692 property bool popoverShouldClose: false
11793
118− property bool narrowMode: parent.width < label.contentWidth + units.gu(50)
94+ property bool narrowMode: parent.width < units.gu(80)
11995
12096 property bool active: searchField.text != "" || searchField.activeFocus
12197 property var popover: null
12298
123− anchors.right: textContainer.right
99+ anchors.right: headerContainer.right
124100 height: header.height
125101
126102 state:
@@ -141,9 +117,8 @@ Item {
141117
142118 function openPopover() {
143119 if (searchHistory.count > 0) {
144− searchContainer.popover = PopupUtils.open(popoverComponent, searchField,
120+ searchContainer.popover = PopupUtils.open(popoverComponent, pointerPositioner,
145121 {
146− "pointerTarget": pointerPositioner,
147122 "contentWidth": searchField.width,
148123 "edgeMargins": units.gu(1)
149124 }
@@ -256,13 +231,13 @@ Item {
256231 states: [
257232 State {
258233 name: "wide"
259− AnchorChanges { target: textContainer; anchors.top: headerContainer.top }
260− AnchorChanges { target: searchContainer; anchors.left: undefined; anchors.top: textContainer.top }
234+ AnchorChanges { target: itemContainer; anchors.top: headerContainer.top }
235+ AnchorChanges { target: searchContainer; anchors.left: undefined; anchors.top: itemContainer.top }
261236 },
262237 State {
263238 name: "narrow"
264239 PropertyChanges { target: searchField; highlighted: true }
265− AnchorChanges { target: textContainer; anchors.top: searchContainer.bottom }
240+ AnchorChanges { target: itemContainer; anchors.top: searchContainer.bottom }
266241 AnchorChanges { target: searchContainer; anchors.left: headerContainer.left; anchors.top: headerContainer.top }
267242 },
268243 State {
@@ -300,7 +275,7 @@ Item {
300275 ParallelAnimation {
301276 NumberAnimation { targets: [searchContainer, searchField]; property: "width"; duration: 200; easing.type: Easing.InOutQuad }
302277 PropertyAction { target: primaryImage; property: "source" }
303− AnchorAnimation { targets: [searchContainer, textContainer]; duration: 200; easing.type: Easing.InOutQuad }
278+ AnchorAnimation { targets: [searchContainer, itemContainer]; duration: 200; easing.type: Easing.InOutQuad }
304279 }
305280 ScriptAction { script: if (searchContainer.popoverShouldOpen) { searchContainer.openPopover(); } }
306281 }
@@ -309,14 +284,14 @@ Item {
309284 to: "inactive"
310285 ScriptAction { script: if (searchContainer.popoverShouldClose) { searchContainer.closePopover(); } }
311286 NumberAnimation { targets: [searchContainer, searchField] ; property: "width"; duration: 200; easing.type: Easing.InOutQuad }
312− AnchorAnimation { targets: [searchContainer, textContainer]; duration: 200; easing.type: Easing.InOutQuad }
287+ AnchorAnimation { targets: [searchContainer, itemContainer]; duration: 200; easing.type: Easing.InOutQuad }
313288 },
314289 Transition {
315290 to: "narrowActive"
316291 SequentialAnimation {
317292 ParallelAnimation {
318293 NumberAnimation { targets: [searchContainer, searchField] ; property: "width"; duration: 200; easing.type: Easing.OutQuad }
319− AnchorAnimation { targets: [searchContainer, textContainer]; duration: 200; easing.type: Easing.InOutQuad }
294+ AnchorAnimation { targets: [searchContainer, itemContainer]; duration: 200; easing.type: Easing.InOutQuad }
320295 }
321296 ScriptAction { script: if (searchContainer.popoverShouldOpen) { searchContainer.openPopover(); } }
322297 }
@@ -325,7 +300,7 @@ Item {
325300 to: "narrowInactive"
326301 ScriptAction { script: if (searchContainer.popoverShouldClose) { searchContainer.closePopover(); } }
327302 NumberAnimation { targets: [searchContainer, searchField] ; property: "width"; duration: 200; easing.type: Easing.OutQuad }
328− AnchorAnimation { targets: [searchContainer, textContainer]; duration: 200; easing.type: Easing.InOutQuad }
303+ AnchorAnimation { targets: [searchContainer, itemContainer]; duration: 200; easing.type: Easing.InOutQuad }
329304 }
330305 ]
331306
qml/Components/PageHeaderLabel.qmlnew file+41
@@ -0,0 +1,41 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+
20+PageHeader {
21+ property alias text: label.text
22+
23+ childItem: Label {
24+ id: label
25+ anchors {
26+ left: parent.left
27+ leftMargin: units.gu(2)
28+ right: parent.right
29+ verticalCenter: parent.verticalCenter
30+ }
31+
32+ color: Theme.palette.selected.backgroundText
33+ opacity: 0.8
34+ font.family: "Ubuntu"
35+ font.weight: Font.Light
36+ fontSize: "x-large"
37+ elide: Text.ElideRight
38+ style: Text.Raised
39+ styleColor: "black"
40+ }
41+}
qml/Dash/Dash.qml−18
@@ -66,11 +66,6 @@ Showable {
6666 model: filteredScopes
6767 scopes: scopes
6868 searchHistory: dash.searchHistory
69− onMovementStarted: dashbar.startNavigation()
70− onMovementEnded: dashbar.stopNavigation()
71− onContentFlickStarted: dashbar.finishNavigation()
72− onContentEndReached: dashbar.finishNavigation()
73− onPreviewShown: dashbar.finishNavigation()
7469 onScopeLoaded: {
7570 if (scopeId == dash.showScopeOnLoaded) {
7671 dash.setCurrentScope(scopeId, false, false)
@@ -80,17 +75,4 @@ Showable {
8075 scale: dash.contentScale
8176 clip: scale != 1.0
8277 }
83−
84− DashBar {
85− id: dashbar
86− objectName: "dashbar"
87− anchors.bottom: parent.bottom
88− anchors.left: parent.left
89− anchors.right: parent.right
90− model: filteredScopes
91− currentIndex: dashContent.currentIndex
92− onItemSelected: dashContent.setCurrentScopeAtIndex(index, true, false)
93− opacity: dash.contentScale == 1.0 ? 1.0 : 0.0
94− Behavior on opacity { NumberAnimation { easing.type: Easing.OutQuad; duration: 150 } }
95− }
9678}
qml/Dash/DashApps.qml−3
@@ -26,9 +26,6 @@ GenericScopeView {
2626 id: scopeView
2727 objectName: "DashApps"
2828
29− //FIXME an alias to itself shouldn't be required. More than likely a Qt bug. Try removing with newer Qt.
30− property alias searchHistory: scopeView.searchHistory
31−
3229 ListModel {
3330 id: dummyVisibilityModifier
3431
qml/Dash/DashBar.qmldeleted−128
@@ -1,128 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−import Unity 0.1
20−
21−Item {
22− id: dashBar
23−
24− property var model
25− property alias currentIndex: row.currentIndex
26−
27− property int lineHeight: units.dp(2)
28− property int itemSize: units.gu(7)
29− property int iconSize: units.gu(3.5)
30−
31− signal itemSelected(int index)
32−
33− width: units.gu(40)
34− height: units.gu(6)
35−
36− function startNavigation() {
37− timeout.stop()
38− panel.open()
39− }
40−
41− function stopNavigation() {
42− timeout.restart()
43− }
44−
45− function finishNavigation() {
46− panel.close()
47− }
48−
49− Timer {
50− id: timeout
51− interval: 1500
52− running: false
53− repeat: false
54− onTriggered: finishNavigation()
55− }
56−
57− Panel {
58− id: panel
59− objectName: "panel"
60− anchors.fill: parent
61−
62− locked: true // TODO: remove this when lp bug #1179569 will be fixed
63−
64− Rectangle {
65− color: "black"
66− anchors.fill: parent
67−
68− ListView {
69− id: row
70− objectName: "row"
71− model: dashBar.model
72− orientation: ListView.Horizontal
73− width: Math.min(Math.max(dashBar.width/2, units.gu(40)), count * itemSize)
74− height: parent.height
75− anchors.horizontalCenter: parent.horizontalCenter
76− onMovingChanged: if (moving) { timeout.stop() } else { timeout.restart() }
77− interactive: visibleArea.widthRatio < 1 && panel.opened
78− highlightFollowsCurrentItem: false
79−
80− onCurrentItemChanged: {
81− highlightLine.width = currentItem.width
82− highlightLine.x = x + currentItem.x
83− }
84−
85− delegate:
86− // FIXME: Use Panel's integrated trigger() once that works again.
87− MouseArea {
88− width: itemSize
89− height: dashBar.height
90− anchors.top: parent.top
91−
92− onClicked: {
93− dashBar.itemSelected(index)
94− timeout.restart()
95− }
96−
97− Image {
98− anchors.centerIn: parent
99− source: scope.iconHint
100− sourceSize { width: iconSize; height: iconSize }
101− // opacity: index == currentIndex ? 1 : 1 // same opacity for now
102− }
103− }
104− }
105− }
106− }
107−
108− Rectangle {
109− anchors {
110− left: parent.left
111− right: parent.right
112− bottom: parent.bottom
113− }
114− height: dashBar.lineHeight
115− color: "black"
116−
117− Rectangle {
118− id: highlightLine
119− color: Theme.palette.selected.foreground
120− height: parent.height
121− anchors.bottom: parent.bottom
122− z: 1
123−
124− Behavior on x {NumberAnimation { duration: 150; easing.type: Easing.OutCubic}}
125− Behavior on width {NumberAnimation { duration: 150; easing.type: Easing.OutCubic}}
126− }
127− }
128−}
qml/Dash/DashContent.qml+123−62
@@ -17,6 +17,7 @@
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
1919import Unity 0.1
20+import "../Components"
2021
2122Item {
2223 id: dashContent
@@ -33,7 +34,6 @@ Item {
3334 signal movementEnded()
3435 signal contentFlickStarted()
3536 signal contentEndReached()
36− signal previewShown()
3737 signal scopeLoaded(string scopeId)
3838 signal positionedAtBeginning()
3939
@@ -80,77 +80,138 @@ Item {
8080 }
8181 }
8282
83− ListView {
84− id: dashContentList
85− objectName: "dashContentList"
86−
87− interactive: dashContent.scopes.loaded && !currentItem.previewShown && !currentItem.moving
88−
83+ Item {
84+ id: dashContentListHolder
8985 anchors.fill: parent
90− model: dashContent.model
91− orientation: ListView.Horizontal
92− boundsBehavior: Flickable.DragAndOvershootBounds
93− flickDeceleration: units.gu(625)
94− maximumFlickVelocity: width * 5
95− snapMode: ListView.SnapOneItem
96− highlightMoveDuration: 250
97− highlightRangeMode: ListView.StrictlyEnforceRange
98− /* FIXME: workaround rendering issue due to use of ShaderEffectSource in
99− UbuntuShape. While switching from the home scope to the People scope the
100− rendering would block midway.
101− */
102− cacheBuffer: 2147483647
103− onMovementStarted: dashContent.movementStarted()
104− onMovementEnded: dashContent.movementEnded()
105−
106− // If the number of items is less than the current index, then need to reset to another item.
107− onCountChanged: {
108− if (count > 0) {
109− if (currentIndex >= count) {
110− dashContent.setCurrentScopeAtIndex(count-1, true, true)
111− } else if (currentIndex < 0) {
112− // setting currentIndex directly, cause we don't want to loose set_current_index
113− dashContent.currentIndex = 0
86+
87+ ListView {
88+ id: dashContentList
89+ objectName: "dashContentList"
90+
91+ interactive: dashContent.scopes.loaded && !previewListView.onScreen && !currentItem.moving
92+
93+ anchors.fill: parent
94+ model: dashContent.model
95+ orientation: ListView.Horizontal
96+ boundsBehavior: Flickable.DragAndOvershootBounds
97+ flickDeceleration: units.gu(625)
98+ maximumFlickVelocity: width * 5
99+ snapMode: ListView.SnapOneItem
100+ highlightMoveDuration: 250
101+ highlightRangeMode: ListView.StrictlyEnforceRange
102+ // TODO Investigate if we can switch to a smaller cache buffer when/if UbuntuShape gets more performant
103+ cacheBuffer: 1073741823
104+ onMovementStarted: dashContent.movementStarted()
105+ onMovementEnded: dashContent.movementEnded()
106+
107+ // If the number of items is less than the current index, then need to reset to another item.
108+ onCountChanged: {
109+ if (count > 0) {
110+ if (currentIndex >= count) {
111+ dashContent.setCurrentScopeAtIndex(count-1, true, true)
112+ } else if (currentIndex < 0) {
113+ // setting currentIndex directly, cause we don't want to loose set_current_index
114+ dashContent.currentIndex = 0
115+ }
114116 }
115117 }
118+
119+ delegate:
120+ Loader {
121+ width: ListView.view.width
122+ height: ListView.view.height
123+ asynchronous: true
124+ source: scopeMapper.map(scope.id)
125+ objectName: scope.id + " loader"
126+
127+ readonly property bool moving: item ? item.moving : false
128+ readonly property var categoryView: item ? item.categoryView : null
129+ readonly property Scope theScope: scope
130+
131+ // these are needed for autopilot tests
132+ readonly property string scopeId: scope.id
133+ readonly property bool isCurrent: ListView.isCurrentItem
134+ readonly property bool isLoaded: status == Loader.Ready
135+
136+ onLoaded: {
137+ item.scope = Qt.binding(function() { return scope })
138+ item.isCurrent = Qt.binding(function() { return visible && ListView.isCurrentItem })
139+ item.tabBarHeight = pageHeader.implicitHeight;
140+ item.pageHeader = pageHeader;
141+ item.openEffect = openEffect;
142+ item.previewListView = previewListView;
143+ dashContentList.movementStarted.connect(item.movementStarted)
144+ dashContent.positionedAtBeginning.connect(item.positionedAtBeginning)
145+ dashContent.scopeLoaded(item.scope.id)
146+ }
147+ Connections {
148+ target: item
149+ ignoreUnknownSignals: true
150+ onEndReached: contentEndReached()
151+ }
152+
153+ Component.onDestruction: active = false
154+ }
116155 }
117156
118− delegate:
119− Loader {
120− width: ListView.view.width
121− height: ListView.view.height
122− asynchronous: true
123− source: scopeMapper.map(scope.id)
124− objectName: scope.id + " loader"
125−
126− readonly property bool previewShown: item ? item.previewShown : false
127− readonly property bool moving: item ? item.moving : false
128−
129− // these are needed for autopilot tests
130− readonly property string scopeId: scope.id
131− readonly property bool isCurrent: ListView.isCurrentItem
132− readonly property bool isLoaded: status == Loader.Ready
133−
134− onLoaded: {
135− item.scope = Qt.binding(function() { return scope })
136− item.isCurrent = Qt.binding(function() { return visible && ListView.isCurrentItem })
137− item.searchHistory = Qt.binding(function() { return dashContent.searchHistory })
138− dashContentList.movementStarted.connect(item.movementStarted)
139− dashContent.positionedAtBeginning.connect(item.positionedAtBeginning)
140− dashContent.scopeLoaded(item.scope.id)
157+ PageHeader {
158+ id: pageHeader
159+ width: parent.width
160+ searchEntryEnabled: true
161+ searchHistory: dashContent.searchHistory
162+
163+ childItem: TabBar {
164+ id: tabBar
165+ objectName: "tabbar"
166+ height: units.gu(7)
167+ width: parent.width
168+ selectionMode: false
169+ style: DashContentTabBarStyle {}
170+
171+ model: dashContentList.model
172+
173+ onSelectedIndexChanged: {
174+ dashContentList.currentIndex = selectedIndex;
141175 }
176+
142177 Connections {
143− target: item
144− ignoreUnknownSignals: true
145− onEndReached: contentEndReached()
146− onPreviewShownChanged: {
147− if (item.previewShown) {
148− dashContent.previewShown()
149− }
178+ target: dashContentList
179+ onCurrentIndexChanged: {
180+ tabBar.selectedIndex = dashContentList.currentIndex
150181 }
151182 }
152183
153− Component.onDestruction: active = false
184+ Connections {
185+ target: model
186+ onCountChanged: {
187+ if (tabBar.selectedIndex < 0 && model.count > 0)
188+ tabBar.selectedIndex = 0;
189+ }
190+ }
191+
192+ Component.onCompleted: {
193+ __styleInstance.headerTextStyle = Text.Raised
194+ __styleInstance.headerTextStyleColor = "black"
195+ }
154196 }
197+ }
198+ }
199+
200+ DashContentOpenEffect {
201+ id: openEffect
202+ anchors {
203+ fill: parent
204+ bottomMargin: -bottomOverflow
205+ }
206+ sourceItem: dashContentListHolder
207+ previewListView: previewListView
208+ }
209+
210+ PreviewListView {
211+ id: previewListView
212+ openEffect: openEffect
213+ categoryView: dashContentList.currentItem ? dashContentList.currentItem.categoryView : null
214+ scope: dashContentList.currentItem ? dashContentList.currentItem.theScope : null
215+ anchors.fill: parent
155216 }
156217}
qml/Dash/DashContentOpenEffect.qmlnew file+57
@@ -0,0 +1,57 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+import "../Components"
20+
21+OpenEffect {
22+ property PreviewListView previewListView: null
23+
24+ objectName: "openEffect"
25+ anchors {
26+ fill: parent
27+ bottomMargin: -bottomOverflow
28+ }
29+
30+ enabled: gap > 0.0
31+
32+ topGapPx: (1 - gap) * positionPx
33+ topOpacity: (1 - gap * 1.2)
34+ bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
35+ bottomOverflow: units.gu(20)
36+ live: !expansionAnimation.running
37+
38+ property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
39+ property real gap: previewListView.open ? 1.0 : 0.0
40+
41+ Behavior on gap {
42+ NumberAnimation {
43+ id: expansionAnimation
44+ duration: 200
45+ easing.type: Easing.InOutQuad
46+ onRunningChanged: {
47+ if (!previewListView.open && !running) {
48+ previewListView.onScreen = false
49+ }
50+ }
51+ }
52+ }
53+ Behavior on positionPx {
54+ enabled: previewListView.open
55+ UbuntuNumberAnimation {}
56+ }
57+}
qml/Dash/DashContentTabBarStyle.qmlnew file+21
@@ -0,0 +1,21 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import Ubuntu.Components.Themes.Ambiance 0.1
18+
19+TabBarStyle {
20+ indicatorImageSource: "graphics/tabbarchevron.png"
21+}
qml/Dash/GenericScopeView.qml+28−228
@@ -27,15 +27,18 @@ FocusScope {
2727 property Scope scope
2828 property SortFilterProxyModel categories: categoryFilter
2929 property bool isCurrent
30− property ListModel searchHistory
3130 property alias moving: categoryView.moving
31+ property int tabBarHeight: 0
32+ property PageHeader pageHeader: null
33+ property OpenEffect openEffect: null
34+ property Item previewListView: null
3235
3336 signal endReached
3437 signal movementStarted
3538 signal positionedAtBeginning
3639
37− readonly property alias previewShown: previewListView.onScreen
3840 property bool enableHeightBehaviorOnNextCreation: false
41+ property var categoryView: categoryView
3942
4043 // FIXME delay the search so that daemons have time to settle, note that
4144 // removing this will break ScopeView::test_changeScope
@@ -84,12 +87,14 @@ FocusScope {
8487 target: scopeView.scope
8588 property: "searchQuery"
8689 value: pageHeader.searchQuery
90+ when: isCurrent
8791 }
8892
8993 Binding {
9094 target: pageHeader
9195 property: "searchQuery"
9296 value: scopeView.scope.searchQuery
97+ when: isCurrent
9398 }
9499
95100 Connections {
@@ -119,6 +124,10 @@ FocusScope {
119124 property string expandedCategoryId: ""
120125 signal correctExpandedCategory();
121126
127+ onContentYChanged: pageHeader.positionRealHeader();
128+ onOriginYChanged: pageHeader.positionRealHeader();
129+ onContentHeightChanged: pageHeader.positionRealHeader();
130+
122131 Behavior on contentY {
123132 enabled: previewListView.open
124133 UbuntuNumberAnimation {}
@@ -175,7 +184,7 @@ FocusScope {
175184 target: rendererLoader.item
176185 onClicked: {
177186 // Prepare the preview in case activate() triggers a preview only
178− effect.positionPx = Math.max(mapToItem(categoryView, 0, itemY).y, pageHeader.height + categoryView.stickyHeaderHeight);
187+ openEffect.positionPx = Math.max(mapToItem(categoryView, 0, itemY).y, pageHeader.height + categoryView.stickyHeaderHeight);
179188 previewListView.categoryId = categoryId
180189 previewListView.categoryDelegate = rendererLoader.item
181190 previewListView.model = target.model;
@@ -196,7 +205,7 @@ FocusScope {
196205 }
197206 }
198207 onPressAndHold: {
199− effect.positionPx = Math.max(mapToItem(categoryView, 0, itemY).y, pageHeader.height + categoryView.stickyHeaderHeight);
208+ openEffect.positionPx = Math.max(mapToItem(categoryView, 0, itemY).y, pageHeader.height + categoryView.stickyHeaderHeight);
200209 previewListView.categoryId = categoryId
201210 previewListView.categoryDelegate = rendererLoader.item
202211 previewListView.model = target.model;
@@ -287,14 +296,20 @@ FocusScope {
287296 categoryView.expandedCategoryId = "";
288297 }
289298 }
290− pageHeader: PageHeader {
291− id: pageHeader
292− objectName: "pageHeader"
293− width: categoryView.width
294− text: scopeView.scope.name
295− searchEntryEnabled: true
296− scope: scopeView.scope
297− searchHistory: scopeView.searchHistory
299+ pageHeader: Item {
300+ implicitHeight: scopeView.tabBarHeight
301+ onHeightChanged: {
302+ if (scopeView.pageHeader) {
303+ scopeView.pageHeader.height = height;
304+ }
305+ }
306+ onYChanged: positionRealHeader();
307+
308+ function positionRealHeader() {
309+ if (scopeView.pageHeader) {
310+ scopeView.pageHeader.y = y + parent.y;
311+ }
312+ }
298313 }
299314 }
300315
@@ -345,44 +360,6 @@ FocusScope {
345360 }
346361 }
347362
348− OpenEffect {
349− id: effect
350− objectName: "openEffect"
351− anchors {
352− fill: parent
353− bottomMargin: -bottomOverflow
354− }
355− sourceItem: categoryView
356−
357− enabled: gap > 0.0
358−
359− topGapPx: (1 - gap) * positionPx
360− topOpacity: (1 - gap * 1.2)
361− bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
362− bottomOverflow: units.gu(20)
363− live: !expansionAnimation.running
364−
365− property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
366− property real gap: previewListView.open ? 1.0 : 0.0
367−
368− Behavior on gap {
369− NumberAnimation {
370− id: expansionAnimation
371− duration: 200
372− easing.type: Easing.InOutQuad
373− onRunningChanged: {
374− if (!previewListView.open && !running) {
375− previewListView.onScreen = false
376− }
377− }
378− }
379− }
380− Behavior on positionPx {
381− enabled: previewListView.open
382− UbuntuNumberAnimation {}
383− }
384− }
385−
386363 Connections {
387364 target: scopeView.scope
388365 onPreviewReady: {
@@ -401,190 +378,13 @@ FocusScope {
401378 }
402379 }
403380
404− PreviewDelegateMapper {
405− id: previewDelegateMapper
406− }
407−
408− ListView {
409− id: previewListView
410− objectName: "previewListView"
411− height: effect.bottomGapPx - effect.topGapPx
412− anchors {
413− top: parent.top
414− topMargin: effect.topGapPx
415− left: parent.left
416− right: parent.right
417− }
418− orientation: ListView.Horizontal
419− highlightRangeMode: ListView.StrictlyEnforceRange
420− snapMode: ListView.SnapOneItem
421− boundsBehavior: Flickable.DragAndOvershootBounds
422− highlightMoveDuration: 250
423− flickDeceleration: units.gu(625)
424− maximumFlickVelocity: width * 5
425− cacheBuffer: 0
426−
427− // To be set before opening the preview
428− property string categoryId: ""
429− property var categoryDelegate
430−
431− // because the ListView is built asynchronous, setting the
432− // currentIndex directly won't work. We need to refresh it
433− // when the first preview is ready to be displayed.
434− property bool init: true
435−
436− onCurrentIndexChanged: {
437− var row = Math.floor(currentIndex / categoryDelegate.columns);
438− if (categoryDelegate.collapsedRowCount <= row) {
439− categoryView.expandedCategoryId = categoryId
440− }
441−
442− if (open) {
443− categoryDelegate.highlightIndex = currentIndex
444− }
445−
446− if (!init && model !== undefined) {
447− var item = model.get(currentIndex)
448− scopeView.scope.preview(item.uri, item.icon, item.category, 0, item.mimetype, item.title, item.comment, item.dndUri, item.metadata)
449− }
450−
451− // Adjust contentY in case we need to change to it to show the next row
452− if (categoryDelegate.rows > 1) {
453− var itemY = categoryView.contentItem.mapFromItem(categoryDelegate.currentItem).y;
454−
455− // Find new contentY and effect.postionPx
456− var newContentY = itemY - effect.positionPx - categoryDelegate.verticalSpacing;
457−
458− // Make sure the item is not covered by a header. Move the effect split down if necessary
459− var headerHeight = pageHeader.height + categoryView.stickyHeaderHeight;
460− var effectAdjust = Math.max(effect.positionPx, headerHeight);
461−
462− // Make sure we don't overscroll the listview. If yes, adjust effect position
463− if (newContentY < 0) {
464− effectAdjust += newContentY;
465− newContentY = 0;
466− }
467− if (newContentY > Math.max(0, categoryView.contentHeight - categoryView.height)) {
468− effectAdjust += -(categoryView.contentHeight - categoryView.height) + newContentY
469− newContentY = categoryView.contentHeight - categoryView.height;
470− }
471−
472− effect.positionPx = effectAdjust;
473− categoryView.contentY = newContentY;
474− }
475− }
476−
477− property bool open: false
478− property bool onScreen: false
479−
480− onOpenChanged: {
481− if (open) {
482− onScreen = true;
483− categoryDelegate.highlightIndex = currentIndex;
484− pageHeader.unfocus();
485− } else {
486− // Cancel any pending preview requests or actions
487− if (previewListView.currentItem.previewData !== undefined) {
488− previewListView.currentItem.previewData.cancelAction();
489− }
490− scopeView.scope.cancelActivation();
491− model = undefined;
492− categoryView.correctExpandedCategory();
493− categoryDelegate.highlightIndex = -1;
494− }
495− }
496−
497− Rectangle {
498− anchors.fill: parent
499− color: Qt.rgba(0, 0, 0, .3)
500− z: -1
501− }
502−
503− delegate: Loader {
504− id: previewLoader
505− objectName: "previewLoader" + index
506− height: previewListView.height
507− width: previewListView.width
508− asynchronous: true
509− source: previewListView.onScreen ?
510− (previewData !== undefined ? previewDelegateMapper.map(previewData.rendererName) : "DashPreviewPlaceholder.qml") : ""
511−
512− onPreviewDataChanged: {
513− if (previewData !== undefined && source.toString().indexOf("DashPreviewPlaceholder.qml") != -1) {
514− previewLoader.opacity = 0;
515− }
516− }
517−
518− onSourceChanged: {
519− if (previewData !== undefined) {
520− fadeIn.start()
521− }
522− }
523−
524− PropertyAnimation {
525− id: fadeIn
526− target: previewLoader
527− property: "opacity"
528− from: 0.0
529− to: 1.0
530− duration: UbuntuAnimation.BriskDuration
531− }
532−
533− property var previewData
534− property bool valid: item !== null
535−
536− onLoaded: {
537− if (previewListView.onScreen && previewData !== undefined) {
538− item.previewData = Qt.binding(function() { return previewData })
539− item.isCurrent = Qt.binding(function() { return ListView.isCurrentItem })
540− }
541− }
542−
543− Connections {
544− ignoreUnknownSignals: true
545− target: item
546− onClose: {
547− previewListView.open = false
548− }
549− }
550−
551− function closePreviewSpinner() {
552− if (item) {
553− item.showProcessingAction = false;
554− }
555− }
556− }
557− }
558−
559− Image {
560− objectName: "pointerArrow"
561− anchors {
562− top: previewListView.bottom
563− left: parent.left
564− leftMargin: previewListView.categoryDelegate !== undefined && previewListView.categoryDelegate.currentItem ?
565− previewListView.categoryDelegate.currentItem.center + (-width + margins) / 2 : 0
566−
567− Behavior on leftMargin {
568− SmoothedAnimation {
569− duration: UbuntuAnimation.FastDuration
570− }
571− }
572− }
573− height: units.gu(1)
574− width: units.gu(2)
575− property int margins: previewListView.categoryDelegate ? previewListView.categoryDelegate.margins : 0
576− opacity: previewListView.open ? .5 : 0
577−
578− source: "graphics/tooltip_arrow.png"
579− }
580−
581381 // TODO: Move as InverseMouseArea to DashPreview
582382 MouseArea {
583383 objectName: "closePreviewMouseArea"
584384 enabled: previewListView.onScreen
585385 anchors {
586386 fill: parent
587− topMargin: effect.bottomGapPx
387+ topMargin: openEffect.bottomGapPx
588388 }
589389 onClicked: {
590390 previewListView.open = false;
qml/Dash/PreviewListView.qmlnew file+212
@@ -0,0 +1,212 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+import Unity 0.1
20+import "../Components"
21+
22+Item {
23+ property OpenEffect openEffect: null
24+ property ScopeListView categoryView: null
25+ property Scope scope: null
26+
27+ property alias open: previewListView.open
28+ property alias onScreen: previewListView.onScreen
29+ property alias categoryId: previewListView.categoryId
30+ property alias categoryDelegate: previewListView.categoryDelegate
31+ property alias init: previewListView.init
32+ property alias model: previewListView.model
33+ property alias currentIndex: previewListView.currentIndex
34+ property alias currentItem: previewListView.currentItem
35+
36+ Image {
37+ objectName: "pointerArrow"
38+ anchors {
39+ top: previewListView.bottom
40+ left: parent.left
41+ leftMargin: previewListView.categoryDelegate !== undefined && previewListView.categoryDelegate.currentItem ?
42+ previewListView.categoryDelegate.currentItem.center + (-width + margins) / 2 : 0
43+
44+ Behavior on leftMargin {
45+ SmoothedAnimation {
46+ duration: UbuntuAnimation.FastDuration
47+ }
48+ }
49+ }
50+ height: units.gu(1)
51+ width: units.gu(2)
52+ property int margins: previewListView.categoryDelegate ? previewListView.categoryDelegate.margins : 0
53+ opacity: previewListView.open ? .5 : 0
54+
55+ source: "graphics/tooltip_arrow.png"
56+ }
57+
58+ ListView {
59+ id: previewListView
60+ objectName: "previewListView"
61+ height: openEffect.bottomGapPx - openEffect.topGapPx
62+ anchors {
63+ top: parent.top
64+ topMargin: openEffect.topGapPx
65+ left: parent.left
66+ right: parent.right
67+ }
68+ orientation: ListView.Horizontal
69+ highlightRangeMode: ListView.StrictlyEnforceRange
70+ snapMode: ListView.SnapOneItem
71+ boundsBehavior: Flickable.DragAndOvershootBounds
72+ highlightMoveDuration: 250
73+ flickDeceleration: units.gu(625)
74+ maximumFlickVelocity: width * 5
75+ cacheBuffer: 0
76+
77+ // To be set before opening the preview
78+ property string categoryId: ""
79+ property var categoryDelegate
80+
81+ // because the ListView is built asynchronous, setting the
82+ // currentIndex directly won't work. We need to refresh it
83+ // when the first preview is ready to be displayed.
84+ property bool init: true
85+
86+ PreviewDelegateMapper {
87+ id: previewDelegateMapper
88+ }
89+
90+ onCurrentIndexChanged: {
91+ var row = Math.floor(currentIndex / categoryDelegate.columns);
92+ if (categoryDelegate.collapsedRowCount <= row) {
93+ categoryView.expandedCategoryId = categoryId
94+ }
95+
96+ if (open) {
97+ categoryDelegate.highlightIndex = currentIndex
98+ }
99+
100+ if (!init && model !== undefined) {
101+ var item = model.get(currentIndex)
102+ scope.preview(item.uri, item.icon, item.category, 0, item.mimetype, item.title, item.comment, item.dndUri, item.metadata)
103+ }
104+
105+ // Adjust contentY in case we need to change to it to show the next row
106+ if (categoryDelegate.rows > 1) {
107+ var itemY = categoryView.contentItem.mapFromItem(categoryDelegate.currentItem).y;
108+
109+ // Find new contentY and effect.postionPx
110+ var newContentY = itemY - openEffect.positionPx - categoryDelegate.verticalSpacing;
111+
112+ // Make sure the item is not covered by a header. Move the effect split down if necessary
113+ var headerHeight = pageHeader.height + categoryView.stickyHeaderHeight;
114+ var effectAdjust = Math.max(openEffect.positionPx, headerHeight);
115+
116+ // Make sure we don't overscroll the listview. If yes, adjust effect position
117+ if (newContentY < 0) {
118+ effectAdjust += newContentY;
119+ newContentY = 0;
120+ }
121+ if (newContentY > Math.max(0, categoryView.contentHeight - categoryView.height)) {
122+ effectAdjust += -(categoryView.contentHeight - categoryView.height) + newContentY
123+ newContentY = categoryView.contentHeight - categoryView.height;
124+ }
125+
126+ openEffect.positionPx = effectAdjust;
127+ categoryView.contentY = newContentY;
128+ }
129+ }
130+
131+ property bool open: false
132+ property bool onScreen: false
133+
134+ onOpenChanged: {
135+ if (open) {
136+ onScreen = true;
137+ categoryDelegate.highlightIndex = currentIndex;
138+ pageHeader.unfocus();
139+ } else {
140+ // Cancel any pending preview requests or actions
141+ if (previewListView.currentItem.previewData !== undefined) {
142+ previewListView.currentItem.previewData.cancelAction();
143+ }
144+ scope.cancelActivation();
145+ model = undefined;
146+ categoryView.correctExpandedCategory();
147+ categoryDelegate.highlightIndex = -1;
148+ }
149+ }
150+
151+ Rectangle {
152+ anchors.fill: parent
153+ color: Qt.rgba(0, 0, 0, .3)
154+ z: -1
155+ }
156+
157+ delegate: Loader {
158+ id: previewLoader
159+ objectName: "previewLoader" + index
160+ height: previewListView.height
161+ width: previewListView.width
162+ asynchronous: true
163+ source: previewListView.onScreen ?
164+ (previewData !== undefined ? previewDelegateMapper.map(previewData.rendererName) : "DashPreviewPlaceholder.qml") : ""
165+
166+ onPreviewDataChanged: {
167+ if (previewData !== undefined && source.toString().indexOf("DashPreviewPlaceholder.qml") != -1) {
168+ previewLoader.opacity = 0;
169+ }
170+ }
171+
172+ onSourceChanged: {
173+ if (previewData !== undefined) {
174+ fadeIn.start()
175+ }
176+ }
177+
178+ PropertyAnimation {
179+ id: fadeIn
180+ target: previewLoader
181+ property: "opacity"
182+ from: 0.0
183+ to: 1.0
184+ duration: UbuntuAnimation.BriskDuration
185+ }
186+
187+ property var previewData
188+ property bool valid: item !== null
189+
190+ onLoaded: {
191+ if (previewListView.onScreen && previewData !== undefined) {
192+ item.previewData = Qt.binding(function() { return previewData })
193+ item.isCurrent = Qt.binding(function() { return ListView.isCurrentItem })
194+ }
195+ }
196+
197+ Connections {
198+ ignoreUnknownSignals: true
199+ target: item
200+ onClose: {
201+ previewListView.open = false
202+ }
203+ }
204+
205+ function closePreviewSpinner() {
206+ if (item) {
207+ item.showProcessingAction = false;
208+ }
209+ }
210+ }
211+ }
212+}
qml/Dash/graphics/tabbarchevron@27.pngnew file

Binary file not shown.

qml/Hud/HudParametrizedActionsPage.qml+1−1
@@ -89,7 +89,7 @@ Item {
8989 anchors.left: parent.left
9090 anchors.right: parent.right
9191
92− PageHeader {
92+ PageHeaderLabel {
9393 id: header
9494 anchors.top: parent.top
9595 anchors.left: parent.left
qml/Notifications/Notification.qml+14−4
@@ -34,10 +34,12 @@ Item {
3434 property var notification
3535 property color color
3636 property bool fullscreen
37+ property int maxHeight
38+ property int margins
3739
3840 fullscreen: false
3941 objectName: "background"
40− implicitHeight: type != Notification.PlaceHolder ? (fullscreen ? notificationList.fullHeight : contentColumn.height + contentColumn.spacing * 4) : 0
42+ implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : contentColumn.height + contentColumn.spacing * 4) : 0
4143
4244 color: Qt.rgba(0.132, 0.117, 0.109, 0.97)
4345 opacity: 0
@@ -50,7 +52,7 @@ Item {
5052
5153 visible: !fullscreen
5254 anchors.fill: parent
53− anchors.margins: notificationList.margin
55+ anchors.margins: notification.margins
5456 color: parent.color
5557 opacity: parent.opacity
5658 radius: "medium"
@@ -104,8 +106,11 @@ Item {
104106
105107 anchors.fill: contentColumn
106108 objectName: "interactiveArea"
107− enabled: notification.type == Notification.Interactive
108− onClicked: notification.notification.invokeAction(actionRepeater.itemAt(0).actionId)
109+ onClicked: {
110+ if (notification.type == Notification.Interactive) {
111+ notification.notification.invokeAction(actionRepeater.itemAt(0).actionId)
112+ }
113+ }
109114 }
110115
111116 Column {
@@ -207,6 +212,11 @@ Item {
207212 menuModel: unityMenuModel
208213 menuData: model
209214 menuIndex: index
215+ maxHeight: notification.maxHeight
216+
217+ onLoaded: {
218+ notification.fullscreen = Qt.binding(function() { return fullscreen; });
219+ }
210220 }
211221 }
212222 }
qml/Notifications/NotificationMenuItemFactory.qml+4−5
@@ -28,6 +28,8 @@ Loader {
2828 property QtObject menuModel: null
2929 property QtObject menuData: null
3030 property int menuIndex
31+ property int maxHeight
32+ readonly property bool fullscreen: menuData.type === "com.canonical.snapdecision.pinlock"
3133
3234 property var _map: {
3335 "com.canonical.snapdecision.textfield": textfield,
@@ -43,10 +45,6 @@ Loader {
4345 }
4446 }
4547
46− Component.onCompleted: {
47− notification.fullscreen = menuData.type == "com.canonical.snapdecision.pinlock"
48− }
49−
5048 Component {
5149 id: textfield
5250
@@ -68,6 +66,7 @@ Loader {
6866 TextField {
6967 id: textfield
7068
69+ inputMethodHints: Qt.ImhNoAutoUppercase
7170 anchors.left: parent.left; anchors.right: parent.right
7271 echoMode: checkBox.checked ? TextInput.Normal : TextInput.Password
7372 height: units.gu(5)
@@ -100,7 +99,7 @@ Loader {
10099
101100 Lockscreen {
102101 anchors.left: parent.left; anchors.right: parent.right
103− height: notificationList.fullHeight
102+ height: menuFactory.maxHeight
104103 placeholderText: i18n.tr("Please enter SIM PIN")
105104 background: shell.background
106105
qml/Notifications/Notifications.qml+2−1
@@ -25,7 +25,6 @@ ListView {
2525 interactive: false
2626
2727 property real margin
28− property real fullHeight
2928
3029 delegate: Notification {
3130 objectName: "notification" + index
@@ -42,6 +41,8 @@ ListView {
4241 actions: model.actions
4342 notificationId: model.id
4443 notification: notificationList.model.getRaw(notificationId)
44+ maxHeight: notificationList.height
45+ margins: notificationList.margin
4546
4647 // make sure there's no opacity-difference between the several
4748 // elements in a notification
qml/Panel/IndicatorItem.qml−13
@@ -23,7 +23,6 @@ Item {
2323 id: indicatorItem
2424
2525 property alias widgetSource: loader.source
26− property bool highlighted: false
2726 property bool dimmed: false
2827 property var indicatorProperties: undefined
2928 property bool indicatorVisible: loader.item ? loader.item.enabled : false
@@ -46,16 +45,4 @@ Item {
4645 }
4746 }
4847 }
49−
50− Rectangle {
51− color: Theme.palette.selected.foreground
52− objectName: "highlight"
53− height: units.dp(2)
54− width: parent.width
55− anchors.top: parent.bottom
56− visible: highlighted
57−
58− opacity: highlighted ? 1 : 0
59− Behavior on opacity { StandardAnimation {} }
60− }
6148}
qml/Panel/IndicatorRow.qml+39−8
@@ -29,6 +29,8 @@ Item {
2929 property var visibleIndicators: defined
3030 property int overFlowWidth: width
3131 property bool showAll: false
32+ property real currentItemOffset: 0.0
33+ property real unitProgress: 0.0
3234
3335 width: units.gu(40)
3436 height: units.gu(3)
@@ -79,23 +81,23 @@ Item {
7981 height: indicatorRow.height
8082 width: indicatorItem.width
8183 visible: indicatorItem.indicatorVisible
82− opacity: 1.0
84+ opacity: 1.0 * opacityMultiplier
8385 y: 0
8486 state: "standard"
8587
8688 property int ownIndex: index
87− property alias highlighted: indicatorItem.highlighted
88− property alias dimmed: indicatorItem.dimmed
89+ property bool highlighted: indicatorRow.state != "initial" ? ownIndex == indicatorRow.currentItemIndex : false
90+ property bool dimmed: indicatorRow.state != "initial" ? ownIndex != indicatorRow.currentItemIndex : false
8991
90− property bool hidden: !showAll && !indicatorItem.highlighted && (indicatorRow.state == "locked" || indicatorRow.state == "commit")
92+ property bool hidden: !showAll && !highlighted && (indicatorRow.state == "locked" || indicatorRow.state == "commit")
9193 property bool overflow: row.width - itemWrapper.x > overFlowWidth
94+ property real opacityMultiplier: highlighted ? 1 : (1 - indicatorRow.unitProgress)
9295
9396 IndicatorItem {
9497 id: indicatorItem
9598 height: parent.height
9699
97− highlighted: indicatorRow.state != "initial" ? itemWrapper.ownIndex == indicatorRow.currentItemIndex : false
98− dimmed: indicatorRow.state != "initial" ? itemWrapper.ownIndex != indicatorRow.currentItemIndex : false
100+ dimmed: itemWrapper.dimmed
99101
100102 widgetSource: model.widgetSource
101103 indicatorProperties : model.indicatorProperties
@@ -135,14 +137,43 @@ Item {
135137
136138 transitions: [
137139 Transition {
138− StandardAnimation {
140+ UbuntuNumberAnimation {
139141 target: itemWrapper
140142 property: "opacity"
141− duration: 300
143+ duration: UbuntuAnimation.BriskDuration
142144 }
143145 }
144146 ]
145147 }
146148 }
147149 }
150+
151+ Rectangle {
152+ id: highlight
153+ color: Theme.palette.selected.foreground
154+ objectName: "highlight"
155+ height: units.dp(2)
156+ anchors.top: row.bottom
157+ visible: indicatorRow.currentItem != null
158+ x: row.x + (indicatorRow.currentItem != null ? indicatorRow.currentItem.x + centerOffset : 0)
159+ width: indicatorRow.currentItem != null ? indicatorRow.currentItem.width : 0
160+
161+ property real centerOffset: {
162+ if (indicatorRow.currentItemOffset > 0.1) {
163+ return (indicatorRow.currentItemOffset - 0.1) * units.gu(0.4);
164+ } else if (indicatorRow.currentItemOffset < -0.1) {
165+ return (indicatorRow.currentItemOffset + 0.1) * units.gu(0.4);
166+ }
167+ return 0.0;
168+ }
169+
170+ Behavior on width {
171+ enabled: unitProgress > 0;
172+ UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
173+ }
174+ Behavior on x {
175+ enabled: unitProgress > 0;
176+ UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
177+ }
178+ }
148179}
qml/Panel/Indicators.qml+22−11
@@ -39,6 +39,7 @@ Showable {
3939 property bool contentEnabled: true
4040 property bool initalizeItem: true
4141 readonly property alias content: menuContent
42+ property real unitProgress: 0.0
4243
4344 // TODO: Perhaps we need a animation standard for showing/hiding? Each showable seems to
4445 // use its own values. Need to ask design about this.
@@ -128,23 +129,31 @@ Showable {
128129 rowCoordinates = indicatorRow.mapToItem(indicatorRow.row, xValue, 0);
129130 // get the current delegate
130131 currentItem = indicatorRow.row.childAt(rowCoordinates.x, 0);
131− if (currentItem && currentItem != indicatorRow.currentItem ) {
132+ if (currentItem) {
132133 itemCoordinates = indicatorRow.row.mapToItem(currentItem, rowCoordinates.x, 0);
133134 distanceFromRightEdge = (currentItem.width - itemCoordinates.x) / (currentItem.width);
134− if (Math.abs(currentItem.ownIndex - indicatorRow.currentItemIndex) > 1) {
135− bufferExceeded = true;
136− } else {
137− if (indicatorRow.currentItemIndex < currentItem.ownIndex && distanceFromRightEdge < (1 - effectiveBufferThreshold)) {
138− bufferExceeded = true;
139− } else if (indicatorRow.currentItemIndex > currentItem.ownIndex && distanceFromRightEdge > effectiveBufferThreshold) {
135+ if (currentItem != indicatorRow.currentItem) {
136+ if (Math.abs(currentItem.ownIndex - indicatorRow.currentItemIndex) > 1) {
140137 bufferExceeded = true;
138+ } else {
139+ if (indicatorRow.currentItemIndex < currentItem.ownIndex && distanceFromRightEdge < (1 - effectiveBufferThreshold)) {
140+ bufferExceeded = true;
141+ } else if (indicatorRow.currentItemIndex > currentItem.ownIndex && distanceFromRightEdge > effectiveBufferThreshold) {
142+ bufferExceeded = true;
143+ }
141144 }
145+ if ((!useBuffer || (useBuffer && bufferExceeded)) || indicatorRow.currentItemIndex < 0 || indicatorRow.currentItem == null) {
146+ indicatorRow.currentItem = currentItem;
147+ }
148+
149+ // need to re-init the distanceFromRightEdge for offset calculation
150+ itemCoordinates = indicatorRow.row.mapToItem(indicatorRow.currentItem, rowCoordinates.x, 0);
151+ distanceFromRightEdge = (indicatorRow.currentItem.width - itemCoordinates.x) / (indicatorRow.currentItem.width);
142152 }
143− if ((!useBuffer || (useBuffer && bufferExceeded)) || indicatorRow.currentItem < 0 || indicatorRow.currentItem == null) {
144− indicatorRow.currentItem = currentItem;
145− }
146− } else if (!currentItem) {
153+ indicatorRow.currentItemOffset = 1 - (distanceFromRightEdge * 2);
154+ } else {
147155 indicatorRow.setDefaultItem();
156+ indicatorRow.currentItemOffset = 0;
148157 }
149158 initalizeItem = indicatorRow.currentItem == null;
150159 }
@@ -268,6 +277,7 @@ Showable {
268277 height: indicators.panelHeight
269278 indicatorsModel: indicatorsModel
270279 state: indicators.state
280+ unitProgress: indicators.unitProgress
271281
272282 Connections {
273283 property bool enableIndexChangeSignal: true
@@ -326,6 +336,7 @@ Showable {
326336 if (hideAnimation.running) {
327337 indicators.state = "initial";
328338 initalizeItem = true;
339+ menuContent.animateNextMenuChange = false;
329340 }
330341 }
331342 }
qml/Panel/MenuContent.qml+3−1
@@ -33,6 +33,7 @@ MainView {
3333 property int contentReleaseInterval: 20000
3434 property bool activeHeader: false
3535 property alias visibleIndicators: visibleIndicatorsModel.visible
36+ property bool animateNextMenuChange: false
3637
3738 width: units.gu(40)
3839 height: units.gu(42)
@@ -41,11 +42,12 @@ MainView {
4142 var filteredIndex = filteredIndicators.mapFromSource(index)
4243
4344 if (tabs.selectedTabIndex !== filteredIndex) {
44− if (tabs.selectedTabIndex == -1) {
45+ if (tabs.selectedTabIndex == -1 || !animateNextMenuChange) {
4546 tabs.tabBar.animate = false;
4647 }
4748 tabs.selectedTabIndex = filteredIndex;
4849 tabs.tabBar.animate = true;
50+ animateNextMenuChange = true;
4951 }
5052 }
5153
qml/Panel/Panel.qml+1−1
@@ -107,7 +107,7 @@ Item {
107107 pinnedMode: !fullscreenMode
108108 overFlowWidth: search.state=="hidden" ? parent.width : parent.width - search.width
109109
110− property real unitProgress: (height - panelHeight) / (openedHeight - panelHeight)
110+ unitProgress: (height - panelHeight) / (openedHeight - panelHeight)
111111 }
112112
113113 PanelSeparatorLine {
qml/ScopeTool.qmlnew file+177
@@ -0,0 +1,177 @@
1+/*
2+* Copyright (C) 2013 Canonical, Ltd.
3+*
4+* This program is free software; you can redistribute it and/or modify
5+* it under the terms of the GNU General Public License as published by
6+* the Free Software Foundation; version 3.
7+*
8+* This program is distributed in the hope that it will be useful,
9+* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+* GNU General Public License for more details.
12+*
13+* You should have received a copy of the GNU General Public License
14+* along with this program. If not, see <http://www.gnu.org/licenses/>.
15+*/
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+import Ubuntu.Components.Popups 0.1
20+import Unity 0.1
21+import "Dash"
22+
23+Rectangle {
24+ id: root
25+ width: units.gu(80)
26+ height: units.gu(72)
27+ color: "#88FFFFFF"
28+
29+ // Fake shell object
30+ QtObject {
31+ id: shell
32+ }
33+
34+ // Fake greeter object
35+ QtObject {
36+ id: greeter
37+ property bool shown
38+ }
39+
40+ // Fake panel object
41+ QtObject {
42+ id: panel
43+ signal searchClicked
44+ }
45+
46+
47+ Scopes {
48+ id: scopes
49+ }
50+
51+ Rectangle {
52+ anchors.fill: scopeView
53+ color: "#FCFCFC"
54+ }
55+
56+ GenericScopeView {
57+ id: scopeView
58+
59+ anchors {
60+ top: parent.top
61+ bottom: parent.bottom
62+ left: parent.left
63+ right: controls.left
64+ }
65+
66+ scope: scopes.loaded ? scopes.get(scopeSelector.selectedIndex) : undefined
67+ }
68+
69+ Rectangle {
70+ id: controls
71+ color: "lightgrey"
72+ width: units.gu(40)
73+ anchors {
74+ top: parent.top
75+ bottom: parent.bottom
76+ right: parent.right
77+ }
78+
79+ Column {
80+ anchors { fill: parent; margins: units.gu(1) }
81+ spacing: units.gu(1)
82+
83+ Label {
84+ text: "Search query"
85+ }
86+
87+ TextField {
88+ id: searchField
89+ anchors { left: parent.left; right: parent.right }
90+
91+ onTextChanged: scopeView.scope.searchQuery = text
92+
93+ Connections {
94+ target: scopeView.scope
95+ onSearchQueryChanged: searchField.text = scopeView.scope.searchQuery
96+ }
97+ }
98+
99+ Label {
100+ text: "Scope selection"
101+ height: units.gu(4)
102+ verticalAlignment: Text.AlignBottom
103+ }
104+
105+ OptionSelector {
106+ id: scopeSelector
107+ anchors { left: parent.left; right: parent.right }
108+
109+ model: scopes
110+ delegate: OptionSelectorDelegate {
111+ text: model.title
112+ }
113+ }
114+
115+ Label {
116+ text: "Category"
117+ height: units.gu(4)
118+ verticalAlignment: Text.AlignBottom
119+ }
120+
121+ OptionSelector {
122+ id: categorySelector
123+ anchors { left: parent.left; right: parent.right }
124+ model: scopeView.scope ? scopeView.scope.categories : null
125+
126+ property Item selectedItem
127+
128+ delegate: OptionSelectorDelegate {
129+ id: categoryDelegate
130+ text: model.name
131+ property string categoryId: model.categoryId
132+ property string template: JSON.stringify(JSON.parse(model.rawRendererTemplate), null, " ");
133+
134+ onSelectedChanged: if (selected) categorySelector.selectedItem = categoryDelegate
135+ }
136+ }
137+
138+ TextArea {
139+ id: categoryJson
140+ width: parent.width
141+ autoSize: true
142+ readOnly: true
143+ text: categorySelector.selectedItem && categorySelector.selectedItem.template
144+ }
145+
146+ Button {
147+ width: parent.width
148+ text: "Override category"
149+ onClicked: {
150+ PopupUtils.open(categoryEditor)
151+ }
152+ }
153+ }
154+ }
155+
156+ Component {
157+ id: categoryEditor
158+
159+ ComposerSheet {
160+ id: sheet
161+ title: "Editing category definition"
162+
163+ TextArea {
164+ id: categoryEditorArea
165+ anchors.fill: parent
166+ wrapMode: Text.WordWrap
167+ text: categoryJson.text
168+ }
169+
170+ onCancelClicked: PopupUtils.close(sheet)
171+ onConfirmClicked: {
172+ PopupUtils.close(sheet);
173+ scopeView.scope.categories.overrideCategoryJson(categorySelector.selectedItem.categoryId, categoryEditorArea.text);
174+ }
175+ }
176+ }
177+}
qml/Shell.qml−1
@@ -545,7 +545,6 @@ FocusScope {
545545
546546 model: NotificationBackend.Model
547547 margin: units.gu(1)
548− fullHeight: parent.height - panel.panelHeight
549548
550549 anchors {
551550 top: parent.top
src/CMakeLists.txt+12−1
@@ -5,11 +5,22 @@ include_directories(
55 ${UNITY-MIR_INCLUDE_DIRS}
66)
77
8+file(GLOB_RECURSE QML_FILES
9+ ${CMAKE_SOURCE_DIR}/qml/*
10+ ${CMAKE_SOURCE_DIR}/tests/*.qml
11+ ${CMAKE_SOURCE_DIR}/tests/*.js
12+ ${CMAKE_SOURCE_DIR}/tests/*.jpg
13+ ${CMAKE_SOURCE_DIR}/tests/*.png
14+ ${CMAKE_SOURCE_DIR}/tests/*.sci
15+ ${CMAKE_SOURCE_DIR}/tests/*.svg
16+)
17+
818add_executable(${SHELL_APP}
919 ApplicationArguments.h
1020 main.cpp
1121 MouseTouchAdaptor.cpp
12− )
22+ ${QML_FILES} # This is to make qml and image files appear in the IDE's project tree
23+)
1324
1425qt5_use_modules(${SHELL_APP} Gui Qml Quick Test)
1526pkg_check_modules(XCB REQUIRED xcb)
tests/mocks/Unity/fake_scopes.cpp+3
@@ -35,6 +35,7 @@ Scopes::Scopes(QObject *parent)
3535 m_roles[Scopes::RoleScope] = "scope";
3636 m_roles[Scopes::RoleId] = "id";
3737 m_roles[Scopes::RoleVisible] = "visible";
38+ m_roles[Scopes::RoleTitle] = "title";
3839
3940 QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
4041 QObject::connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
@@ -110,6 +111,8 @@ QVariant Scopes::data(const QModelIndex& index, int role) const
110111 return QVariant::fromValue(scope->id());
111112 } else if (role == Scopes::RoleVisible) {
112113 return QVariant::fromValue(scope->visible());
114+ } else if (role == Scopes::RoleTitle) {
115+ return QVariant::fromValue(scope->name());
113116 } else {
114117 return QVariant();
115118 }
tests/mocks/Unity/fake_scopes.h+2−1
@@ -41,7 +41,8 @@ public:
4141 enum Roles {
4242 RoleScope,
4343 RoleId,
44− RoleVisible
44+ RoleVisible,
45+ RoleTitle
4546 };
4647
4748 Q_INVOKABLE int rowCount(const QModelIndex& parent = QModelIndex()) const;
tests/plugins/DashViews/CMakeLists.txt+37−8
@@ -40,15 +40,43 @@ qt5_use_modules(listviewwithpageheadertestsectionexternalmodelExec Test Core Qml
4040target_link_libraries(listviewwithpageheadertestsectionexternalmodelExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
4141add_custom_target(testlistviewwithpageheadersectionexternalmodel ${CMAKE_CURRENT_BINARY_DIR}/listviewwithpageheadertestsectionexternalmodelExec -o ${CMAKE_BINARY_DIR}/testlistviewwithpageheadersectionexternalmodel.xml,xunitxml -o -,txt)
4242
43−add_executable(verticaljournaltestExec verticaljournaltest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/verticaljournal.cpp)
44−qt5_use_modules(verticaljournaltestExec Test Core Qml)
45−target_link_libraries(verticaljournaltestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
46−add_custom_target(testVerticalJournal ${CMAKE_CURRENT_BINARY_DIR}/verticaljournaltestExec -o ${CMAKE_BINARY_DIR}/testVerticalJournal.xml,xunitxml -o -,txt)
43+macro(add_journal_try_test FILENAME)
44+ # Capitalite the first letter for the test/try name targets
45+ string(SUBSTRING ${FILENAME} 0 1 FIRST_LETTER)
46+ string(TOUPPER ${FIRST_LETTER} FIRST_LETTER)
47+ string(REGEX REPLACE "^.(.*)" "${FIRST_LETTER}\\1" CUSTOM_TARGET_NAME "${FILENAME}")
4748
48−add_executable(verticaljournaltryExec verticaljournaltry.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/verticaljournal.cpp)
49−qt5_use_modules(verticaljournaltryExec Test Core Qml)
50−target_link_libraries(verticaljournaltryExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
51−add_custom_target(tryVerticalJournal ${CMAKE_CURRENT_BINARY_DIR}/verticaljournaltryExec -o ${CMAKE_BINARY_DIR}/testVerticalJournal.xml,xunitxml -o -,txt)
49+ add_executable(${FILENAME}testExec
50+ ${FILENAME}test.cpp
51+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
52+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractjournal.cpp
53+ )
54+ qt5_use_modules(${FILENAME}testExec Test Core Qml)
55+ target_link_libraries(${FILENAME}testExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
56+ add_custom_target(test${CUSTOM_TARGET_NAME}
57+ ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME}testExec
58+ -o ${CMAKE_BINARY_DIR}/testVerticalJournal.xml,xunitxml
59+ -o -,txt
60+ DEPENDS ${FILENAME}testExec DashViews-qml
61+ )
62+
63+ add_executable(${FILENAME}tryExec
64+ ${FILENAME}try.cpp
65+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
66+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractjournal.cpp
67+ )
68+ qt5_use_modules(${FILENAME}tryExec Test Core Qml)
69+ target_link_libraries(${FILENAME}tryExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
70+ add_custom_target(try${CUSTOM_TARGET_NAME}
71+ ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME}tryExec
72+ -o ${CMAKE_BINARY_DIR}/testVerticalJournal.xml,xunitxml
73+ -o -,txt
74+ DEPENDS ${FILENAME}tryExec DashViews-qml
75+ )
76+endmacro()
77+
78+add_journal_try_test(verticaljournal)
79+add_journal_try_test(horizontaljournal)
5280
5381set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
5482add_qml_test(. listviewwithpageheaderqml IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
@@ -58,3 +86,4 @@ add_dependencies(qmluitests testlistviewwithpageheadersection)
5886add_dependencies(qmluitests testlistviewwithpageheadersectionexternalmodel)
5987add_dependencies(qmluitests testlistviewwithpageheaderqml)
6088add_dependencies(qmluitests testVerticalJournal)
89+add_dependencies(qmluitests testHorizontalJournal)
tests/plugins/DashViews/horizontaljournaltest.cppnew file+465
@@ -0,0 +1,465 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QQuickView>
18+#include <QtTestGui>
19+#include <QDebug>
20+#include <QGuiApplication>
21+#include <QQuickView>
22+#include <QtQml/qqml.h>
23+#include <QStringListModel>
24+#include <QQmlContext>
25+#include <QQmlEngine>
26+#pragma GCC diagnostic push
27+#pragma GCC diagnostic ignored "-pedantic"
28+#include <private/qquickitem_p.h>
29+#pragma GCC diagnostic pop
30+
31+#include "horizontaljournal.h"
32+
33+class WidthModel : public QAbstractListModel {
34+public:
35+ QHash<int, QByteArray> roleNames() const
36+ {
37+ QHash<int, QByteArray> roles;
38+ roles.insert(Qt::DisplayRole, "modelWidth");
39+ return roles;
40+ }
41+
42+ int rowCount(const QModelIndex & /*parent*/) const
43+ {
44+ return m_list.count();
45+ }
46+
47+ QVariant data(const QModelIndex &index, int /*role*/) const
48+ {
49+ return m_list[index.row()];
50+ }
51+
52+ QStringList stringList() const
53+ {
54+ return m_list;
55+ }
56+
57+ void setStringList(const QStringList &list)
58+ {
59+ beginResetModel();
60+ m_list = list;
61+ endResetModel();
62+ }
63+
64+ void addString(const QString& string)
65+ {
66+ beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
67+ m_list << string;
68+ endInsertRows();
69+ }
70+
71+ void removeLast()
72+ {
73+ beginRemoveRows(QModelIndex(), m_list.count() - 1, m_list.count() - 1);
74+ m_list.takeLast();
75+ endRemoveRows();
76+ }
77+
78+private:
79+ QStringList m_list;
80+};
81+
82+class HorizontalJournalTest : public QObject
83+{
84+ Q_OBJECT
85+
86+private:
87+ void verifyItem(const QQuickItem *item, int modelIndex, qreal x, qreal y, bool visible)
88+ {
89+ QTRY_COMPARE(item->x(), x);
90+ QTRY_COMPARE(item->y(), y);
91+ QTRY_COMPARE(item->width(), model->stringList()[modelIndex].toDouble());
92+ QTRY_COMPARE(QQuickItemPrivate::get(item)->culled, !visible);
93+ }
94+
95+ void checkInitialPositions()
96+ {
97+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
98+ QTRY_COMPARE(hj->m_visibleItems.count(), 14);
99+ verifyItem(hj->m_visibleItems[ 0], 0, 0, 0, true);
100+ verifyItem(hj->m_visibleItems[ 1], 1, 110, 0, true);
101+ verifyItem(hj->m_visibleItems[ 2], 2, 170, 0, true);
102+ verifyItem(hj->m_visibleItems[ 3], 3, 305, 0, true);
103+ verifyItem(hj->m_visibleItems[ 4], 4, 325, 0, true);
104+ verifyItem(hj->m_visibleItems[ 5], 5, 375, 0, true);
105+ verifyItem(hj->m_visibleItems[ 6], 6, 0, 160, true);
106+ verifyItem(hj->m_visibleItems[ 7], 7, 210, 160, true);
107+ verifyItem(hj->m_visibleItems[ 8], 8, 0, 320, true);
108+ verifyItem(hj->m_visibleItems[ 9], 9, 170, 320, true);
109+ verifyItem(hj->m_visibleItems[10], 10, 200, 320, true);
110+ verifyItem(hj->m_visibleItems[11], 11, 230, 320, true);
111+ verifyItem(hj->m_visibleItems[12], 12, 305, 320, true);
112+ verifyItem(hj->m_visibleItems[13], 13, 0, 480, false);
113+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 4);
114+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 5], 375.);
115+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 7], 210.);
116+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[12], 305.);
117+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[13], 0.);
118+ QCOMPARE(hj->implicitHeight(), 900.);
119+ }
120+
121+private Q_SLOTS:
122+ void init()
123+ {
124+ view = new QQuickView();
125+ view->setResizeMode(QQuickView::SizeRootObjectToView);
126+ view->engine()->addImportPath(BUILT_PLUGINS_DIR);
127+
128+ model = new WidthModel();
129+ QStringList heightList;
130+ heightList << "100" << "50" << "125" << "10" << "40" << "70" << "200" << "110" << "160" << "20" << "20" << "65" << "80" << "200" << "300" << "130" << "400" << "300" << "500" << "10";
131+ model->setStringList(heightList);
132+
133+ view->setSource(QUrl::fromLocalFile(DASHVIEWSTEST_FOLDER "/horizontaljournaltest.qml"));
134+
135+ view->show();
136+ view->resize(470, 400);
137+ QTest::qWaitForWindowExposed(view);
138+
139+ hj = dynamic_cast<HorizontalJournal*>(view->rootObject()->findChild<QObject*>("hj"));
140+ hj->setModel(model);
141+
142+ checkInitialPositions();
143+ }
144+
145+ void cleanup()
146+ {
147+ delete view;
148+ delete model;
149+ }
150+
151+ void testWidthResize()
152+ {
153+ view->resize(629, 400);
154+ QTRY_COMPARE(hj->width(), 629.);
155+
156+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
157+ QTRY_COMPARE(hj->m_visibleItems.count(), 17);
158+ verifyItem(hj->m_visibleItems[ 0], 0, 0, 0, true);
159+ verifyItem(hj->m_visibleItems[ 1], 1, 110, 0, true);
160+ verifyItem(hj->m_visibleItems[ 2], 2, 170, 0, true);
161+ verifyItem(hj->m_visibleItems[ 3], 3, 305, 0, true);
162+ verifyItem(hj->m_visibleItems[ 4], 4, 325, 0, true);
163+ verifyItem(hj->m_visibleItems[ 5], 5, 375, 0, true);
164+ verifyItem(hj->m_visibleItems[ 6], 6, 0, 160, true);
165+ verifyItem(hj->m_visibleItems[ 7], 7, 210, 160, true);
166+ verifyItem(hj->m_visibleItems[ 8], 8, 330, 160, true);
167+ verifyItem(hj->m_visibleItems[ 9], 9, 500, 160, true);
168+ verifyItem(hj->m_visibleItems[10], 10, 530, 160, true);
169+ verifyItem(hj->m_visibleItems[11], 11, 560, 160, true);
170+ verifyItem(hj->m_visibleItems[12], 12, 0, 320, true);
171+ verifyItem(hj->m_visibleItems[13], 13, 90, 320, true);
172+ verifyItem(hj->m_visibleItems[14], 14, 300, 320, true);
173+ verifyItem(hj->m_visibleItems[15], 15, 0, 480, false);
174+ verifyItem(hj->m_visibleItems[16], 16, 140, 480, false);
175+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 4);
176+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 5], 375.);
177+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[11], 560.);
178+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[14], 300.);
179+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[16], 140.);
180+ QCOMPARE(hj->implicitHeight(), 630. + 3. * 630. / 17.);
181+
182+ view->resize(470, 400);
183+
184+ // This is exactly the same block as the first again
185+ checkInitialPositions();
186+ }
187+
188+ void testColumnSpacing()
189+ {
190+ hj->setColumnSpacing(11);
191+
192+ QTRY_COMPARE(hj->m_needsRelayout, false);
193+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
194+ QTRY_COMPARE(hj->m_visibleItems.count(), 14);
195+ verifyItem(hj->m_visibleItems[ 0], 0, 0, 0, true);
196+ verifyItem(hj->m_visibleItems[ 1], 1, 111, 0, true);
197+ verifyItem(hj->m_visibleItems[ 2], 2, 172, 0, true);
198+ verifyItem(hj->m_visibleItems[ 3], 3, 308, 0, true);
199+ verifyItem(hj->m_visibleItems[ 4], 4, 329, 0, true);
200+ verifyItem(hj->m_visibleItems[ 5], 5, 380, 0, true);
201+ verifyItem(hj->m_visibleItems[ 6], 6, 0, 160, true);
202+ verifyItem(hj->m_visibleItems[ 7], 7, 211, 160, true);
203+ verifyItem(hj->m_visibleItems[ 8], 8, 0, 320, true);
204+ verifyItem(hj->m_visibleItems[ 9], 9, 171, 320, true);
205+ verifyItem(hj->m_visibleItems[10], 10, 202, 320, true);
206+ verifyItem(hj->m_visibleItems[11], 11, 233, 320, true);
207+ verifyItem(hj->m_visibleItems[12], 12, 309, 320, true);
208+ verifyItem(hj->m_visibleItems[13], 13, 0, 480, false);
209+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 4);
210+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 5], 380.);
211+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 7], 211.);
212+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[12], 309.);
213+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[13], 0.);
214+ QCOMPARE(hj->implicitHeight(), 900.);
215+
216+ hj->setColumnSpacing(10);
217+ QTRY_COMPARE(hj->m_needsRelayout, false);
218+
219+ // This is exactly the same block as the first again
220+ checkInitialPositions();
221+ }
222+
223+ void testRowSpacing()
224+ {
225+ hj->setRowSpacing(11);
226+
227+ QTRY_COMPARE(hj->m_needsRelayout, false);
228+ QTRY_COMPARE(hj->m_needsRelayout, false);
229+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
230+ QTRY_COMPARE(hj->m_visibleItems.count(), 14);
231+ verifyItem(hj->m_visibleItems[ 0], 0, 0, 0, true);
232+ verifyItem(hj->m_visibleItems[ 1], 1, 110, 0, true);
233+ verifyItem(hj->m_visibleItems[ 2], 2, 170, 0, true);
234+ verifyItem(hj->m_visibleItems[ 3], 3, 305, 0, true);
235+ verifyItem(hj->m_visibleItems[ 4], 4, 325, 0, true);
236+ verifyItem(hj->m_visibleItems[ 5], 5, 375, 0, true);
237+ verifyItem(hj->m_visibleItems[ 6], 6, 0, 161, true);
238+ verifyItem(hj->m_visibleItems[ 7], 7, 210, 161, true);
239+ verifyItem(hj->m_visibleItems[ 8], 8, 0, 322, true);
240+ verifyItem(hj->m_visibleItems[ 9], 9, 170, 322, true);
241+ verifyItem(hj->m_visibleItems[10], 10, 200, 322, true);
242+ verifyItem(hj->m_visibleItems[11], 11, 230, 322, true);
243+ verifyItem(hj->m_visibleItems[12], 12, 305, 322, true);
244+ verifyItem(hj->m_visibleItems[13], 13, 0, 483, false);
245+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 4);
246+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 5], 375.);
247+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 7], 210.);
248+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[12], 305.);
249+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[13], 0.);
250+ QCOMPARE(hj->implicitHeight(), 633. + 6. * 633. / 14.);
251+
252+ hj->setRowSpacing(10);
253+ QTRY_COMPARE(hj->m_needsRelayout, false);
254+
255+ // This is exactly the same block as the first again
256+ checkInitialPositions();
257+ }
258+
259+ void testDelegateCreationRanges()
260+ {
261+ hj->setDelegateCreationBegin(300);
262+ hj->setDelegateCreationEnd(view->height());
263+
264+ QTRY_COMPARE(hj->m_firstVisibleIndex, 6);
265+ QTRY_COMPARE(hj->m_visibleItems.count(), 7);
266+ verifyItem(hj->m_visibleItems[0], 6, 0, 160, true);
267+ verifyItem(hj->m_visibleItems[1], 7, 210, 160, true);
268+ verifyItem(hj->m_visibleItems[2], 8, 0, 320, true);
269+ verifyItem(hj->m_visibleItems[3], 9, 170, 320, true);
270+ verifyItem(hj->m_visibleItems[4], 10, 200, 320, true);
271+ verifyItem(hj->m_visibleItems[5], 11, 230, 320, true);
272+ verifyItem(hj->m_visibleItems[6], 12, 305, 320, true);
273+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 5], 375.);
274+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 7], 210.);
275+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[12], 305.);
276+
277+ hj->resetDelegateCreationBegin();
278+ hj->resetDelegateCreationEnd();
279+
280+ // This is exactly the same block as the first again
281+ checkInitialPositions();
282+ }
283+
284+
285+ void testRowHeightChange()
286+ {
287+ hj->setRowHeight(200);
288+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
289+ QTRY_COMPARE(hj->m_visibleItems.count(), 13);
290+ verifyItem(hj->m_visibleItems[ 0], 0, 0, 0, true);
291+ verifyItem(hj->m_visibleItems[ 1], 1, 110, 0, true);
292+ verifyItem(hj->m_visibleItems[ 2], 2, 170, 0, true);
293+ verifyItem(hj->m_visibleItems[ 3], 3, 305, 0, true);
294+ verifyItem(hj->m_visibleItems[ 4], 4, 325, 0, true);
295+ verifyItem(hj->m_visibleItems[ 5], 5, 375, 0, true);
296+ verifyItem(hj->m_visibleItems[ 6], 6, 0, 210, true);
297+ verifyItem(hj->m_visibleItems[ 7], 7, 210, 210, true);
298+ verifyItem(hj->m_visibleItems[ 8], 8, 0, 420, false);
299+ verifyItem(hj->m_visibleItems[ 9], 9, 170, 420, false);
300+ verifyItem(hj->m_visibleItems[10], 10, 200, 420, false);
301+ verifyItem(hj->m_visibleItems[11], 11, 230, 420, false);
302+ verifyItem(hj->m_visibleItems[12], 12, 305, 420, false);
303+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 3);
304+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 5], 375.);
305+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 7], 210.);
306+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[12], 305.);
307+ QCOMPARE(hj->implicitHeight(), 620. + 7. * 620. / 13.);
308+
309+ hj->setRowHeight(150);
310+
311+ // This is exactly the same block as the first again
312+ checkInitialPositions();
313+ }
314+
315+ void testChangeModel()
316+ {
317+ WidthModel *model2 = new WidthModel();
318+ QStringList list2;
319+ list2 << "100" << "50" << "125" << "25" << "50" << "50";
320+ model2->setStringList(list2);
321+ hj->setModel(model2);
322+ delete model;
323+ model = model2;
324+
325+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
326+ QTRY_COMPARE(hj->m_visibleItems.count(), 6);
327+ verifyItem(hj->m_visibleItems[ 0], 0, 0, 0, true);
328+ verifyItem(hj->m_visibleItems[ 1], 1, 110, 0, true);
329+ verifyItem(hj->m_visibleItems[ 2], 2, 170, 0, true);
330+ verifyItem(hj->m_visibleItems[ 3], 3, 305, 0, true);
331+ verifyItem(hj->m_visibleItems[ 4], 4, 340, 0, true);
332+ verifyItem(hj->m_visibleItems[ 5], 5, 400, 0, true);
333+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 0);
334+ QCOMPARE(hj->implicitHeight(), 150.);
335+ }
336+
337+ void testModelReset()
338+ {
339+ QStringList heightList;
340+ heightList << "100" << "50" << "125" << "25" << "50" << "50";
341+ model->setStringList(heightList);
342+
343+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
344+ QTRY_COMPARE(hj->m_visibleItems.count(), 6);
345+ verifyItem(hj->m_visibleItems[ 0], 0, 0, 0, true);
346+ verifyItem(hj->m_visibleItems[ 1], 1, 110, 0, true);
347+ verifyItem(hj->m_visibleItems[ 2], 2, 170, 0, true);
348+ verifyItem(hj->m_visibleItems[ 3], 3, 305, 0, true);
349+ verifyItem(hj->m_visibleItems[ 4], 4, 340, 0, true);
350+ verifyItem(hj->m_visibleItems[ 5], 5, 400, 0, true);
351+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 0);
352+ QCOMPARE(hj->implicitHeight(), 150.);
353+ }
354+
355+ void testModelRemoveLastNonVisible()
356+ {
357+ model->removeLast();
358+
359+ // This is the same than checkInitialPositions but
360+ // with a different implicitHeight since there's an item less
361+ // in the model
362+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
363+ QTRY_COMPARE(hj->m_visibleItems.count(), 14);
364+ verifyItem(hj->m_visibleItems[ 0], 0, 0, 0, true);
365+ verifyItem(hj->m_visibleItems[ 1], 1, 110, 0, true);
366+ verifyItem(hj->m_visibleItems[ 2], 2, 170, 0, true);
367+ verifyItem(hj->m_visibleItems[ 3], 3, 305, 0, true);
368+ verifyItem(hj->m_visibleItems[ 4], 4, 325, 0, true);
369+ verifyItem(hj->m_visibleItems[ 5], 5, 375, 0, true);
370+ verifyItem(hj->m_visibleItems[ 6], 6, 0, 160, true);
371+ verifyItem(hj->m_visibleItems[ 7], 7, 210, 160, true);
372+ verifyItem(hj->m_visibleItems[ 8], 8, 0, 320, true);
373+ verifyItem(hj->m_visibleItems[ 9], 9, 170, 320, true);
374+ verifyItem(hj->m_visibleItems[10], 10, 200, 320, true);
375+ verifyItem(hj->m_visibleItems[11], 11, 230, 320, true);
376+ verifyItem(hj->m_visibleItems[12], 12, 305, 320, true);
377+ verifyItem(hj->m_visibleItems[13], 13, 0, 480, false);
378+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 4);
379+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 5], 375.);
380+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[ 7], 210.);
381+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[12], 305.);
382+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[13], 0.);
383+ QTRY_COMPARE(hj->implicitHeight(), 855.);
384+ }
385+
386+ void testModelAppendRemoveLast()
387+ {
388+ WidthModel *model2 = new WidthModel();
389+ QStringList heightList;
390+ heightList << "100" << "50" << "125" << "25" << "50" << "50";
391+ model2->setStringList(heightList);
392+ hj->setModel(model2);
393+ delete model;
394+ model = model2;
395+
396+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
397+ QTRY_COMPARE(hj->m_visibleItems.count(), 6);
398+ verifyItem(hj->m_visibleItems[0], 0, 0, 0, true);
399+ verifyItem(hj->m_visibleItems[1], 1, 110, 0, true);
400+ verifyItem(hj->m_visibleItems[2], 2, 170, 0, true);
401+ verifyItem(hj->m_visibleItems[3], 3, 305, 0, true);
402+ verifyItem(hj->m_visibleItems[4], 4, 340, 0, true);
403+ verifyItem(hj->m_visibleItems[5], 5, 400, 0, true);
404+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 0);
405+ QCOMPARE(hj->implicitHeight(), 150.);
406+
407+ model2->addString("75");
408+
409+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
410+ QTRY_COMPARE(hj->m_visibleItems.count(), 7);
411+ verifyItem(hj->m_visibleItems[0], 0, 0, 0, true);
412+ verifyItem(hj->m_visibleItems[1], 1, 110, 0, true);
413+ verifyItem(hj->m_visibleItems[2], 2, 170, 0, true);
414+ verifyItem(hj->m_visibleItems[3], 3, 305, 0, true);
415+ verifyItem(hj->m_visibleItems[4], 4, 340, 0, true);
416+ verifyItem(hj->m_visibleItems[5], 5, 400, 0, true);
417+ verifyItem(hj->m_visibleItems[6], 6, 0, 160, true);
418+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 1);
419+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[5], 400.);
420+ QCOMPARE(hj->implicitHeight(), 310.);
421+
422+ model2->addString("50");
423+ model2->addString("50");
424+
425+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
426+ QTRY_COMPARE(hj->m_visibleItems.count(), 9);
427+ verifyItem(hj->m_visibleItems[0], 0, 0, 0, true);
428+ verifyItem(hj->m_visibleItems[1], 1, 110, 0, true);
429+ verifyItem(hj->m_visibleItems[2], 2, 170, 0, true);
430+ verifyItem(hj->m_visibleItems[3], 3, 305, 0, true);
431+ verifyItem(hj->m_visibleItems[4], 4, 340, 0, true);
432+ verifyItem(hj->m_visibleItems[5], 5, 400, 0, true);
433+ verifyItem(hj->m_visibleItems[6], 6, 0, 160, true);
434+ verifyItem(hj->m_visibleItems[7], 7, 85, 160, true);
435+ verifyItem(hj->m_visibleItems[8], 8, 145, 160, true);
436+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 1);
437+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[5], 400.);
438+ QCOMPARE(hj->implicitHeight(), 310.);
439+
440+ model2->removeLast();
441+
442+ QTRY_COMPARE(hj->m_firstVisibleIndex, 0);
443+ QTRY_COMPARE(hj->m_visibleItems.count(), 8);
444+ verifyItem(hj->m_visibleItems[0], 0, 0, 0, true);
445+ verifyItem(hj->m_visibleItems[1], 1, 110, 0, true);
446+ verifyItem(hj->m_visibleItems[2], 2, 170, 0, true);
447+ verifyItem(hj->m_visibleItems[3], 3, 305, 0, true);
448+ verifyItem(hj->m_visibleItems[4], 4, 340, 0, true);
449+ verifyItem(hj->m_visibleItems[5], 5, 400, 0, true);
450+ verifyItem(hj->m_visibleItems[6], 6, 0, 160, true);
451+ verifyItem(hj->m_visibleItems[7], 7, 85, 160, true);
452+ QTRY_COMPARE(hj->m_lastInRowIndexPosition.count(), 1);
453+ QTRY_COMPARE(hj->m_lastInRowIndexPosition[5], 400.);
454+ QCOMPARE(hj->implicitHeight(), 310.);
455+ }
456+
457+private:
458+ QQuickView *view;
459+ HorizontalJournal *hj;
460+ WidthModel *model;
461+};
462+
463+QTEST_MAIN(HorizontalJournalTest)
464+
465+#include "horizontaljournaltest.moc"
tests/plugins/DashViews/horizontaljournaltest.qmlnew file+43
@@ -0,0 +1,43 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.1
18+import DashViews 0.1
19+
20+Item {
21+
22+ HorizontalJournal {
23+ objectName: "hj"
24+ anchors.fill: parent
25+ rowHeight: 150
26+ columnSpacing: 10
27+ rowSpacing: 10
28+
29+ delegate: Rectangle {
30+ property real randomValue: Math.random()
31+ height: 150
32+ color: randomValue < 0.3 ? "green" : randomValue < 0.6 ? "blue" : "red";
33+ width: modelWidth
34+ border.width: 3
35+
36+ Text {
37+ text: index + "\nx: " + parent.x + "\nwidth: " + parent.width
38+ x: 10
39+ y: 10
40+ }
41+ }
42+ }
43+}
tests/plugins/DashViews/horizontaljournaltry.cppnew file+90
@@ -0,0 +1,90 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QQuickView>
18+#include <QDebug>
19+#include <QGuiApplication>
20+#include <QQuickView>
21+#include <QtQml/qqml.h>
22+#include <QStringListModel>
23+#include <QQmlContext>
24+#include <QQmlEngine>
25+#include <QGuiApplication>
26+
27+#include "horizontaljournal.h"
28+
29+class QWidthModela : public QStringListModel {
30+ Q_OBJECT
31+public:
32+ QHash<int, QByteArray> roleNames() const
33+ {
34+ QHash<int, QByteArray> roles;
35+ roles.insert(Qt::DisplayRole, "modelWidth");
36+ return roles;
37+ }
38+
39+public slots:
40+ void add(int height)
41+ {
42+ if (height <= 0) {
43+ height = 50 + 50. * qrand() / RAND_MAX ;
44+ }
45+ QStringList sl = stringList();
46+ sl << QString::number(height);
47+ setStringList(sl);
48+ }
49+
50+ void remove()
51+ {
52+ QStringList sl = stringList();
53+ if (!sl.isEmpty()) {
54+ sl.removeLast();
55+ setStringList(sl);
56+ }
57+ }
58+};
59+
60+int main(int argc, char *argv[])
61+{
62+ QGuiApplication a(argc, argv);
63+
64+ QQuickView *view = new QQuickView();
65+ view->setResizeMode(QQuickView::SizeRootObjectToView);
66+ view->engine()->addImportPath(BUILT_PLUGINS_DIR);
67+
68+ QWidthModela model;
69+ QStringList heightList;
70+ heightList << "100" << "50" << "125";
71+ model.setStringList(heightList);
72+
73+ QStringListModel listModel;
74+ listModel.setStringList(QStringList() << QString());
75+
76+ view->rootContext()->setContextProperty("listModel", &listModel);
77+ view->rootContext()->setContextProperty("vjModel", &model);
78+
79+ view->setSource(QUrl::fromLocalFile(DASHVIEWSTEST_FOLDER "/horizontaljournaltry.qml"));
80+
81+ view->show();
82+ view->resize(530, 400);
83+
84+ QObject::connect(view->rootObject(), SIGNAL(add(int)), &model, SLOT(add(int)));
85+ QObject::connect(view->rootObject(), SIGNAL(remove()), &model, SLOT(remove()));
86+
87+ return a.exec();
88+}
89+
90+#include "horizontaljournaltry.moc"
tests/plugins/DashViews/horizontaljournaltry.qmlnew file+99
@@ -0,0 +1,99 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.1
18+import DashViews 0.1
19+
20+Item {
21+ id: root
22+ signal add(int width)
23+ signal remove()
24+
25+ ListViewWithPageHeader {
26+ id: lvwph
27+ model: listModel
28+ height: parent.height
29+ width: parent.width - controls.width
30+
31+ delegate: HorizontalJournal {
32+ model: vjModel
33+ rowHeight: 100
34+ columnSpacing: 10
35+ rowSpacing: 10
36+ width: parent.width
37+ height: implicitHeight > 100 ? implicitHeight : 100
38+ delegateCreationBegin: lvwph.contentY
39+ delegateCreationEnd: lvwph.contentY + lvwph.height
40+
41+ delegate: Rectangle {
42+ height: 100
43+ color: "red";
44+ width: modelWidth
45+ border.width: 3
46+
47+ Text {
48+ text: index + "\nx: " + parent.x + "\nwidth: " + parent.width
49+ x: 10
50+ y: 10
51+ }
52+ }
53+ }
54+ }
55+
56+ Rectangle {
57+ id: controls
58+ height: parent.height
59+ width: 100
60+ anchors.right: parent.right
61+ color: "gray"
62+ opacity: 0.4
63+
64+ Rectangle {
65+ id: addButton
66+ height: 50
67+ width: parent.width
68+ color: "blue"
69+ Text {
70+ text: "Add"
71+ anchors.centerIn: parent
72+ }
73+ MouseArea {
74+ anchors.fill: parent
75+ onClicked: root.add(addField.text)
76+ }
77+ }
78+ TextInput {
79+ id: addField
80+ anchors.top: addButton.bottom
81+ height: 30
82+ width: parent.width
83+ }
84+ Rectangle {
85+ anchors.top: addField.bottom
86+ height: 50
87+ width: parent.width
88+ color: "red"
89+ Text {
90+ text: "Remove"
91+ anchors.centerIn: parent
92+ }
93+ MouseArea {
94+ anchors.fill: parent
95+ onClicked: root.remove()
96+ }
97+ }
98+ }
99+}
tests/plugins/DashViews/verticaljournaltest.cpp+37−5
@@ -30,7 +30,7 @@
3030
3131#include "verticaljournal.h"
3232
33−class QHeightModel : public QAbstractListModel {
33+class HeightModel : public QAbstractListModel {
3434public:
3535 QHash<int, QByteArray> roleNames() const
3636 {
@@ -127,7 +127,7 @@ private Q_SLOTS:
127127 view->setResizeMode(QQuickView::SizeRootObjectToView);
128128 view->engine()->addImportPath(BUILT_PLUGINS_DIR);
129129
130− model = new QHeightModel();
130+ model = new HeightModel();
131131 QStringList heightList;
132132 heightList << "100" << "50" << "125" << "10" << "40" << "70" << "200" << "110" << "160" << "20" << "20" << "65" << "80" << "200" << "300" << "130" << "400" << "300" << "500" << "10";
133133 model->setStringList(heightList);
@@ -323,7 +323,7 @@ private Q_SLOTS:
323323
324324 void testChangeModel()
325325 {
326− QHeightModel *model2 = new QHeightModel();
326+ HeightModel *model2 = new HeightModel();
327327 QStringList list2;
328328 list2 << "100" << "50" << "25" << "25" << "50" << "50";
329329 model2->setStringList(list2);
@@ -359,9 +359,41 @@ private Q_SLOTS:
359359 verifyItem(vj->m_columnVisibleItems[1][1], 3, 160, 60, true);
360360 }
361361
362+ void testModelRemoveLastNonVisible()
363+ {
364+ model->removeLast();
365+
366+ // This is the same as checkInitialPositions but
367+ // with a different implicitHeight since there's an item less
368+ // in the model
369+ QTRY_COMPARE(vj->m_columnVisibleItems.count(), 3);
370+ QTRY_COMPARE(vj->m_columnVisibleItems[0].count(), 5);
371+ QTRY_COMPARE(vj->m_columnVisibleItems[1].count(), 7);
372+ QTRY_COMPARE(vj->m_columnVisibleItems[2].count(), 6);
373+ verifyItem(vj->m_columnVisibleItems[0][0], 0, 0, 0, true);
374+ verifyItem(vj->m_columnVisibleItems[1][0], 1, 160, 0, true);
375+ verifyItem(vj->m_columnVisibleItems[2][0], 2, 320, 0, true);
376+ verifyItem(vj->m_columnVisibleItems[1][1], 3, 160, 60, true);
377+ verifyItem(vj->m_columnVisibleItems[1][2], 4, 160, 80, true);
378+ verifyItem(vj->m_columnVisibleItems[0][1], 5, 0, 110, true);
379+ verifyItem(vj->m_columnVisibleItems[1][3], 6, 160, 130, true);
380+ verifyItem(vj->m_columnVisibleItems[2][1], 7, 320, 135, true);
381+ verifyItem(vj->m_columnVisibleItems[0][2], 8, 0, 190, true);
382+ verifyItem(vj->m_columnVisibleItems[2][2], 9, 320, 255, true);
383+ verifyItem(vj->m_columnVisibleItems[2][3], 10, 320, 285, true);
384+ verifyItem(vj->m_columnVisibleItems[2][4], 11, 320, 315, true);
385+ verifyItem(vj->m_columnVisibleItems[1][4], 12, 160, 340, true);
386+ verifyItem(vj->m_columnVisibleItems[0][3], 13, 0, 360, true);
387+ verifyItem(vj->m_columnVisibleItems[2][5], 14, 320, 390, true);
388+ verifyItem(vj->m_columnVisibleItems[1][5], 15, 160, 430, false);
389+ verifyItem(vj->m_columnVisibleItems[0][4], 16, 0, 570, false);
390+ verifyItem(vj->m_columnVisibleItems[1][6], 17, 160, 570, false);
391+ QTRY_COMPARE(vj->implicitHeight(), 970. + 1. * 970. / 18.);
392+ }
393+
362394 void testModelAppendRemoveLast()
363395 {
364− QHeightModel *model2 = new QHeightModel();
396+ HeightModel *model2 = new HeightModel();
365397 QStringList list2;
366398 list2 << "100" << "50" << "25" << "25" << "50" << "50";
367399 model2->setStringList(list2);
@@ -430,7 +462,7 @@ private Q_SLOTS:
430462private:
431463 QQuickView *view;
432464 VerticalJournal *vj;
433− QHeightModel *model;
465+ HeightModel *model;
434466};
435467
436468QTEST_MAIN(VerticalJournalTest)
tests/qmltests/CMakeLists.txt+1−2
@@ -37,9 +37,8 @@ add_qml_test(Components Stage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
3737 ${CMAKE_BINARY_DIR}/tests/mocks
3838 ${CMAKE_BINARY_DIR}/plugins)
3939add_qml_test(Components Tile)
40−add_qml_test(Components PageHeader)
40+add_qml_test(Components PageHeaderLabel)
4141add_qml_test(Dash Dash IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
42−add_qml_test(Dash DashBar IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
4342add_qml_test(Dash DashContent IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
4443add_qml_test(Dash DashPreview)
4544add_qml_test(Dash GenericPreview)
tests/qmltests/Components/tst_PageHeader.qml → tests/qmltests/Components/tst_PageHeaderLabel.qmlrenamed+2−2
@@ -31,7 +31,7 @@ Item {
3131 }
3232
3333 UT.UnityTestCase {
34− name: "PageHeaderTest"
34+ name: "PageHeaderLabelTest"
3535 when: windowShown
3636
3737 property alias searchEnabled : pageHeader.searchEntryEnabled
@@ -204,7 +204,7 @@ Item {
204204 anchors.fill: parent
205205 spacing: units.gu(1)
206206
207− PageHeader {
207+ PageHeaderLabel {
208208 id: pageHeader
209209 anchors {
210210 left: parent.left
tests/qmltests/Dash/qml/FakeScopeView.qml+1
@@ -23,6 +23,7 @@ GenericScopeView {
2323 id: fakeScopeView
2424
2525 property alias backColor : back.color
26+ property var categoryView: null
2627
2728 onEndReached: {
2829 if (scope != undefined && shell != undefined && shell.scopeStatus != undefined) {
tests/qmltests/Dash/tst_Dash.qml+6−8
@@ -25,8 +25,6 @@ Item {
2525 width: units.gu(40)
2626 height: units.gu(80)
2727
28− property ListModel searchHistory : ListModel {}
29−
3028 Dash {
3129 id: dash
3230 anchors.fill: parent
@@ -83,14 +81,14 @@ Item {
8381 // wait for scopes to load
8482 tryCompare(scopes, "loaded", true);
8583
86− var dashbar = findChild(dash, "dashbar");
87− verify(dashbar != undefined)
84+ var tabbar = findChild(dash, "tabbar");
85+ verify(tabbar != undefined)
8886 var dashContent = findChild(dash, "dashContent");
8987 var current_index = dashContent.currentIndex;
9088
9189 dash.setCurrentScope(data.tag, true /* animate */, false /* reset */);
9290 compare(dashContent.currentIndex, data.shouldBeVisible ? data.visualIndex : current_index);
93− compare(dashbar.currentIndex, data.shouldBeVisible ? data.visualIndex : current_index);
91+ compare(tabbar.selectedIndex, data.shouldBeVisible ? data.visualIndex : current_index);
9492 }
9593
9694 function test_show_scope_on_load_data() {
@@ -127,12 +125,12 @@ Item {
127125 // wait for scopes to load
128126 tryCompare(scopes, "loaded", true);
129127
130− var dashbar = findChild(dash, "dashbar");
131− verify(dashbar != undefined)
128+ var tabbar = findChild(dash, "tabbar");
129+ verify(tabbar != undefined)
132130 var dashContent = findChild(dash, "dashContent");
133131 var current_index = dashContent.currentIndex;
134132
135− dashbar.itemSelected(data.visualIndex);
133+ tabbar.selectedIndex = data.visualIndex;
136134 compare(dashContent.currentIndex, data.shouldBeVisible ? data.visualIndex : current_index);
137135 }
138136 }
tests/qmltests/Dash/tst_DashBar.qmldeleted−111
@@ -1,111 +0,0 @@
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 General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import QtTest 1.0
19−import Unity 0.1
20−import ".."
21−import "../../../qml/Dash"
22−import Ubuntu.Components 0.1
23−import Unity.Test 0.1 as UT
24−
25−Item {
26− width: units.gu(40)
27− height: units.gu(71)
28−
29− DashBar {
30− id: dashBar
31−
32− anchors {
33− left: parent.left
34− right: parent.right
35− bottom: parent.bottom
36− }
37− model: scopes
38− onItemSelected: currentIndex = index
39− }
40−
41− Scopes {
42− id: scopes
43− }
44−
45− SignalSpy {
46− id: signalSpy
47− signalName: "itemSelected"
48− target: dashBar
49− }
50−
51− UT.UnityTestCase {
52− name: "DashBar"
53− when: scopes.loaded
54−
55− property alias model: dashBar.model
56− property alias currentIndex: dashBar.currentIndex
57−
58− readonly property alias lineHeight: dashBar.lineHeight
59− readonly property alias itemSize: dashBar.itemSize
60− readonly property alias iconSize: dashBar.iconSize
61− readonly property var panel: findChild(dashBar, "panel");
62−
63− function initTestCase() {
64− currentIndex = 2
65− }
66−
67− function waitForAnimationToEnd() {
68− compare(panel.animating, true) // check the animation started
69− tryCompare(panel, "animating", false) // wait till the animation ends
70− }
71−
72− function closePanel() {
73− dashBar.finishNavigation()
74− waitForAnimationToEnd()
75− tryCompare(panel, "opened", false)
76− }
77−
78− function openPanel() {
79− dashBar.startNavigation()
80− waitForAnimationToEnd()
81− tryCompare(panel, "opened", true)
82− }
83−
84− function test_navigationAndHide() {
85− openPanel()
86− closePanel()
87− }
88−
89− function test_itemSelected() {
90− openPanel()
91−
92− var row = findChild(dashBar, "row");
93− tryCompareFunction(function(){return row.width > 0;}, true);
94− for (var i = 0; i < model.rowCount(); i++) {
95− // coordinate x in the middle of item with index 'i'
96− var x = row.x + (row.width / model.rowCount()) * i + itemSize / 2
97−
98− // FIXME workaround for a bug in SignalSpy
99− signalSpy.clear()
100−
101− // (item, x, y, button, modifiers, delay)
102− mouseClick(panel, x, row.height / 2, Qt.LeftButton, Qt.NoModifier, 0)
103− compare(signalSpy.count > 0, true, "signal itemSelected not triggered")
104− compare(signalSpy.signalArguments[0][0], i, "signal itemSelected emitted unexpected index");
105− tryCompare(dashBar, "currentIndex", i);
106− }
107−
108− closePanel()
109− }
110− }
111−}
tests/qmltests/Dash/tst_DashContent.qml+67−4
@@ -17,9 +17,11 @@
1717import QtQuick 2.0
1818import QtTest 1.0
1919import "../../../qml/Dash"
20+import "../../../qml/Components"
2021import Ubuntu.Components 0.1
2122import Unity 0.1
2223import Unity.Test 0.1 as UT
24+import Utils 0.1
2325
2426Item {
2527 id: shell
@@ -54,11 +56,13 @@ Item {
5456 id: dashContent
5557 anchors.fill: parent
5658
57− model: scopesModel
59+ model: SortFilterProxyModel {
60+ model: scopesModel
61+ }
5862 scopes : scopesModel
5963
6064 scopeMapper : scopeDelegateMapper
61− searchHistory: ListModel {}
65+ searchHistory: SearchHistoryModel {}
6266 }
6367
6468 ScopeDelegateMapper {
@@ -109,9 +113,10 @@ Item {
109113
110114 UT.UnityTestCase {
111115 name: "DashContent"
112− when: windowShown
116+ when: scopesModel.loaded
113117
114118 function init() {
119+ scopesModel.clear();
115120 scopesModel.load();
116121 }
117122
@@ -268,6 +273,65 @@ Item {
268273 tryCompare(scopesModel.get(2), "isActive", data.active2);
269274 }
270275
276+ function doFindMusicButton(parent) {
277+ for (var i = 0; i < parent.children.length; i++) {
278+ var c = parent.children[i];
279+ if (UT.Util.isInstanceOf(c, "AbstractButton") && parent.x >= 0) {
280+ for (var ii = 0; ii < c.children.length; ii++) {
281+ var cc = c.children[ii];
282+ if (UT.Util.isInstanceOf(cc, "Label") && cc.text == "Music") {
283+ return c;
284+ }
285+ }
286+ }
287+ var r = doFindMusicButton(c);
288+ if (r !== undefined) {
289+ return r;
290+ }
291+ }
292+ return undefined;
293+ }
294+
295+ function findMusicButton() {
296+ // We need to find a AbstractButton that has a Label child
297+ // with text Music and it's parent x is >= 0
298+ var tabbar = findChild(dashContent, "tabbar");
299+ return doFindMusicButton(tabbar);
300+ }
301+
302+ function test_tabBar_index_change() {
303+ tryCompare(scopesModel, "loaded", true);
304+ var tabbar = findChild(dashContent, "tabbar");
305+
306+ compare(dashContent.currentIndex, 0);
307+ tryCompare(tabbar, "selectedIndex", 0);
308+ tryCompare(tabbar, "selectionMode", false);
309+
310+ mouseClick(tabbar, units.gu(5), units.gu(5))
311+
312+ tryCompare(tabbar, "selectionMode", true);
313+ tryCompare(tabbar, "selectedIndex", 0);
314+ tryCompare(dashContent, "currentIndex", 0);
315+
316+ var button;
317+ tryCompareFunction(function() { button = findMusicButton(); return button != undefined; }, true);
318+ waitForRendering(button);
319+
320+ mouseClick(button, button.width / 2, button.height / 2)
321+
322+ tryCompare(tabbar, "selectionMode", false);
323+ tryCompare(tabbar, "selectedIndex", 1);
324+ tryCompare(dashContent, "currentIndex", 1);
325+ }
326+
327+ function test_tabBar_listens_to_index_change() {
328+ var tabbar = findChild(dashContent, "tabbar");
329+ tryCompare(dashContent, "currentIndex", 0);
330+ compare(tabbar.selectedIndex, 0);
331+ dashContent.currentIndex = 1;
332+ compare(tabbar.selectedIndex, 1);
333+ }
334+
271335 function checkFlickMovingAndNotInteractive()
272336 {
273337 var dashContentList = findChild(dashContent, "dashContentList");
@@ -282,7 +346,6 @@ Item {
282346 return dashContentList.currentItem.moving && !dashContentList.interactive;
283347 }
284348
285−
286349 function test_hswipe_disabled_vswipe() {
287350 var dashContentList = findChild(dashContent, "dashContentList");
288351
tests/qmltests/Dash/tst_GenericScopeView.qml+57−27
@@ -41,54 +41,83 @@ Item {
4141 signal mainStageFocusedApplicationChanged()
4242 }
4343
44+ PreviewListView {
45+ id: previewListView
46+ anchors.fill: parent
47+ openEffect: openEffect
48+ categoryView: genericScopeView.categoryView
49+ scope: genericScopeView.scope
50+ }
51+
52+ DashContentOpenEffect {
53+ id: openEffect
54+ previewListView: previewListView
55+ }
56+
57+ PageHeaderLabel {
58+ id: pageHeader
59+ searchHistory: SearchHistoryModel {}
60+ }
61+
4462 GenericScopeView {
4563 id: genericScopeView
4664 anchors.fill: parent
47− searchHistory: SearchHistoryModel {}
65+ previewListView: previewListView
66+ openEffect: openEffect
67+ pageHeader: pageHeader
68+ tabBarHeight: pageHeader.implicitHeight
4869
4970 UT.UnityTestCase {
5071 name: "GenericScopeView"
5172 when: scopes.loaded
5273
53− function test_isCurrent() {
54− var pageHeader = findChild(genericScopeView, "pageHeader");
55− var previewListView = findChild(genericScopeView, "previewListView");
74+ function test_isCurrent() {
5675 genericScopeView.isCurrent = true
5776 pageHeader.searchQuery = "test"
5877 previewListView.open = true
5978 genericScopeView.isCurrent = false
6079 tryCompare(pageHeader, "searchQuery", "")
61− tryCompare(genericScopeView, "previewShown", false);
80+ tryCompare(previewListView, "open", false);
6281 }
6382
6483 function test_showDash() {
65− var previewListView = findChild(genericScopeView, "previewListView");
6684 previewListView.open = true;
85+ tryCompare(openEffect, "live", true);
6786 scopes.get(0).showDash();
68− tryCompare(genericScopeView, "previewShown", false);
87+ tryCompare(previewListView, "open", false);
88+ tryCompare(openEffect, "live", true);
6989 }
7090
7191 function test_hideDash() {
72− var previewListView = findChild(genericScopeView, "previewListView");
7392 previewListView.open = true;
7493 scopes.get(0).hideDash();
75− tryCompare(genericScopeView, "previewShown", false);
94+ tryCompare(previewListView, "open", false);
7695 }
7796
7897 function openPreview() {
7998 var categoryListView = findChild(genericScopeView, "categoryListView");
8099 categoryListView.positionAtBeginning();
81100
101+ tryCompareFunction(function() {
102+ var tile = findChild(findChild(genericScopeView, "0"), "delegate0");
103+ return tile != undefined;
104+ },
105+ true);
82106 var tile = findChild(findChild(genericScopeView, "0"), "delegate0");
83107 mouseClick(tile, tile.width / 2, tile.height / 2);
84− var openEffect = findChild(genericScopeView, "openEffect");
108+ tryCompare(previewListView, "open", true);
85109 tryCompare(openEffect, "gap", 1);
86110 }
87111
88112 function checkArrowPosition(index) {
113+ tryCompareFunction(function() {
114+ var tile = findChild(findChild(genericScopeView, "0"), "delegate" + index);
115+ return tile != undefined;
116+ },
117+ true);
89118 var tile = findChild(findChild(genericScopeView, "0"), "delegate" + index);
90119 var tileCenter = tile.x + tile.width/2;
91− var pointerArrow = findChild(genericScopeView, "pointerArrow");
120+ var pointerArrow = findChild(previewListView, "pointerArrow");
92121 var pointerArrowCenter = pointerArrow.x + pointerArrow.width/2;
93122 compare(pointerArrowCenter, tileCenter, "Pointer did not move to tile");
94123 }
@@ -97,9 +126,7 @@ Item {
97126 var closePreviewMouseArea = findChild(genericScopeView, "closePreviewMouseArea");
98127 mouseClick(closePreviewMouseArea, closePreviewMouseArea.width / 2, closePreviewMouseArea.height / 2);
99128
100− var previewListView = findChild(genericScopeView, "previewListView");
101129 tryCompare(previewListView, "open", false);
102− var openEffect = findChild(genericScopeView, "openEffect");
103130 tryCompare(openEffect, "gap", 0);
104131
105132 var categoryListView = findChild(genericScopeView, "categoryListView");
@@ -108,13 +135,12 @@ Item {
108135 }
109136
110137 function test_previewOpenClose() {
111− var previewListView = findChild(genericScopeView, "previewListView");
112138 tryCompare(previewListView, "open", false);
113139
114140 openPreview();
115141
116142 // check for it opening successfully
117− var currentPreviewItem = findChild(genericScopeView, "previewLoader0");
143+ var currentPreviewItem = findChild(previewListView, "previewLoader0");
118144 tryCompareFunction(function() {
119145 var parts = currentPreviewItem.source.toString().split("/");
120146 var name = parts[parts.length - 1];
@@ -138,26 +164,23 @@ Item {
138164 var categoryListView = findChild(genericScopeView, "categoryListView");
139165 categoryListView.positionAtBeginning();
140166 waitForRendering(categoryListView);
141− categoryListView.flick(0, -units.gu(80));
167+ categoryListView.flick(0, -units.gu(60));
142168 tryCompare(categoryListView.flicking, false);
143169
144170 var tile = findChild(findChild(genericScopeView, "0"), "delegate0");
145171 mouseClick(tile, tile.width / 2, tile.height - 1);
146− var openEffect = findChild(genericScopeView, "openEffect");
147172 tryCompare(openEffect, "gap", 1);
148173
149− var pageHeader = findChild(genericScopeView, "pageHeader");
150174 verify(openEffect.positionPx >= pageHeader.height + categoryListView.stickyHeaderHeight);
151175 }
152176
153177 function test_previewCycle() {
154− var previewListView = findChild(genericScopeView, "previewListView");
155178 tryCompare(previewListView, "open", false);
156179
157180 openPreview();
158181
159182 // wait for it to be loaded
160− var currentPreviewItem = findChild(genericScopeView, "previewLoader0");
183+ var currentPreviewItem = findChild(previewListView, "previewLoader0");
161184 tryCompareFunction(function() {
162185 var parts = currentPreviewItem.source.toString().split("/");
163186 var name = parts[parts.length - 1];
@@ -179,7 +202,7 @@ Item {
179202 previewListView.height / 2);
180203
181204 // wait for it to be loaded
182− var nextPreviewItem = findChild(genericScopeView, "previewLoader" + i);
205+ var nextPreviewItem = findChild(previewListView, "previewLoader" + i);
183206 tryCompareFunction(function() {
184207 var parts = nextPreviewItem.source.toString().split("/");
185208 var name = parts[parts.length - 1];
@@ -206,11 +229,12 @@ Item {
206229
207230 function test_show_spinner() {
208231 openPreview();
209− var previewListView = findChild(genericScopeView, "previewListView");
210− var previewLoader = findChild(genericScopeView, "previewLoader0");
232+ var previewLoader = findChild(previewListView, "previewLoader0");
233+ tryCompare(previewLoader, "progress", 1.0);
234+ tryCompareFunction(function() { return previewLoader.item != undefined; }, true);
211235
212236 previewLoader.item.showProcessingAction = true;
213− var waitingForAction = findChild(genericScopeView, "waitingForActionMouseArea");
237+ var waitingForAction = findChild(previewListView, "waitingForActionMouseArea");
214238 tryCompare(waitingForAction, "enabled", true);
215239 previewLoader.closePreviewSpinner();
216240 tryCompare(waitingForAction, "enabled", false);
@@ -252,12 +276,10 @@ Item {
252276 tryCompareFunction(function() { return findChild(genericScopeView, "carouselDelegate") != undefined; }, true);
253277 var tile = findChild(genericScopeView, "carouselDelegate");
254278 mouseClick(tile, tile.width / 2, tile.height / 2);
255− var openEffect = findChild(genericScopeView, "openEffect");
256279 tryCompare(openEffect, "gap", 1);
257280
258281 // check for it opening successfully
259− var previewListView = findChild(genericScopeView, "previewListView");
260− var currentPreviewItem = findChild(genericScopeView, "previewLoader0");
282+ var currentPreviewItem = findChild(previewListView, "previewLoader0");
261283 tryCompareFunction(function() {
262284 var parts = currentPreviewItem.source.toString().split("/");
263285 var name = parts[parts.length - 1];
@@ -299,6 +321,14 @@ Item {
299321
300322 categoryListView.positionAtBeginning();
301323
324+ // wait for the header0 to be on its position
325+ tryCompareFunction(
326+ function() {
327+ var header0 = findChild(genericScopeView, "dashSectionHeader0")
328+ return header0.y == pageHeader.height;
329+ },
330+ true);
331+
302332 var header0 = findChild(genericScopeView, "dashSectionHeader0")
303333 var category0 = findChild(genericScopeView, "dashCategory0")
304334 mouseClick(header0, header0.width / 2, header0.height / 2);
tests/qmltests/Notifications/tst_Notifications.qml+4−11
@@ -141,7 +141,6 @@ Row {
141141 id: notifications
142142
143143 margin: units.gu(1)
144− fullHeight: parent.height
145144
146145 anchors.fill: parent
147146 model: mockModel
@@ -226,7 +225,6 @@ Row {
226225 { id: "nada_id", label: "Nada"}],
227226 summaryVisible: true,
228227 bodyVisible: true,
229− interactiveAreaEnabled: false,
230228 iconVisible: true,
231229 shapedIcon: true,
232230 nonShapedIcon: false,
@@ -245,7 +243,6 @@ Row {
245243 actions: [],
246244 summaryVisible: true,
247245 bodyVisible: false,
248− interactiveAreaEnabled: false,
249246 iconVisible: false,
250247 shapedIcon: false,
251248 nonShapedIcon: false,
@@ -283,7 +280,6 @@ Row {
283280 actions: [{ id: "reply_id", label: "Dummy"}],
284281 summaryVisible: true,
285282 bodyVisible: true,
286− interactiveAreaEnabled: true,
287283 iconVisible: true,
288284 shapedIcon: true,
289285 nonShapedIcon: false,
@@ -303,7 +299,6 @@ Row {
303299 { id: "reject_id", label: "Reject"}],
304300 summaryVisible: true,
305301 bodyVisible: true,
306− interactiveAreaEnabled: false,
307302 iconVisible: true,
308303 shapedIcon: true,
309304 nonShapedIcon: false,
@@ -322,7 +317,6 @@ Row {
322317 actions: [],
323318 summaryVisible: true,
324319 bodyVisible: true,
325− interactiveAreaEnabled: false,
326320 iconVisible: true,
327321 shapedIcon: true,
328322 nonShapedIcon: false,
@@ -342,7 +336,6 @@ Row {
342336 actions: [],
343337 summaryVisible: true,
344338 bodyVisible: true,
345− interactiveAreaEnabled: false,
346339 iconVisible: true,
347340 shapedIcon: false,
348341 nonShapedIcon: true,
@@ -396,14 +389,14 @@ Row {
396389 compare(icon.visible, data.iconVisible, "avatar-icon visibility is incorrect")
397390 compare(shapedIcon.visible, data.shapedIcon, "shaped-icon visibility is incorrect")
398391 compare(nonShapedIcon.visible, data.nonShapedIcon, "non-shaped-icon visibility is incorrect")
399− compare(interactiveArea.enabled, data.interactiveAreaEnabled, "check for interactive area")
400392
401− if(data.interactiveAreaEnabled) {
402− mouseClick(notification, notification.width / 2, notification.height / 2)
393+ // test input does not fall through
394+ mouseClick(notification, notification.width / 2, notification.height / 2)
395+ if(data.type == Notification.Interactive) {
403396 actionSpy.wait()
404397 compare(actionSpy.signalArguments[0][0], data.actions[0]["id"], "got wrong id for interactive action")
405− compare(clickThroughSpy.count, 0, "click on interactive notification fell through")
406398 }
399+ compare(clickThroughSpy.count, 0, "click on notification fell through")
407400
408401 compare(secondaryIcon.visible, data.secondaryIconVisible, "secondary-icon visibility is incorrect")
409402 compare(summaryLabel.visible, data.summaryVisible, "summary-text visibility is incorrect")
tests/qmltests/Panel/tst_IndicatorItem.qml−11
@@ -41,17 +41,6 @@ Rectangle {
4141 tryCompareFunction(function(){return indicatorItem.opacity < 1.0}, true);
4242 }
4343
44− function test_highlight() {
45− var itemHighlight = findChild(indicatorItem, "highlight");
46− verify(itemHighlight != undefined);
47−
48− indicatorItem.highlighted = true;
49− compare(itemHighlight.visible, true, "Indicator should be highlighted");
50−
51− indicatorItem.highlighted = false;
52− compare(itemHighlight.visible, false, "Indicator should not be highlighted");
53− }
54−
5544 function test_empty() {
5645 compare(indicatorItem.indicatorVisible, false, "IndicatorItem should not be visible.");
5746 indicatorItem.widgetSource = "qrc:/tests/indciators/qml/fake_menu_widget1.qml";
tests/qmltests/Panel/tst_IndicatorRow.qml+18−4
@@ -34,6 +34,7 @@ Item {
3434 {
3535 indicatorRow.state = "initial";
3636 indicatorRow.currentItem = null;
37+ indicatorRow.unitProgress = 0.0;
3738 }
3839
3940 PanelBackground {
@@ -83,14 +84,13 @@ Item {
8384 function test_current_item_commit() {
8485 init_test();
8586
86− indicatorRow.setCurrentItem(1);
87+ indicatorRow.setCurrentItem(2);
8788 indicatorRow.state = "commit";
8889 tryCompare(get_indicator_item_at(0), "opacity", 0.0);
89− tryCompare(get_indicator_item_at(1), "opacity", 1.0);
90− tryCompare(get_indicator_item_at(2), "opacity", 0.0);
90+ tryCompare(get_indicator_item_at(1), "opacity", 0.0);
91+ tryCompare(get_indicator_item_at(2), "opacity", 1.0);
9192 tryCompare(get_indicator_item_at(3), "opacity", 0.0);
9293 tryCompare(get_indicator_item_at(4), "opacity", 0.0);
93−
9494 }
9595 }
9696
@@ -166,6 +166,20 @@ Item {
166166 compare(get_indicator_item_at(3).highlighted, false, "Other indicators should not highlight when in locked state");
167167 compare(get_indicator_item_at(4).highlighted, false, "Other indicators should not highlight when in locked state");
168168 }
169+
170+ function test_opacity() {
171+ init_test();
172+
173+ indicatorRow.state = "reveal";
174+ indicatorRow.unitProgress = 0.5;
175+ indicatorRow.setCurrentItem(2);
176+
177+ compare(get_indicator_item_at(0).opacity, 0.5, "Other indicator opacity should change in response to panel progress");
178+ compare(get_indicator_item_at(1).opacity, 0.5, "Other indicator opacity should change in response to panel progress");
179+ compare(get_indicator_item_at(2).opacity, 1.0, "Current indicator should not change in response to panel progress");
180+ compare(get_indicator_item_at(3).opacity, 0.5, "Other indicator opacity should change in response to panel progress");
181+ compare(get_indicator_item_at(4).opacity, 0.5, "Other indicator opacity should change in response to panel progress");
182+ }
169183 }
170184
171185 UT.UnityTestCase {
tools/CMakeLists.txtnew file+11
@@ -0,0 +1,11 @@
1+add_executable(${SCOPE_TOOL}
2+ scopetool.cpp
3+ registry-tracker.cpp
4+ )
5+
6+qt5_use_modules(${SCOPE_TOOL} Qml Quick)
7+
8+# install binaries
9+install(TARGETS ${SCOPE_TOOL}
10+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
11+ )
tools/registry-tracker.cppnew file+116
@@ -0,0 +1,116 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Michal Hruby <michal.hruby@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+// Qt
21+#include <QtGlobal>
22+#include <QDir>
23+
24+// local
25+#include "registry-tracker.h"
26+
27+
28+RegistryTracker::RegistryTracker(QString const& scope_dir):
29+ m_scopeDir(scope_dir),
30+ m_registry(nullptr),
31+ m_endpoints_dir(QDir::temp().filePath("scope-dev-endpoints.XXXXXX"))
32+{
33+ runRegistry();
34+}
35+
36+RegistryTracker::~RegistryTracker()
37+{
38+ if (m_registry.state() != QProcess::NotRunning) {
39+ m_registry.terminate();
40+ m_registry.waitForFinished(5000);
41+ m_registry.kill();
42+ }
43+}
44+
45+#define RUNTIME_CONFIG \
46+"[Runtime]\n" \
47+"Registry.Identity = Registry\n" \
48+"Registry.ConfigFile = %1\n" \
49+"Default.Middleware = Zmq\n" \
50+"Zmq.ConfigFile = %2\n"
51+
52+#define REGISTRY_CONFIG \
53+"[Registry]\n" \
54+"Middleware = Zmq\n" \
55+"Zmq.Endpoint = ipc://%1/Registry\n" \
56+"Zmq.EndpointDir = %2\n" \
57+"Zmq.ConfigFile = %3\n" \
58+"Scope.InstallDir = %4\n" \
59+"Scoperunner.Path = %5\n"
60+
61+#define MW_CONFIG \
62+"[Zmq]\n" \
63+"EndpointDir.Public = %1\n" \
64+"EndpointDir.Private = %2\n"
65+
66+void RegistryTracker::runRegistry()
67+{
68+ QDir tmp(QDir::temp());
69+ m_runtime_config.setFileTemplate(tmp.filePath("Runtime.ini.XXXXXX"));
70+ m_registry_config.setFileTemplate(tmp.filePath("Registry.ini.XXXXXX"));
71+ m_mw_config.setFileTemplate(tmp.filePath("Zmq.ini.XXXXXX"));
72+
73+ if (!m_runtime_config.open() || !m_registry_config.open() || !m_mw_config.open() || !m_endpoints_dir.isValid()) {
74+ qWarning("Unable to open temporary files!");
75+ return;
76+ }
77+
78+ QString scopesLibdir;
79+ {
80+ QProcess pkg_config;
81+ QStringList arguments;
82+ arguments << "--variable=libdir";
83+ arguments << "libunity-scopes";
84+ pkg_config.start("pkg-config", arguments);
85+ pkg_config.waitForFinished();
86+ QByteArray libdir = pkg_config.readAllStandardOutput();
87+ scopesLibdir = QDir(QString::fromLocal8Bit(libdir)).path().trimmed();
88+ }
89+
90+ if (scopesLibdir.size() == 0) {
91+ qWarning("Unable to find libunity-scopes package config file");
92+ return;
93+ }
94+
95+ QString scopeRunnerPath = QDir(scopesLibdir).filePath("scoperunner/scoperunner");
96+
97+ QString runtime_ini = QString(RUNTIME_CONFIG).arg(m_registry_config.fileName()).arg(m_mw_config.fileName());
98+ QString registry_ini = QString(REGISTRY_CONFIG).arg(m_endpoints_dir.path()).arg(m_endpoints_dir.path()).arg(m_mw_config.fileName()).arg(m_scopeDir).arg(scopeRunnerPath);
99+ QString mw_ini = QString(MW_CONFIG).arg(m_endpoints_dir.path()).arg(m_endpoints_dir.path());
100+
101+ m_runtime_config.write(runtime_ini.toUtf8());
102+ m_registry_config.write(registry_ini.toUtf8());
103+ m_mw_config.write(mw_ini.toUtf8());
104+
105+ m_runtime_config.flush();
106+ m_registry_config.flush();
107+ m_mw_config.flush();
108+
109+ qputenv("UNITY_SCOPES_RUNTIME_PATH", m_runtime_config.fileName().toLocal8Bit());
110+
111+ QString registryBin(QDir(scopesLibdir).filePath("scoperegistry/scoperegistry"));
112+ QStringList arguments;
113+ arguments << m_runtime_config.fileName();
114+
115+ m_registry.start(registryBin, arguments);
116+}
tools/registry-tracker.hnew file+44
@@ -0,0 +1,44 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Michal Hruby <michal.hruby@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+// Qt
21+#include <QProcess>
22+#include <QTemporaryFile>
23+#include <QTemporaryDir>
24+#include <QDebug>
25+
26+
27+class RegistryTracker
28+{
29+public:
30+ RegistryTracker(QString const&);
31+ ~RegistryTracker();
32+
33+ QProcess* registry() const;
34+
35+private:
36+ void runRegistry();
37+
38+ QString m_scopeDir;
39+ QProcess m_registry;
40+ QTemporaryDir m_endpoints_dir;
41+ QTemporaryFile m_runtime_config;
42+ QTemporaryFile m_registry_config;
43+ QTemporaryFile m_mw_config;
44+};
tools/scopetool.cppnew file+84
@@ -0,0 +1,84 @@
1+/*
2+ * Copyright (C) 2012 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Gerry Boland <gerry.boland@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+// Qt
21+#include <QtQuick/QQuickView>
22+#include <QtGui/QGuiApplication>
23+#include <QtQml/QQmlEngine>
24+#include <QtQml/QQmlContext>
25+#include <QScopedPointer>
26+#include <QDebug>
27+#include <libintl.h>
28+
29+
30+// local
31+#include <paths.h>
32+#include "registry-tracker.h"
33+
34+
35+int main(int argc, char *argv[])
36+{
37+ /* Workaround Qt platform integration plugin not advertising itself
38+ as having the following capabilities:
39+ - QPlatformIntegration::ThreadedOpenGL
40+ - QPlatformIntegration::BufferQueueingOpenGL
41+ */
42+ setenv("QML_FORCE_THREADED_RENDERER", "1", 1);
43+ setenv("QML_FIXED_ANIMATION_STEP", "1", 1);
44+
45+ QGuiApplication::setApplicationName("Unity Scope Tool");
46+ QGuiApplication *application;
47+ application = new QGuiApplication(argc, argv);
48+
49+ QStringList arguments = application->arguments();
50+ QString scopesDir;
51+ if (arguments.contains("--scope-dir")) {
52+ int idx = arguments.indexOf("--scope-dir");
53+ scopesDir = arguments[idx+1];
54+ }
55+
56+ QScopedPointer<RegistryTracker> tracker;
57+ if (!scopesDir.isEmpty()) {
58+ tracker.reset(new RegistryTracker(scopesDir));
59+ }
60+
61+ resolveIconTheme();
62+
63+ bindtextdomain("unity8", translationDirectory().toUtf8().data());
64+
65+ QQuickView* view = new QQuickView();
66+ view->setResizeMode(QQuickView::SizeRootObjectToView);
67+ view->setTitle(QGuiApplication::applicationName());
68+ view->engine()->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
69+
70+ QUrl source(::qmlDirectory() + "ScopeTool.qml");
71+ prependImportPaths(view->engine(), ::overrideImportPaths());
72+ appendImportPaths(view->engine(), ::fallbackImportPaths());
73+
74+ view->setSource(source);
75+
76+ view->show();
77+
78+ int result = application->exec();
79+
80+ delete view;
81+ delete application;
82+
83+ return result;
84+}
unity8.qmlprojectdeleted−25
@@ -1,25 +0,0 @@
1−/* File generated by Qt Creator, version 2.4.1 */
2−
3−import QmlProject 1.1
4−
5−Project {
6− mainFile: "Shell.qml"
7−
8− /* Include .qml, .js, and image files from current directory and subdirectories */
9− QmlFiles {
10− directory: "."
11− }
12− JavaScriptFiles {
13− directory: "."
14− }
15− ImageFiles {
16− directory: "."
17− }
18− /* List of plugin directories passed to QML runtime */
19− importPaths: [
20− "builddir/plugins",
21− "builddir/modules",
22− "builddir/tests/plugins",
23− "builddir/tests/utils/modules"
24− ]
25−}