on xenial help sign in
merged with trunk
a4c01ddNick Dedekind
CMakeLists.txt+2
@@ -96,6 +96,7 @@ include(autopilot)
96declare_autopilot_test(shell unity8.shell ${CMAKE_SOURCE_DIR}/tests/autopilot/)
96declare_autopilot_test(shell unity8.shell ${CMAKE_SOURCE_DIR}/tests/autopilot/)
97
97
98set(SHELL_APP unity8)
98set(SHELL_APP unity8)
99+set(SCOPE_TOOL unity-scope-tool)
99
100
100include_directories(
101include_directories(
101 ${CMAKE_SOURCE_DIR}/include
102 ${CMAKE_SOURCE_DIR}/include
@@ -105,6 +106,7 @@ include_directories(
105# add subdirectories to build
106# add subdirectories to build
106add_subdirectory(include)
107add_subdirectory(include)
107add_subdirectory(src)
108add_subdirectory(src)
109+add_subdirectory(tools)
108add_subdirectory(qml)
110add_subdirectory(qml)
109add_subdirectory(tests)
111add_subdirectory(tests)
110add_subdirectory(plugins)
112add_subdirectory(plugins)
CODING+29
@@ -166,3 +166,32 @@ $ make tryDragHandle
166NB: Manual test or interation doesn't make sense for all components or classes,
166NB: Manual test or interation doesn't make sense for all components or classes,
167particularly the non-graphical ones. In such cases no "tryFoo" make target is
167particularly the non-graphical ones. In such cases no "tryFoo" make target is
168provided.
168provided.
169+
170+NB: The tests expect the locale to be the C locale, they may fail if run
171+under a different locale, if you get failures others can not reproduce
172+prepend 'LC_ALL=C' to the 'make test' command
173+
174+Running autopilot tests
175+=======================
176+
177+Autopilot tests are run from the build directory (which will be ./builddir if you used
178+the ./build script).
179+
180+$ make autopilot
181+
182+If you want you can also run tests individually, for that you need to install first,
183+otherwise the tests will run on the system-installed version
184+
185+$ make install
186+
187+To see the list of existing autopilot tests you can do
188+
189+$ PYTHONPATH=../tests/autopilot autopilot list unity8
190+
191+And then run a single test with
192+
193+$ PYTHONPATH=../tests/autopilot autopilot run unity8.some.very.long.test.name
194+
195+or run them all with
196+
197+$ PYTHONPATH=../tests/autopilot autopilot run unity8
cmake/modules/autopilot.cmake+5−1
@@ -4,7 +4,11 @@ function(declare_autopilot_test TEST_NAME TEST_SUITE WORKING_DIR)
4 add_custom_target(autopilot-${TEST_NAME}
4 add_custom_target(autopilot-${TEST_NAME}
5 COMMAND LANG=C UBUNTU_ICON_THEME=ubuntu-mobile QML2_IMPORT_PATH=${SHELL_INSTALL_QML}/mocks autopilot run ${TEST_SUITE}
5 COMMAND LANG=C UBUNTU_ICON_THEME=ubuntu-mobile QML2_IMPORT_PATH=${SHELL_INSTALL_QML}/mocks autopilot run ${TEST_SUITE}
6 WORKING_DIRECTORY ${WORKING_DIR}
6 WORKING_DIRECTORY ${WORKING_DIR}
7− DEPENDS install
7+ DEPENDS fake_install
8+ )
9+
10+ add_custom_target(fake_install
11+ COMMAND cmake --build ${CMAKE_BINARY_DIR} --target install
8 )
12 )
9
13
10 add_dependencies(autopilot autopilot-${TEST_NAME})
14 add_dependencies(autopilot autopilot-${TEST_NAME})
data/CMakeLists.txt+3−2
@@ -1,7 +1,8 @@
1# generate desktop file
1# generate desktop file
2configure_file(${SHELL_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop @ONLY)
2configure_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)
3
4
4−# install desktop file
5+# install desktop files
5−install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop
6+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop ${CMAKE_CURRENT_BINARY_DIR}/${SCOPE_TOOL}.desktop
6 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
7 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
7 )
8 )
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/control+12−3
@@ -53,23 +53,31 @@ Vcs-Browser: https://bazaar.launchpad.net/~unity-team/unity8/trunk
53Package: indicators-client
53Package: indicators-client
54Architecture: amd64 armhf i386
54Architecture: amd64 armhf i386
55Depends: qmenumodel-qml (>= 0.2.7),
55Depends: qmenumodel-qml (>= 0.2.7),
56− qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.46+13.10.20130829.1-0ubuntu1),
56+ qtdeclarative5-ubuntu-ui-toolkit-plugin,
57 unity8 (= ${binary:Version}),
57 unity8 (= ${binary:Version}),
58 ${misc:Depends},
58 ${misc:Depends},
59 ${shlibs:Depends},
59 ${shlibs:Depends},
60Description: Indicators client test application
60Description: Indicators client test application
61 This package contains the client application for indicators used by autopilot
61 This package contains the client application for indicators used by autopilot
62
62
63+Package: unity-scope-tool
64+Architecture: any
65+Depends: unity8 (= ${binary:Version}),
66+ ${misc:Depends},
67+ ${shlibs:Depends},
68+Description: Development tool for Unity scopes
69+ This package contains the development tool for new generation Unity scopes, based
70+ on Unity8.
71+
63Package: unity8
72Package: unity8
64Architecture: any
73Architecture: any
65Provides: indicator-renderer,
74Provides: indicator-renderer,
66Depends: gsettings-desktop-schemas,
75Depends: gsettings-desktop-schemas,
67− libunity-mir1 (>= 0.2),
68 qmenumodel-qml (>= 0.2.7),
76 qmenumodel-qml (>= 0.2.7),
69 qtdeclarative5-dee-plugin,
77 qtdeclarative5-dee-plugin,
70 qtdeclarative5-gsettings1.0,
78 qtdeclarative5-gsettings1.0,
71 qtdeclarative5-ubuntu-settings-components,
79 qtdeclarative5-ubuntu-settings-components,
72− qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.46+13.10.20130829.1-0ubuntu1),
80+ qtdeclarative5-ubuntu-ui-toolkit-plugin,
73 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
81 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
74 qtdeclarative5-xmllistmodel-plugin,
82 qtdeclarative5-xmllistmodel-plugin,
75 unity-launcher-impl-3,
83 unity-launcher-impl-3,
@@ -97,6 +105,7 @@ Depends: gir1.2-glib-2.0,
97 libqt5widgets5,
105 libqt5widgets5,
98 python-evdev,
106 python-evdev,
99 python-gi,
107 python-gi,
108+ python-mock,
100 unity8 (>= ${source:Version}),
109 unity8 (>= ${source:Version}),
101 unity8-fake-env (>= ${source:Version}),
110 unity8-fake-env (>= ${source:Version}),
102 ${misc:Depends},
111 ${misc:Depends},
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)
24set(QMLPLUGIN_SRC
24set(QMLPLUGIN_SRC
25 plugin.cpp
25 plugin.cpp
26 listviewwithpageheader.cpp
26 listviewwithpageheader.cpp
27+ abstractjournal.cpp
27 verticaljournal.cpp
28 verticaljournal.cpp
29+ horizontaljournal.cpp
28 )
30 )
29
31
30add_library(DashViews-qml MODULE
32add_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 = -1;
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 = -1;
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 @@
17
17
18#include "plugin.h"
18#include "plugin.h"
19
19
20+#include "horizontaljournal.h"
20#include "listviewwithpageheader.h"
21#include "listviewwithpageheader.h"
21#include "verticaljournal.h"
22#include "verticaljournal.h"
22
23
@@ -26,6 +27,7 @@ void DashViewsPlugin::registerTypes(const char *uri)
26{
27{
27 Q_ASSERT(uri == QLatin1String("DashViews"));
28 Q_ASSERT(uri == QLatin1String("DashViews"));
28 qmlRegisterType<QAbstractItemModel>();
29 qmlRegisterType<QAbstractItemModel>();
30+ qmlRegisterType<HorizontalJournal>(uri, 0, 1, "HorizontalJournal");
29 qmlRegisterType<ListViewWithPageHeader>(uri, 0, 1, "ListViewWithPageHeader");
31 qmlRegisterType<ListViewWithPageHeader>(uri, 0, 1, "ListViewWithPageHeader");
30 qmlRegisterType<VerticalJournal>(uri, 0, 1, "VerticalJournal");
32 qmlRegisterType<VerticalJournal>(uri, 0, 1, "VerticalJournal");
31}
33}
plugins/DashViews/verticaljournal.cpp+78−420
@@ -27,88 +27,16 @@
27 */
27 */
28#include "verticaljournal.h"
28#include "verticaljournal.h"
29
29
30−#include <qqmlengine.h>
31#pragma GCC diagnostic push
30#pragma GCC diagnostic push
32#pragma GCC diagnostic ignored "-pedantic"
31#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
39#include <private/qquickitem_p.h>
32#include <private/qquickitem_p.h>
40#pragma GCC diagnostic pop
33#pragma GCC diagnostic pop
41
34
42static const qreal bufferRatio = 0.5;
35static const qreal bufferRatio = 0.5;
43
36
44VerticalJournal::VerticalJournal()
37VerticalJournal::VerticalJournal()
45− : m_delegateModel(nullptr)
46− , m_asyncRequestedIndex(-1)
47− , m_columnWidth(0)
38+ : 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{
39{
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)
98−{
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− }
112}
40}
113
41
114qreal VerticalJournal::columnWidth() const
42qreal VerticalJournal::columnWidth() const
@@ -133,133 +61,6 @@ void VerticalJournal::setColumnWidth(qreal columnWidth)
133 }
61 }
134}
62}
135
63
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−
263void VerticalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
64void VerticalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
264{
65{
265 *modelIndex = 0;
66 *modelIndex = 0;
@@ -268,7 +69,7 @@ void VerticalJournal::findBottomModelIndexToAdd(int *modelIndex, qreal *yPos)
268 Q_FOREACH(const auto &column, m_columnVisibleItems) {
69 Q_FOREACH(const auto &column, m_columnVisibleItems) {
269 if (!column.isEmpty()) {
70 if (!column.isEmpty()) {
270 const ViewItem &item = column.last();
71 const ViewItem &item = column.last();
271− *yPos = qMin(*yPos, item.y() + item.height() + m_rowSpacing);
72+ *yPos = qMin(*yPos, item.y() + item.height() + rowSpacing());
272 *modelIndex = qMax(*modelIndex, item.m_modelIndex + 1);
73 *modelIndex = qMax(*modelIndex, item.m_modelIndex + 1);
273 } else {
74 } else {
274 *yPos = 0;
75 *yPos = 0;
@@ -287,7 +88,7 @@ void VerticalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
287 const auto &column = m_columnVisibleItems[i];
88 const auto &column = m_columnVisibleItems[i];
288 if (!column.isEmpty()) {
89 if (!column.isEmpty()) {
289 const ViewItem &item = column.first();
90 const ViewItem &item = column.first();
290− const auto itemTopPos = item.y() - m_rowSpacing;
91+ const auto itemTopPos = item.y() - rowSpacing();
291 if (itemTopPos > *yPos) {
92 if (itemTopPos > *yPos) {
292 *yPos = itemTopPos;
93 *yPos = itemTopPos;
293 *modelIndex = item.m_modelIndex - 1;
94 *modelIndex = item.m_modelIndex - 1;
@@ -309,51 +110,19 @@ void VerticalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
309 }
110 }
310}
111}
311
112
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)
345{
114{
346 bool changed = false;
115 bool changed = false;
347
116
348 for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
117 for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
349 QList<ViewItem> &column = m_columnVisibleItems[i];
118 QList<ViewItem> &column = m_columnVisibleItems[i];
350− while (!column.isEmpty() && column.first().y() + column.first().height() < bufferFrom) {
119+ while (!column.isEmpty() && column.first().y() + column.first().height() < bufferFromY) {
351− releaseItem(column.takeFirst());
120+ releaseItem(column.takeFirst().m_item);
352 changed = true;
121 changed = true;
353 }
122 }
354
123
355− while (!column.isEmpty() && column.last().y() > bufferTo) {
124+ while (!column.isEmpty() && column.last().y() > bufferToY) {
356− releaseItem(column.takeLast());
125+ releaseItem(column.takeLast().m_item);
357 changed = true;
126 changed = true;
358 }
127 }
359 }
128 }
@@ -361,53 +130,20 @@ bool VerticalJournal::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
361 return changed;
130 return changed;
362}
131}
363
132
364−QQuickItem *VerticalJournal::createItem(int modelIndex, bool asynchronous)
133+void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
365{
134{
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) {
135+ if (item->width() != m_columnWidth) {
394− qWarning() << "Item" << modelIndex << "width is not the one that the columnWidth mandates, resetting it";
136+ qWarning() << "Item" << modelIndex << "width is not the one that the columnWidth mandates, resetting it";
395− item->setWidth(m_columnWidth);
137+ item->setWidth(m_columnWidth);
396− }
397− positionItem(modelIndex, item);
398− return item;
399 }
138 }
400−}
401
139
402−void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
403−{
404 // Check if we add it to the bottom of existing column items
140 // Check if we add it to the bottom of existing column items
405 const QList<ViewItem> &firstColumn = m_columnVisibleItems[0];
141 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();
407 int columnToAddTo = 0;
143 int columnToAddTo = 0;
408 for (int i = 1; i < m_columnVisibleItems.count(); ++i) {
144 for (int i = 1; i < m_columnVisibleItems.count(); ++i) {
409 const QList<ViewItem> &column = m_columnVisibleItems[i];
145 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();
411 if (iY < columnToAddY) {
147 if (iY < columnToAddY) {
412 columnToAddTo = i;
148 columnToAddTo = i;
413 columnToAddY = iY;
149 columnToAddY = iY;
@@ -416,8 +152,8 @@ void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
416
152
417 const QList<ViewItem> &columnToAdd = m_columnVisibleItems[columnToAddTo];
153 const QList<ViewItem> &columnToAdd = m_columnVisibleItems[columnToAddTo];
418 if (columnToAdd.isEmpty() || columnToAdd.last().m_modelIndex < modelIndex) {
154 if (columnToAdd.isEmpty() || columnToAdd.last().m_modelIndex < modelIndex) {
419− item->setX(columnToAddTo * (m_columnWidth + m_columnSpacing));
155+ item->setX(columnToAddTo * (m_columnWidth + columnSpacing()));
420− item->setY(columnToAddY + m_rowSpacing);
156+ item->setY(columnToAddY + rowSpacing());
421
157
422 m_columnVisibleItems[columnToAddTo] << ViewItem(item, modelIndex);
158 m_columnVisibleItems[columnToAddTo] << ViewItem(item, modelIndex);
423 m_indexColumnMap[modelIndex] = columnToAddTo;
159 m_indexColumnMap[modelIndex] = columnToAddTo;
@@ -426,8 +162,8 @@ void VerticalJournal::positionItem(int modelIndex, QQuickItem *item)
426 columnToAddTo = m_indexColumnMap[modelIndex];
162 columnToAddTo = m_indexColumnMap[modelIndex];
427 columnToAddY = m_columnVisibleItems[columnToAddTo].first().y();
163 columnToAddY = m_columnVisibleItems[columnToAddTo].first().y();
428
164
429− item->setX(columnToAddTo * (m_columnWidth + m_columnSpacing));
165+ item->setX(columnToAddTo * (m_columnWidth + columnSpacing()));
430− item->setY(columnToAddY - m_rowSpacing - item->height());
166+ item->setY(columnToAddY - rowSpacing() - item->height());
431
167
432 m_columnVisibleItems[columnToAddTo].prepend(ViewItem(item, modelIndex));
168 m_columnVisibleItems[columnToAddTo].prepend(ViewItem(item, modelIndex));
433 }
169 }
@@ -439,29 +175,14 @@ void VerticalJournal::cleanupExistingItems()
439 for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
175 for (int i = 0; i < m_columnVisibleItems.count(); ++i) {
440 QList<ViewItem> &column = m_columnVisibleItems[i];
176 QList<ViewItem> &column = m_columnVisibleItems[i];
441 Q_FOREACH(const ViewItem &item, column)
177 Q_FOREACH(const ViewItem &item, column)
442− releaseItem(item);
178+ releaseItem(item.m_item);
443 column.clear();
179 column.clear();
444 }
180 }
445 m_indexColumnMap.clear();
181 m_indexColumnMap.clear();
446}
182}
447
183
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−
461void VerticalJournal::calculateImplicitHeight()
184void VerticalJournal::calculateImplicitHeight()
462{
185{
463− m_implicitHeightDirty = false;
464−
465 int lastModelIndex = -1;
186 int lastModelIndex = -1;
466 qreal bottomMostY = 0;
187 qreal bottomMostY = 0;
467 Q_FOREACH(const auto &column, m_columnVisibleItems) {
188 Q_FOREACH(const auto &column, m_columnVisibleItems) {
@@ -479,144 +200,81 @@ void VerticalJournal::calculateImplicitHeight()
479 }
200 }
480}
201}
481
202
482−#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
483−void VerticalJournal::itemCreated(int modelIndex, QQuickItem *item)
203+void VerticalJournal::doRelayout()
484−{
485−#else
486−void VerticalJournal::itemCreated(int modelIndex, QObject *object)
487{
204{
488− QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
205+ QList<ViewItem> allItems;
489− if (!item) {
206+ Q_FOREACH(const auto &column, m_columnVisibleItems)
490− qWarning() << "VerticalJournal::itemCreated got a non item for index" << modelIndex;
207+ allItems << column;
491− return;
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+ }
492 }
228 }
493−#endif
229+}
494− item->setParentItem(this);
230+
495− if (modelIndex == m_asyncRequestedIndex) {
231+void VerticalJournal::updateItemCulling(qreal visibleFromY, qreal visibleToY)
496− createItem(modelIndex, false);
232+{
497− m_implicitHeightDirty = true;
233+ Q_FOREACH(const auto &column, m_columnVisibleItems) {
498− polish();
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+ }
499 }
238 }
500}
239}
501
240
502#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
241#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)
504#else
243#else
505−void VerticalJournal::onModelUpdated(const QQmlChangeSet &changeSet, bool reset)
244+void VerticalJournal::processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes)
506#endif
245#endif
507{
246{
508− if (reset) {
509− cleanupExistingItems();
510− } else {
511#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
247#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) {
513#else
249#else
514− Q_FOREACH(const QQmlChangeSet::Remove &remove, changeSet.removes()) {
250+ Q_FOREACH(const QQmlChangeSet::Remove &remove, removes) {
515#endif
251#endif
516− for (int i = remove.count - 1; i >= 0; --i) {
252+ for (int i = remove.count - 1; i >= 0; --i) {
517− const int indexToRemove = remove.index + i;
253+ const int indexToRemove = remove.index + i;
518− // Since we only support removing from the end, indexToRemove
254+ // Since we only support removing from the end, indexToRemove
519− // must refer to the last item of one of the columns or
255+ // must refer to the last item of one of the columns or
520− // be bigger than them (because it's not in the viewport and
256+ // be bigger than them (because it's not in the viewport and
521− // thus we have not created a delegate for it)
257+ // thus we have not created a delegate for it)
522− bool found = false;
258+ bool found = false;
523− int lastCreatedIndex = INT_MIN;
259+ int lastCreatedIndex = INT_MIN;
524− for (int i = 0; !found && i < m_columnVisibleItems.count(); ++i) {
260+ for (int i = 0; !found && i < m_columnVisibleItems.count(); ++i) {
525− QList<ViewItem> &column = m_columnVisibleItems[i];
261+ QList<ViewItem> &column = m_columnVisibleItems[i];
526− if (!column.isEmpty()) {
262+ if (!column.isEmpty()) {
527− const int lastColumnIndex = column.last().m_modelIndex;
263+ const int lastColumnIndex = column.last().m_modelIndex;
528− if (lastColumnIndex == indexToRemove) {
264+ if (lastColumnIndex == indexToRemove) {
529− releaseItem(column.takeLast());
265+ releaseItem(column.takeLast().m_item);
530− found = true;
531− }
532− lastCreatedIndex = qMax(lastCreatedIndex, lastColumnIndex);
533− }
534− }
535− if (!found) {
266+ found = true;
536− if (indexToRemove < lastCreatedIndex) {
537− qFatal("VerticalJournal only supports removal from the end of the model");
538− } else {
539− m_implicitHeightDirty = true;
540 }
267 }
268+ lastCreatedIndex = qMax(lastCreatedIndex, lastColumnIndex);
541 }
269 }
542 }
270 }
543− }
271+ if (!found) {
544− }
272+ if (indexToRemove < lastCreatedIndex) {
545− polish();
273+ qFatal("VerticalJournal only supports removal from the end of the model");
546−}
547−
548−
549−void VerticalJournal::relayout()
550−{
551− m_needsRelayout = true;
552− polish();
553−}
554−
555−void VerticalJournal::onHeightChanged()
556−{
557− polish();
558−}
559−
560−void VerticalJournal::updatePolish()
561−{
562− if (!model())
563− return;
564−
565− if (m_needsRelayout) {
566− m_implicitHeightDirty = true;
567−
568− QList<ViewItem> allItems;
569− Q_FOREACH(const auto &column, m_columnVisibleItems)
570− allItems << column;
571−
572− qSort(allItems);
573−
574− const int nColumns = qMax(1., floor((double)(width() + m_columnSpacing) / (m_columnWidth + m_columnSpacing)));
575− m_columnVisibleItems.resize(nColumns);
576− m_indexColumnMap.clear();
577− for (int i = 0; i < nColumns; ++i)
578− m_columnVisibleItems[i].clear();
579−
580− // If the first of allItems doesn't contain index 0 we need to drop them
581− // all since we can't consistently relayout without the first item being there
582−
583− if (!allItems.isEmpty()) {
584− if (allItems.first().m_modelIndex == 0) {
585− Q_FOREACH(const ViewItem &item, allItems)
586− positionItem(item.m_modelIndex, item.m_item);
587− } else {
274+ } else {
588− Q_FOREACH(const ViewItem &item, allItems)
275+ setImplicitHeightDirty();
589− releaseItem(item);
276+ }
590 }
277 }
591 }
278 }
592−
593− m_needsRelayout = false;
594− }
595−
596− refill();
597−
598− const bool delegateRangesValid = m_delegateCreationBeginValid && m_delegateCreationEndValid;
599− const qreal from = delegateRangesValid ? m_delegateCreationBegin : 0;
600− const qreal to = delegateRangesValid ? m_delegateCreationEnd : from + height();
601− Q_FOREACH(const auto &column, m_columnVisibleItems) {
602− Q_FOREACH(const ViewItem &item, column) {
603− QQuickItemPrivate::get(item.m_item)->setCulled(item.y() + item.height() <= from || item.y() >= to);
604− }
605 }
279 }
606−
607− if (m_implicitHeightDirty) {
608− calculateImplicitHeight();
609− }
610−}
611−
612−void VerticalJournal::componentComplete()
613−{
614− if (m_delegateModel)
615− m_delegateModel->componentComplete();
616−
617− QQuickItem::componentComplete();
618−
619− m_needsRelayout = true;
620−
621− polish();
622}
280}
plugins/DashViews/verticaljournal.h+13−91
@@ -17,17 +17,7 @@
17#ifndef VERTICALJOURNAL_H
17#ifndef VERTICALJOURNAL_H
18#define VERTICALJOURNAL_H
18#define VERTICALJOURNAL_H
19
19
20−#include <QQuickItem>
20+#include "abstractjournal.h"
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
31
21
32 /** A vertical journal is a view that creates delegates
22 /** A vertical journal is a view that creates delegates
33 * based on a model and layouts them in columns following
23 * based on a model and layouts them in columns following
@@ -60,75 +50,23 @@ class QQmlDelegateModel;
60 * +-----+
50 * +-----+
61 *
51 *
62 */
52 */
53+
63− class VerticalJournal : public QQuickItem
54+class VerticalJournal : public AbstractJournal
64{
55{
65 Q_OBJECT
56 Q_OBJECT
66
57
67− Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged)
68− Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
69 Q_PROPERTY(qreal columnWidth READ columnWidth WRITE setColumnWidth NOTIFY columnWidthChanged)
58 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)
80
59
81friend class VerticalJournalTest;
60friend class VerticalJournalTest;
82
61
83public:
62public:
84 VerticalJournal();
63 VerticalJournal();
85
64
86− QAbstractItemModel *model() const;
87− void setModel(QAbstractItemModel *model);
88−
89− QQmlComponent *delegate() const;
90− void setDelegate(QQmlComponent *delegate);
91−
92 qreal columnWidth() const;
65 qreal columnWidth() const;
93 void setColumnWidth(qreal columnWidth);
66 void setColumnWidth(qreal columnWidth);
94
67
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−
109Q_SIGNALS:
68Q_SIGNALS:
110− void modelChanged();
111− void delegateChanged();
112 void columnWidthChanged();
69 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();
132
70
133private:
71private:
134 class ViewItem
72 class ViewItem
@@ -144,39 +82,23 @@ private:
144 int m_modelIndex;
82 int m_modelIndex;
145 };
83 };
146
84
147− void createDelegateModel();
148− void refill();
149− void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos);
85+ void findBottomModelIndexToAdd(int *modelIndex, qreal *yPos) override;
150− void findTopModelIndexToAdd(int *modelIndex, qreal *yPos);
86+ void findTopModelIndexToAdd(int *modelIndex, qreal *yPos) override;
151− bool addVisibleItems(qreal fillFrom, qreal fillTo, bool asynchronous);
152− bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
87+ bool removeNonVisibleItems(qreal bufferFromY, qreal bufferToY) override;
153− QQuickItem *createItem(int modelIndex, bool asynchronous);
154− void positionItem(int modelIndex, QQuickItem *item);
88+ void positionItem(int modelIndex, QQuickItem *item) override;
155− void cleanupExistingItems();
89+ void cleanupExistingItems() override;
156− void releaseItem(const ViewItem &item);
157− void calculateImplicitHeight();
90+ void calculateImplicitHeight() override;
158−
91+ void doRelayout() override;
92+ void updateItemCulling(qreal visibleFromY, qreal visibleToY) override;
159#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
93#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
160− QQuickVisualDataModel *m_delegateModel;
94+ void processModelRemoves(const QVector<QQuickChangeSet::Remove> &removes) override;
161#else
95#else
162− QQmlDelegateModel *m_delegateModel;
96+ void processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes) override;
163#endif
97#endif
164
98
165− // Index we are waiting because we requested it asynchronously
166− int m_asyncRequestedIndex;
167−
168 QVector<QList<ViewItem>> m_columnVisibleItems;
99 QVector<QList<ViewItem>> m_columnVisibleItems;
169 QHash<int, int> m_indexColumnMap;
100 QHash<int, int> m_indexColumnMap;
170 int m_columnWidth;
101 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;
180};
102};
181
103
182#endif
104#endif
plugins/Utils/qsortfilterproxymodelqml.cpp+12
@@ -68,6 +68,18 @@ QSortFilterProxyModelQML::setModel(QAbstractItemModel *itemModel)
68 }
68 }
69}
69}
70
70
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+
71QVariant
83QVariant
72QSortFilterProxyModelQML::data(int row, int role)
84QSortFilterProxyModelQML::data(int row, int role)
73{
85{
plugins/Utils/qsortfilterproxymodelqml.h+1
@@ -31,6 +31,7 @@ class QSortFilterProxyModelQML : public QSortFilterProxyModel
31public:
31public:
32 explicit QSortFilterProxyModelQML(QObject *parent = 0);
32 explicit QSortFilterProxyModelQML(QObject *parent = 0);
33
33
34+ Q_INVOKABLE QVariantMap get(int row); // Use with caution, it can be slow to query all the roles
34 Q_INVOKABLE QVariant data(int row, int role);
35 Q_INVOKABLE QVariant data(int row, int role);
35 Q_INVOKABLE int count();
36 Q_INVOKABLE int count();
36 Q_INVOKABLE int findFirst(int role, const QVariant& value) const;
37 Q_INVOKABLE int findFirst(int role, const QVariant& value) const;
po/ca.po+7−7
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11−"PO-Revision-Date: 2013-09-28 07:38+0000\n"
11+"PO-Revision-Date: 2014-01-10 07:45+0000\n"
12−"Last-Translator: David Planella <david.planella@ubuntu.com>\n"
12+"Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
13"Language-Team: Catalan <ca@li.org>\n"
13"Language-Team: Catalan <ca@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
18+"X-Launchpad-Export-Date: 2014-01-11 06:38+0000\n"
19−"X-Generator: Launchpad (build 16877)\n"
19+"X-Generator: Launchpad (build 16890)\n"
20
20
21#: Components/EdgeDemoOverlay.qml:151
21#: Components/EdgeDemoOverlay.qml:151
22msgid "Skip intro"
22msgid "Skip intro"
@@ -143,7 +143,7 @@ msgstr "S'està parlant..."
143
143
144#: Notifications/NotificationMenuItemFactory.qml:89
144#: Notifications/NotificationMenuItemFactory.qml:89
145msgid "Show password"
145msgid "Show password"
146−msgstr ""
146+msgstr "Mostra la contrasenya"
147
147
148#: Panel/SearchIndicator.qml:27
148#: Panel/SearchIndicator.qml:27
149msgid "Search"
149msgid "Search"
@@ -152,11 +152,11 @@ msgstr "Cerca"
152#: plugins/Unity/Launcher/launcheritem.cpp:43
152#: plugins/Unity/Launcher/launcheritem.cpp:43
153#: plugins/Unity/Launcher/launcheritem.cpp:73
153#: plugins/Unity/Launcher/launcheritem.cpp:73
154msgid "Lock to Launcher"
154msgid "Lock to Launcher"
155−msgstr ""
155+msgstr "Mantén-la al llançador"
156
156
157#: plugins/Unity/Launcher/launcheritem.cpp:73
157#: plugins/Unity/Launcher/launcheritem.cpp:73
158msgid "Unlock from Launcher"
158msgid "Unlock from Launcher"
159−msgstr ""
159+msgstr "No la mantinguis al llançador"
160
160
161#: Shell.qml:439
161#: Shell.qml:439
162#, qt-format
162#, qt-format
po/ckb.ponew file+158
@@ -0,0 +1,158 @@
1+# Kurdish (Sorani) translation for unity8
2+# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11+"PO-Revision-Date: 2014-01-14 20:05+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Kurdish (Sorani) <ckb@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2014-01-15 06:35+0000\n"
19+"X-Generator: Launchpad (build 16890)\n"
20+
21+#: Components/EdgeDemoOverlay.qml:151
22+msgid "Skip intro"
23+msgstr ""
24+
25+#: Components/EdgeDemo.qml:112
26+msgid "Right edge"
27+msgstr ""
28+
29+#: Components/EdgeDemo.qml:113
30+msgid "Try swiping from the right edge to unlock the phone"
31+msgstr ""
32+
33+#: Components/EdgeDemo.qml:144
34+msgid "Top edge"
35+msgstr ""
36+
37+#: Components/EdgeDemo.qml:145
38+msgid "Try swiping from the top edge to access the indicators"
39+msgstr ""
40+
41+#: Components/EdgeDemo.qml:170
42+msgid "Close"
43+msgstr ""
44+
45+#: Components/EdgeDemo.qml:171
46+msgid "Swipe up again to close the settings screen"
47+msgstr ""
48+
49+#: Components/EdgeDemo.qml:198
50+msgid "Left edge"
51+msgstr ""
52+
53+#: Components/EdgeDemo.qml:199
54+msgid "Swipe from the left to reveal the launcher for quick access to apps"
55+msgstr ""
56+
57+#: Components/EdgeDemo.qml:226
58+msgid "Well done"
59+msgstr ""
60+
61+#: Components/EdgeDemo.qml:227
62+msgid ""
63+"You have now mastered the edge gestures and can start using the "
64+"phone<br><br>Tap anywhere to start"
65+msgstr ""
66+
67+#: Components/PageHeader.qml:364
68+msgid "Recent searches"
69+msgstr ""
70+
71+#: Dash/Apps/AppPreview.qml:135
72+msgid "Rate this"
73+msgstr ""
74+
75+#. TRANSLATORS: Number of persons who rated this app/video/whatever
76+#: Dash/Previews/Header.qml:93
77+#, qt-format
78+msgid "(%1)"
79+msgstr ""
80+
81+#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
82+#: Dash/Previews/Header.qml:105
83+#, qt-format
84+msgid "%1 review"
85+msgid_plural "%1 reviews"
86+msgstr[0] ""
87+msgstr[1] ""
88+
89+#: Dash/Previews/Reviews.qml:74
90+msgid "Add a review"
91+msgstr ""
92+
93+#: Dash/Previews/Reviews.qml:86
94+msgid "Review"
95+msgstr ""
96+
97+#: Dash/Previews/Reviews.qml:121
98+msgid "Send"
99+msgstr ""
100+
101+#: Dash/Previews/Reviews.qml:139
102+msgid "Comments:"
103+msgstr ""
104+
105+#: Greeter/Lockscreen.qml:149
106+msgid "Emergency Call"
107+msgstr ""
108+
109+#: Greeter/PassphraseLockscreen.qml:59
110+#, qt-format
111+msgid "Hello %1"
112+msgstr ""
113+
114+#: Greeter/PassphraseLockscreen.qml:59
115+msgid "Hello"
116+msgstr ""
117+
118+#: Hud/HudParametrizedActionsPage.qml:132
119+msgid "Confirm"
120+msgstr ""
121+
122+#: Hud/Hud.qml:61 Hud/Hud.qml:99 Hud/Hud.qml:372
123+msgid "Type or say a command"
124+msgstr ""
125+
126+#: Hud/Hud.qml:84
127+msgid "Loading. Please Wait..."
128+msgstr ""
129+
130+#: Hud/Hud.qml:89
131+msgid "Speak Now..."
132+msgstr ""
133+
134+#: Hud/Hud.qml:94
135+msgid "Speaking..."
136+msgstr ""
137+
138+#: Notifications/NotificationMenuItemFactory.qml:89
139+msgid "Show password"
140+msgstr ""
141+
142+#: Panel/SearchIndicator.qml:27
143+msgid "Search"
144+msgstr ""
145+
146+#: plugins/Unity/Launcher/launcheritem.cpp:43
147+#: plugins/Unity/Launcher/launcheritem.cpp:73
148+msgid "Lock to Launcher"
149+msgstr ""
150+
151+#: plugins/Unity/Launcher/launcheritem.cpp:73
152+msgid "Unlock from Launcher"
153+msgstr ""
154+
155+#: Shell.qml:439
156+#, qt-format
157+msgid "Please enter %1"
158+msgstr ""
po/cs.po+9−7
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
10"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"
12"Last-Translator: Tadeáš Pařík <tadeas.parik@gmail.com>\n"
12"Last-Translator: Tadeáš Pařík <tadeas.parik@gmail.com>\n"
13"Language-Team: Czech <cs@li.org>\n"
13"Language-Team: Czech <cs@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
17"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
18+"X-Launchpad-Export-Date: 2014-01-09 05:41+0000\n"
19−"X-Generator: Launchpad (build 16877)\n"
19+"X-Generator: Launchpad (build 16884)\n"
20
20
21#: Components/EdgeDemoOverlay.qml:151
21#: Components/EdgeDemoOverlay.qml:151
22msgid "Skip intro"
22msgid "Skip intro"
@@ -44,7 +44,7 @@ msgstr "Uzavřít"
44
44
45#: Components/EdgeDemo.qml:171
45#: Components/EdgeDemo.qml:171
46msgid "Swipe up again to close the settings screen"
46msgid "Swipe up again to close the settings screen"
47−msgstr ""
47+msgstr "Přetažením nahoru dojde k uzavření obrazovky nastavení"
48
48
49#: Components/EdgeDemo.qml:198
49#: Components/EdgeDemo.qml:198
50msgid "Left edge"
50msgid "Left edge"
@@ -63,6 +63,8 @@ msgid ""
63"You have now mastered the edge gestures and can start using the "
63"You have now mastered the edge gestures and can start using the "
64"phone<br><br>Tap anywhere to start"
64"phone<br><br>Tap anywhere to start"
65msgstr ""
65msgstr ""
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"
66
68
67#: Components/PageHeader.qml:364
69#: Components/PageHeader.qml:364
68msgid "Recent searches"
70msgid "Recent searches"
@@ -138,7 +140,7 @@ msgstr "Mluvte..."
138
140
139#: Notifications/NotificationMenuItemFactory.qml:89
141#: Notifications/NotificationMenuItemFactory.qml:89
140msgid "Show password"
142msgid "Show password"
141−msgstr ""
143+msgstr "Zobrazit heslo"
142
144
143#: Panel/SearchIndicator.qml:27
145#: Panel/SearchIndicator.qml:27
144msgid "Search"
146msgid "Search"
@@ -147,11 +149,11 @@ msgstr "Hledat"
147#: plugins/Unity/Launcher/launcheritem.cpp:43
149#: plugins/Unity/Launcher/launcheritem.cpp:43
148#: plugins/Unity/Launcher/launcheritem.cpp:73
150#: plugins/Unity/Launcher/launcheritem.cpp:73
149msgid "Lock to Launcher"
151msgid "Lock to Launcher"
150−msgstr ""
152+msgstr "Přidat do Launcheru"
151
153
152#: plugins/Unity/Launcher/launcheritem.cpp:73
154#: plugins/Unity/Launcher/launcheritem.cpp:73
153msgid "Unlock from Launcher"
155msgid "Unlock from Launcher"
154−msgstr ""
156+msgstr "Odebrat z Launcheru"
155
157
156#: Shell.qml:439
158#: Shell.qml:439
157#, qt-format
159#, qt-format
po/da.ponew file+158
@@ -0,0 +1,158 @@
1+# Danish translation for unity8
2+# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11+"PO-Revision-Date: 2014-01-14 20:16+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Danish <da@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2014-01-15 06:35+0000\n"
19+"X-Generator: Launchpad (build 16890)\n"
20+
21+#: Components/EdgeDemoOverlay.qml:151
22+msgid "Skip intro"
23+msgstr ""
24+
25+#: Components/EdgeDemo.qml:112
26+msgid "Right edge"
27+msgstr ""
28+
29+#: Components/EdgeDemo.qml:113
30+msgid "Try swiping from the right edge to unlock the phone"
31+msgstr ""
32+
33+#: Components/EdgeDemo.qml:144
34+msgid "Top edge"
35+msgstr ""
36+
37+#: Components/EdgeDemo.qml:145
38+msgid "Try swiping from the top edge to access the indicators"
39+msgstr ""
40+
41+#: Components/EdgeDemo.qml:170
42+msgid "Close"
43+msgstr ""
44+
45+#: Components/EdgeDemo.qml:171
46+msgid "Swipe up again to close the settings screen"
47+msgstr ""
48+
49+#: Components/EdgeDemo.qml:198
50+msgid "Left edge"
51+msgstr ""
52+
53+#: Components/EdgeDemo.qml:199
54+msgid "Swipe from the left to reveal the launcher for quick access to apps"
55+msgstr ""
56+
57+#: Components/EdgeDemo.qml:226
58+msgid "Well done"
59+msgstr ""
60+
61+#: Components/EdgeDemo.qml:227
62+msgid ""
63+"You have now mastered the edge gestures and can start using the "
64+"phone<br><br>Tap anywhere to start"
65+msgstr ""
66+
67+#: Components/PageHeader.qml:364
68+msgid "Recent searches"
69+msgstr ""
70+
71+#: Dash/Apps/AppPreview.qml:135
72+msgid "Rate this"
73+msgstr ""
74+
75+#. TRANSLATORS: Number of persons who rated this app/video/whatever
76+#: Dash/Previews/Header.qml:93
77+#, qt-format
78+msgid "(%1)"
79+msgstr ""
80+
81+#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
82+#: Dash/Previews/Header.qml:105
83+#, qt-format
84+msgid "%1 review"
85+msgid_plural "%1 reviews"
86+msgstr[0] ""
87+msgstr[1] ""
88+
89+#: Dash/Previews/Reviews.qml:74
90+msgid "Add a review"
91+msgstr ""
92+
93+#: Dash/Previews/Reviews.qml:86
94+msgid "Review"
95+msgstr ""
96+
97+#: Dash/Previews/Reviews.qml:121
98+msgid "Send"
99+msgstr ""
100+
101+#: Dash/Previews/Reviews.qml:139
102+msgid "Comments:"
103+msgstr ""
104+
105+#: Greeter/Lockscreen.qml:149
106+msgid "Emergency Call"
107+msgstr ""
108+
109+#: Greeter/PassphraseLockscreen.qml:59
110+#, qt-format
111+msgid "Hello %1"
112+msgstr ""
113+
114+#: Greeter/PassphraseLockscreen.qml:59
115+msgid "Hello"
116+msgstr ""
117+
118+#: Hud/HudParametrizedActionsPage.qml:132
119+msgid "Confirm"
120+msgstr ""
121+
122+#: Hud/Hud.qml:61 Hud/Hud.qml:99 Hud/Hud.qml:372
123+msgid "Type or say a command"
124+msgstr ""
125+
126+#: Hud/Hud.qml:84
127+msgid "Loading. Please Wait..."
128+msgstr ""
129+
130+#: Hud/Hud.qml:89
131+msgid "Speak Now..."
132+msgstr ""
133+
134+#: Hud/Hud.qml:94
135+msgid "Speaking..."
136+msgstr ""
137+
138+#: Notifications/NotificationMenuItemFactory.qml:89
139+msgid "Show password"
140+msgstr ""
141+
142+#: Panel/SearchIndicator.qml:27
143+msgid "Search"
144+msgstr ""
145+
146+#: plugins/Unity/Launcher/launcheritem.cpp:43
147+#: plugins/Unity/Launcher/launcheritem.cpp:73
148+msgid "Lock to Launcher"
149+msgstr ""
150+
151+#: plugins/Unity/Launcher/launcheritem.cpp:73
152+msgid "Unlock from Launcher"
153+msgstr ""
154+
155+#: Shell.qml:439
156+#, qt-format
157+msgid "Please enter %1"
158+msgstr ""
po/en_GB.po+28−26
@@ -8,61 +8,63 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11−"PO-Revision-Date: 2013-06-27 08:53+0000\n"
11+"PO-Revision-Date: 2014-01-14 20:46+0000\n"
12−"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12+"Last-Translator: Dan Bishop <dan@danbishop.org>\n"
13"Language-Team: English (United Kingdom) <en_GB@li.org>\n"
13"Language-Team: English (United Kingdom) <en_GB@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
18+"X-Launchpad-Export-Date: 2014-01-15 06:35+0000\n"
19−"X-Generator: Launchpad (build 16877)\n"
19+"X-Generator: Launchpad (build 16890)\n"
20
20
21#: Components/EdgeDemoOverlay.qml:151
21#: Components/EdgeDemoOverlay.qml:151
22msgid "Skip intro"
22msgid "Skip intro"
23−msgstr ""
23+msgstr "Skip intro"
24
24
25#: Components/EdgeDemo.qml:112
25#: Components/EdgeDemo.qml:112
26msgid "Right edge"
26msgid "Right edge"
27−msgstr ""
27+msgstr "Right edge"
28
28
29#: Components/EdgeDemo.qml:113
29#: Components/EdgeDemo.qml:113
30msgid "Try swiping from the right edge to unlock the phone"
30msgid "Try swiping from the right edge to unlock the phone"
31−msgstr ""
31+msgstr "Try swiping from the right edge to unlock the phone"
32
32
33#: Components/EdgeDemo.qml:144
33#: Components/EdgeDemo.qml:144
34msgid "Top edge"
34msgid "Top edge"
35−msgstr ""
35+msgstr "Top edge"
36
36
37#: Components/EdgeDemo.qml:145
37#: Components/EdgeDemo.qml:145
38msgid "Try swiping from the top edge to access the indicators"
38msgid "Try swiping from the top edge to access the indicators"
39−msgstr ""
39+msgstr "Try swiping from the top edge to access the indicators"
40
40
41#: Components/EdgeDemo.qml:170
41#: Components/EdgeDemo.qml:170
42msgid "Close"
42msgid "Close"
43−msgstr ""
43+msgstr "Close"
44
44
45#: Components/EdgeDemo.qml:171
45#: Components/EdgeDemo.qml:171
46msgid "Swipe up again to close the settings screen"
46msgid "Swipe up again to close the settings screen"
47−msgstr ""
47+msgstr "Swipe up again to close the settings screen"
48
48
49#: Components/EdgeDemo.qml:198
49#: Components/EdgeDemo.qml:198
50msgid "Left edge"
50msgid "Left edge"
51−msgstr ""
51+msgstr "Left edge"
52
52
53#: Components/EdgeDemo.qml:199
53#: Components/EdgeDemo.qml:199
54msgid "Swipe from the left to reveal the launcher for quick access to apps"
54msgid "Swipe from the left to reveal the launcher for quick access to apps"
55−msgstr ""
55+msgstr "Swipe from the left to reveal the launcher for quick access to apps"
56
56
57#: Components/EdgeDemo.qml:226
57#: Components/EdgeDemo.qml:226
58msgid "Well done"
58msgid "Well done"
59−msgstr ""
59+msgstr "Well done"
60
60
61#: Components/EdgeDemo.qml:227
61#: Components/EdgeDemo.qml:227
62msgid ""
62msgid ""
63"You have now mastered the edge gestures and can start using the "
63"You have now mastered the edge gestures and can start using the "
64"phone<br><br>Tap anywhere to start"
64"phone<br><br>Tap anywhere to start"
65msgstr ""
65msgstr ""
66+"You have now mastered the edge gestures and can start using the "
67+"phone<br><br>Tap anywhere to start"
66
68
67#: Components/PageHeader.qml:364
69#: Components/PageHeader.qml:364
68msgid "Recent searches"
70msgid "Recent searches"
@@ -70,41 +72,41 @@ msgstr "Recent searches"
70
72
71#: Dash/Apps/AppPreview.qml:135
73#: Dash/Apps/AppPreview.qml:135
72msgid "Rate this"
74msgid "Rate this"
73−msgstr ""
75+msgstr "Rate this"
74
76
75#. TRANSLATORS: Number of persons who rated this app/video/whatever
77#. TRANSLATORS: Number of persons who rated this app/video/whatever
76#: Dash/Previews/Header.qml:93
78#: Dash/Previews/Header.qml:93
77#, qt-format
79#, qt-format
78msgid "(%1)"
80msgid "(%1)"
79−msgstr ""
81+msgstr "(%1)"
80
82
81#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
83#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
82#: Dash/Previews/Header.qml:105
84#: Dash/Previews/Header.qml:105
83#, qt-format
85#, qt-format
84msgid "%1 review"
86msgid "%1 review"
85msgid_plural "%1 reviews"
87msgid_plural "%1 reviews"
86−msgstr[0] ""
88+msgstr[0] "%1 review"
87−msgstr[1] ""
89+msgstr[1] "%1 reviews"
88
90
89#: Dash/Previews/Reviews.qml:74
91#: Dash/Previews/Reviews.qml:74
90msgid "Add a review"
92msgid "Add a review"
91−msgstr ""
93+msgstr "Add a review"
92
94
93#: Dash/Previews/Reviews.qml:86
95#: Dash/Previews/Reviews.qml:86
94msgid "Review"
96msgid "Review"
95−msgstr ""
97+msgstr "Review"
96
98
97#: Dash/Previews/Reviews.qml:121
99#: Dash/Previews/Reviews.qml:121
98msgid "Send"
100msgid "Send"
99−msgstr ""
101+msgstr "Send"
100
102
101#: Dash/Previews/Reviews.qml:139
103#: Dash/Previews/Reviews.qml:139
102msgid "Comments:"
104msgid "Comments:"
103−msgstr ""
105+msgstr "Comments:"
104
106
105#: Greeter/Lockscreen.qml:149
107#: Greeter/Lockscreen.qml:149
106msgid "Emergency Call"
108msgid "Emergency Call"
107−msgstr ""
109+msgstr "Emergency Call"
108
110
109#: Greeter/PassphraseLockscreen.qml:59
111#: Greeter/PassphraseLockscreen.qml:59
110#, qt-format
112#, qt-format
@@ -137,7 +139,7 @@ msgstr "Speaking..."
137
139
138#: Notifications/NotificationMenuItemFactory.qml:89
140#: Notifications/NotificationMenuItemFactory.qml:89
139msgid "Show password"
141msgid "Show password"
140−msgstr ""
142+msgstr "Show password"
141
143
142#: Panel/SearchIndicator.qml:27
144#: Panel/SearchIndicator.qml:27
143msgid "Search"
145msgid "Search"
@@ -146,11 +148,11 @@ msgstr "Search"
146#: plugins/Unity/Launcher/launcheritem.cpp:43
148#: plugins/Unity/Launcher/launcheritem.cpp:43
147#: plugins/Unity/Launcher/launcheritem.cpp:73
149#: plugins/Unity/Launcher/launcheritem.cpp:73
148msgid "Lock to Launcher"
150msgid "Lock to Launcher"
149−msgstr ""
151+msgstr "Lock to Launcher"
150
152
151#: plugins/Unity/Launcher/launcheritem.cpp:73
153#: plugins/Unity/Launcher/launcheritem.cpp:73
152msgid "Unlock from Launcher"
154msgid "Unlock from Launcher"
153−msgstr ""
155+msgstr "Unlock from Launcher"
154
156
155#: Shell.qml:439
157#: Shell.qml:439
156#, qt-format
158#, qt-format
po/es.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11−"PO-Revision-Date: 2013-11-20 08:46+0000\n"
11+"PO-Revision-Date: 2014-01-14 21:06+0000\n"
12"Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
12"Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
13"Language-Team: Spanish <es@li.org>\n"
13"Language-Team: Spanish <es@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
18+"X-Launchpad-Export-Date: 2014-01-15 06:35+0000\n"
19−"X-Generator: Launchpad (build 16877)\n"
19+"X-Generator: Launchpad (build 16890)\n"
20
20
21#: Components/EdgeDemoOverlay.qml:151
21#: Components/EdgeDemoOverlay.qml:151
22msgid "Skip intro"
22msgid "Skip intro"
@@ -113,7 +113,7 @@ msgstr "Llamada de emergencia"
113#: Greeter/PassphraseLockscreen.qml:59
113#: Greeter/PassphraseLockscreen.qml:59
114#, qt-format
114#, qt-format
115msgid "Hello %1"
115msgid "Hello %1"
116−msgstr "Hola %1"
116+msgstr "Hola, %1"
117
117
118#: Greeter/PassphraseLockscreen.qml:59
118#: Greeter/PassphraseLockscreen.qml:59
119msgid "Hello"
119msgid "Hello"
po/fo.po+5−5
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11−"PO-Revision-Date: 2013-10-01 15:13+0000\n"
11+"PO-Revision-Date: 2014-01-14 20:08+0000\n"
12"Last-Translator: Jógvan Olsen <jeggy@jebster.net>\n"
12"Last-Translator: Jógvan Olsen <jeggy@jebster.net>\n"
13"Language-Team: Faroese <fo@li.org>\n"
13"Language-Team: Faroese <fo@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
18+"X-Launchpad-Export-Date: 2014-01-15 06:35+0000\n"
19−"X-Generator: Launchpad (build 16877)\n"
19+"X-Generator: Launchpad (build 16890)\n"
20
20
21#: Components/EdgeDemoOverlay.qml:151
21#: Components/EdgeDemoOverlay.qml:151
22msgid "Skip intro"
22msgid "Skip intro"
@@ -24,7 +24,7 @@ msgstr ""
24
24
25#: Components/EdgeDemo.qml:112
25#: Components/EdgeDemo.qml:112
26msgid "Right edge"
26msgid "Right edge"
27−msgstr ""
27+msgstr "Høgra kant"
28
28
29#: Components/EdgeDemo.qml:113
29#: Components/EdgeDemo.qml:113
30msgid "Try swiping from the right edge to unlock the phone"
30msgid "Try swiping from the right edge to unlock the phone"
@@ -32,7 +32,7 @@ msgstr ""
32
32
33#: Components/EdgeDemo.qml:144
33#: Components/EdgeDemo.qml:144
34msgid "Top edge"
34msgid "Top edge"
35−msgstr ""
35+msgstr "Topp kantur"
36
36
37#: Components/EdgeDemo.qml:145
37#: Components/EdgeDemo.qml:145
38msgid "Try swiping from the top edge to access the indicators"
38msgid "Try swiping from the top edge to access the indicators"
po/hi.ponew file+158
@@ -0,0 +1,158 @@
1+# Hindi translation for unity8
2+# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11+"PO-Revision-Date: 2014-01-15 01:49+0000\n"
12+"Last-Translator: Siddhanathan S. <Unknown>\n"
13+"Language-Team: Hindi <hi@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2014-01-15 06:35+0000\n"
19+"X-Generator: Launchpad (build 16890)\n"
20+
21+#: Components/EdgeDemoOverlay.qml:151
22+msgid "Skip intro"
23+msgstr "परिचय छोड़"
24+
25+#: Components/EdgeDemo.qml:112
26+msgid "Right edge"
27+msgstr "दायाँ किनारा"
28+
29+#: Components/EdgeDemo.qml:113
30+msgid "Try swiping from the right edge to unlock the phone"
31+msgstr ""
32+
33+#: Components/EdgeDemo.qml:144
34+msgid "Top edge"
35+msgstr "ऊपर किनारे"
36+
37+#: Components/EdgeDemo.qml:145
38+msgid "Try swiping from the top edge to access the indicators"
39+msgstr ""
40+
41+#: Components/EdgeDemo.qml:170
42+msgid "Close"
43+msgstr "बंद करें"
44+
45+#: Components/EdgeDemo.qml:171
46+msgid "Swipe up again to close the settings screen"
47+msgstr ""
48+
49+#: Components/EdgeDemo.qml:198
50+msgid "Left edge"
51+msgstr "बायाँ किनारा"
52+
53+#: Components/EdgeDemo.qml:199
54+msgid "Swipe from the left to reveal the launcher for quick access to apps"
55+msgstr ""
56+
57+#: Components/EdgeDemo.qml:226
58+msgid "Well done"
59+msgstr "शाबाश"
60+
61+#: Components/EdgeDemo.qml:227
62+msgid ""
63+"You have now mastered the edge gestures and can start using the "
64+"phone<br><br>Tap anywhere to start"
65+msgstr ""
66+
67+#: Components/PageHeader.qml:364
68+msgid "Recent searches"
69+msgstr "पिछले खोजों"
70+
71+#: Dash/Apps/AppPreview.qml:135
72+msgid "Rate this"
73+msgstr ""
74+
75+#. TRANSLATORS: Number of persons who rated this app/video/whatever
76+#: Dash/Previews/Header.qml:93
77+#, qt-format
78+msgid "(%1)"
79+msgstr ""
80+
81+#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
82+#: Dash/Previews/Header.qml:105
83+#, qt-format
84+msgid "%1 review"
85+msgid_plural "%1 reviews"
86+msgstr[0] ""
87+msgstr[1] ""
88+
89+#: Dash/Previews/Reviews.qml:74
90+msgid "Add a review"
91+msgstr "समीक्षा दे"
92+
93+#: Dash/Previews/Reviews.qml:86
94+msgid "Review"
95+msgstr "समीक्षा"
96+
97+#: Dash/Previews/Reviews.qml:121
98+msgid "Send"
99+msgstr "भेजें"
100+
101+#: Dash/Previews/Reviews.qml:139
102+msgid "Comments:"
103+msgstr ""
104+
105+#: Greeter/Lockscreen.qml:149
106+msgid "Emergency Call"
107+msgstr "आपातकालीन कॉल"
108+
109+#: Greeter/PassphraseLockscreen.qml:59
110+#, qt-format
111+msgid "Hello %1"
112+msgstr ""
113+
114+#: Greeter/PassphraseLockscreen.qml:59
115+msgid "Hello"
116+msgstr "नमस्ते"
117+
118+#: Hud/HudParametrizedActionsPage.qml:132
119+msgid "Confirm"
120+msgstr ""
121+
122+#: Hud/Hud.qml:61 Hud/Hud.qml:99 Hud/Hud.qml:372
123+msgid "Type or say a command"
124+msgstr ""
125+
126+#: Hud/Hud.qml:84
127+msgid "Loading. Please Wait..."
128+msgstr "काम कर रहा है | कृपया प्रतीक्षा करे |"
129+
130+#: Hud/Hud.qml:89
131+msgid "Speak Now..."
132+msgstr "अब बात करें"
133+
134+#: Hud/Hud.qml:94
135+msgid "Speaking..."
136+msgstr "अब बोल रहे हैं"
137+
138+#: Notifications/NotificationMenuItemFactory.qml:89
139+msgid "Show password"
140+msgstr ""
141+
142+#: Panel/SearchIndicator.qml:27
143+msgid "Search"
144+msgstr "खोजें"
145+
146+#: plugins/Unity/Launcher/launcheritem.cpp:43
147+#: plugins/Unity/Launcher/launcheritem.cpp:73
148+msgid "Lock to Launcher"
149+msgstr ""
150+
151+#: plugins/Unity/Launcher/launcheritem.cpp:73
152+msgid "Unlock from Launcher"
153+msgstr ""
154+
155+#: Shell.qml:439
156+#, qt-format
157+msgid "Please enter %1"
158+msgstr ""
po/hu.po+6−6
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11−"PO-Revision-Date: 2014-01-04 15:30+0000\n"
11+"PO-Revision-Date: 2014-01-14 23:02+0000\n"
12−"Last-Translator: Péter MÁTÓ <mato.peter@gmail.com>\n"
12+"Last-Translator: Kristóf Kiszel <Unknown>\n"
13"Language-Team: Hungarian <hu@li.org>\n"
13"Language-Team: Hungarian <hu@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2014-01-05 05:50+0000\n"
18+"X-Launchpad-Export-Date: 2014-01-15 06:35+0000\n"
19−"X-Generator: Launchpad (build 16877)\n"
19+"X-Generator: Launchpad (build 16890)\n"
20
20
21#: Components/EdgeDemoOverlay.qml:151
21#: Components/EdgeDemoOverlay.qml:151
22msgid "Skip intro"
22msgid "Skip intro"
@@ -133,7 +133,7 @@ msgstr "Betöltés, kis türelmet…"
133
133
134#: Hud/Hud.qml:89
134#: Hud/Hud.qml:89
135msgid "Speak Now..."
135msgid "Speak Now..."
136−msgstr "Beszéljen most…"
136+msgstr "Most beszéljen…"
137
137
138#: Hud/Hud.qml:94
138#: Hud/Hud.qml:94
139msgid "Speaking..."
139msgid "Speaking..."
@@ -159,4 +159,4 @@ msgstr "Eltávolítás az Indítóból"
159#: Shell.qml:439
159#: Shell.qml:439
160#, qt-format
160#, qt-format
161msgid "Please enter %1"
161msgid "Please enter %1"
162−msgstr ""
162+msgstr "Adja meg a következőt: %1"
po/lv.po+6−6
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11−"PO-Revision-Date: 2013-09-28 16:29+0000\n"
11+"PO-Revision-Date: 2014-01-11 16:48+0000\n"
12"Last-Translator: Jānis-Marks Gailis <jeanmarc.gailis@gmail.com>\n"
12"Last-Translator: Jānis-Marks Gailis <jeanmarc.gailis@gmail.com>\n"
13"Language-Team: Latvian <lv@li.org>\n"
13"Language-Team: Latvian <lv@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
18+"X-Launchpad-Export-Date: 2014-01-12 05:52+0000\n"
19−"X-Generator: Launchpad (build 16877)\n"
19+"X-Generator: Launchpad (build 16890)\n"
20
20
21#: Components/EdgeDemoOverlay.qml:151
21#: Components/EdgeDemoOverlay.qml:151
22msgid "Skip intro"
22msgid "Skip intro"
@@ -32,7 +32,7 @@ msgstr ""
32
32
33#: Components/EdgeDemo.qml:144
33#: Components/EdgeDemo.qml:144
34msgid "Top edge"
34msgid "Top edge"
35−msgstr ""
35+msgstr "Augšējā mala"
36
36
37#: Components/EdgeDemo.qml:145
37#: Components/EdgeDemo.qml:145
38msgid "Try swiping from the top edge to access the indicators"
38msgid "Try swiping from the top edge to access the indicators"
@@ -40,7 +40,7 @@ msgstr ""
40
40
41#: Components/EdgeDemo.qml:170
41#: Components/EdgeDemo.qml:170
42msgid "Close"
42msgid "Close"
43−msgstr ""
43+msgstr "Aizvērt"
44
44
45#: Components/EdgeDemo.qml:171
45#: Components/EdgeDemo.qml:171
46msgid "Swipe up again to close the settings screen"
46msgid "Swipe up again to close the settings screen"
@@ -48,7 +48,7 @@ msgstr ""
48
48
49#: Components/EdgeDemo.qml:198
49#: Components/EdgeDemo.qml:198
50msgid "Left edge"
50msgid "Left edge"
51−msgstr ""
51+msgstr "Kreisā mala"
52
52
53#: Components/EdgeDemo.qml:199
53#: Components/EdgeDemo.qml:199
54msgid "Swipe from the left to reveal the launcher for quick access to apps"
54msgid "Swipe from the left to reveal the launcher for quick access to apps"
po/mr.ponew file+158
@@ -0,0 +1,158 @@
1+# Marathi translation for unity8
2+# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11+"PO-Revision-Date: 2014-01-14 19:54+0000\n"
12+"Last-Translator: Rohan Deshmukh <Unknown>\n"
13+"Language-Team: Marathi <mr@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2014-01-15 06:35+0000\n"
19+"X-Generator: Launchpad (build 16890)\n"
20+
21+#: Components/EdgeDemoOverlay.qml:151
22+msgid "Skip intro"
23+msgstr "परिचय वगळा"
24+
25+#: Components/EdgeDemo.qml:112
26+msgid "Right edge"
27+msgstr "उजवा काठ"
28+
29+#: Components/EdgeDemo.qml:113
30+msgid "Try swiping from the right edge to unlock the phone"
31+msgstr "फोन अनलॉक करण्यासाठी उजव्या काठावरुन स्वाइप करण्याचा प्रयत्न करा"
32+
33+#: Components/EdgeDemo.qml:144
34+msgid "Top edge"
35+msgstr "वरचा काठ"
36+
37+#: Components/EdgeDemo.qml:145
38+msgid "Try swiping from the top edge to access the indicators"
39+msgstr "इंडिकेटर वापरण्याकरिता वरचा काठावरून स्वाइप करण्याचा प्रयत्न करा"
40+
41+#: Components/EdgeDemo.qml:170
42+msgid "Close"
43+msgstr "बंद करा"
44+
45+#: Components/EdgeDemo.qml:171
46+msgid "Swipe up again to close the settings screen"
47+msgstr "सेटिंग्ज स्क्रीन बंद करण्यासाठी पुन्हा एकदा स्वाइप करा"
48+
49+#: Components/EdgeDemo.qml:198
50+msgid "Left edge"
51+msgstr "डावा काठ"
52+
53+#: Components/EdgeDemo.qml:199
54+msgid "Swipe from the left to reveal the launcher for quick access to apps"
55+msgstr "लाँचर वरील अॕप्प्स त्वरित वापरण्याकरिता डाव्या बाजूने स्वाइप करा"
56+
57+#: Components/EdgeDemo.qml:226
58+msgid "Well done"
59+msgstr "कौशल्यपूर्वक केले"
60+
61+#: Components/EdgeDemo.qml:227
62+msgid ""
63+"You have now mastered the edge gestures and can start using the "
64+"phone<br><br>Tap anywhere to start"
65+msgstr ""
66+
67+#: Components/PageHeader.qml:364
68+msgid "Recent searches"
69+msgstr ""
70+
71+#: Dash/Apps/AppPreview.qml:135
72+msgid "Rate this"
73+msgstr ""
74+
75+#. TRANSLATORS: Number of persons who rated this app/video/whatever
76+#: Dash/Previews/Header.qml:93
77+#, qt-format
78+msgid "(%1)"
79+msgstr ""
80+
81+#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
82+#: Dash/Previews/Header.qml:105
83+#, qt-format
84+msgid "%1 review"
85+msgid_plural "%1 reviews"
86+msgstr[0] ""
87+msgstr[1] ""
88+
89+#: Dash/Previews/Reviews.qml:74
90+msgid "Add a review"
91+msgstr ""
92+
93+#: Dash/Previews/Reviews.qml:86
94+msgid "Review"
95+msgstr ""
96+
97+#: Dash/Previews/Reviews.qml:121
98+msgid "Send"
99+msgstr ""
100+
101+#: Dash/Previews/Reviews.qml:139
102+msgid "Comments:"
103+msgstr ""
104+
105+#: Greeter/Lockscreen.qml:149
106+msgid "Emergency Call"
107+msgstr ""
108+
109+#: Greeter/PassphraseLockscreen.qml:59
110+#, qt-format
111+msgid "Hello %1"
112+msgstr ""
113+
114+#: Greeter/PassphraseLockscreen.qml:59
115+msgid "Hello"
116+msgstr ""
117+
118+#: Hud/HudParametrizedActionsPage.qml:132
119+msgid "Confirm"
120+msgstr ""
121+
122+#: Hud/Hud.qml:61 Hud/Hud.qml:99 Hud/Hud.qml:372
123+msgid "Type or say a command"
124+msgstr ""
125+
126+#: Hud/Hud.qml:84
127+msgid "Loading. Please Wait..."
128+msgstr ""
129+
130+#: Hud/Hud.qml:89
131+msgid "Speak Now..."
132+msgstr ""
133+
134+#: Hud/Hud.qml:94
135+msgid "Speaking..."
136+msgstr ""
137+
138+#: Notifications/NotificationMenuItemFactory.qml:89
139+msgid "Show password"
140+msgstr ""
141+
142+#: Panel/SearchIndicator.qml:27
143+msgid "Search"
144+msgstr ""
145+
146+#: plugins/Unity/Launcher/launcheritem.cpp:43
147+#: plugins/Unity/Launcher/launcheritem.cpp:73
148+msgid "Lock to Launcher"
149+msgstr ""
150+
151+#: plugins/Unity/Launcher/launcheritem.cpp:73
152+msgid "Unlock from Launcher"
153+msgstr ""
154+
155+#: Shell.qml:439
156+#, qt-format
157+msgid "Please enter %1"
158+msgstr ""
po/pl.po+7−7
@@ -8,16 +8,16 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11−"PO-Revision-Date: 2013-10-13 13:44+0000\n"
11+"PO-Revision-Date: 2014-01-10 11:38+0000\n"
12−"Last-Translator: GTriderXC <gtriderxc@yahoo.com>\n"
12+"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Polish <pl@li.org>\n"
13"Language-Team: Polish <pl@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
17"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
18"|| n%100>=20) ? 1 : 2;\n"
18"|| n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Launchpad-Export-Date: 2014-01-11 06:38+0000\n"
20−"X-Generator: Launchpad (build 16877)\n"
20+"X-Generator: Launchpad (build 16890)\n"
21
21
22#: Components/EdgeDemoOverlay.qml:151
22#: Components/EdgeDemoOverlay.qml:151
23msgid "Skip intro"
23msgid "Skip intro"
@@ -143,7 +143,7 @@ msgstr "Rozpoznanie..."
143
143
144#: Notifications/NotificationMenuItemFactory.qml:89
144#: Notifications/NotificationMenuItemFactory.qml:89
145msgid "Show password"
145msgid "Show password"
146−msgstr ""
146+msgstr "Wyświetl hasło"
147
147
148#: Panel/SearchIndicator.qml:27
148#: Panel/SearchIndicator.qml:27
149msgid "Search"
149msgid "Search"
@@ -152,11 +152,11 @@ msgstr "Szukaj"
152#: plugins/Unity/Launcher/launcheritem.cpp:43
152#: plugins/Unity/Launcher/launcheritem.cpp:43
153#: plugins/Unity/Launcher/launcheritem.cpp:73
153#: plugins/Unity/Launcher/launcheritem.cpp:73
154msgid "Lock to Launcher"
154msgid "Lock to Launcher"
155−msgstr ""
155+msgstr "Dodaj do panelu uruchamiania"
156
156
157#: plugins/Unity/Launcher/launcheritem.cpp:73
157#: plugins/Unity/Launcher/launcheritem.cpp:73
158msgid "Unlock from Launcher"
158msgid "Unlock from Launcher"
159−msgstr ""
159+msgstr "Usuń z panelu uruchamiania"
160
160
161#: Shell.qml:439
161#: Shell.qml:439
162#, qt-format
162#, qt-format
po/pt.ponew file+158
@@ -0,0 +1,158 @@
1+# Portuguese translation for unity8
2+# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11+"PO-Revision-Date: 2014-01-14 19:18+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Portuguese <pt@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2014-01-15 06:35+0000\n"
19+"X-Generator: Launchpad (build 16890)\n"
20+
21+#: Components/EdgeDemoOverlay.qml:151
22+msgid "Skip intro"
23+msgstr ""
24+
25+#: Components/EdgeDemo.qml:112
26+msgid "Right edge"
27+msgstr ""
28+
29+#: Components/EdgeDemo.qml:113
30+msgid "Try swiping from the right edge to unlock the phone"
31+msgstr ""
32+
33+#: Components/EdgeDemo.qml:144
34+msgid "Top edge"
35+msgstr ""
36+
37+#: Components/EdgeDemo.qml:145
38+msgid "Try swiping from the top edge to access the indicators"
39+msgstr ""
40+
41+#: Components/EdgeDemo.qml:170
42+msgid "Close"
43+msgstr ""
44+
45+#: Components/EdgeDemo.qml:171
46+msgid "Swipe up again to close the settings screen"
47+msgstr ""
48+
49+#: Components/EdgeDemo.qml:198
50+msgid "Left edge"
51+msgstr ""
52+
53+#: Components/EdgeDemo.qml:199
54+msgid "Swipe from the left to reveal the launcher for quick access to apps"
55+msgstr ""
56+
57+#: Components/EdgeDemo.qml:226
58+msgid "Well done"
59+msgstr ""
60+
61+#: Components/EdgeDemo.qml:227
62+msgid ""
63+"You have now mastered the edge gestures and can start using the "
64+"phone<br><br>Tap anywhere to start"
65+msgstr ""
66+
67+#: Components/PageHeader.qml:364
68+msgid "Recent searches"
69+msgstr ""
70+
71+#: Dash/Apps/AppPreview.qml:135
72+msgid "Rate this"
73+msgstr ""
74+
75+#. TRANSLATORS: Number of persons who rated this app/video/whatever
76+#: Dash/Previews/Header.qml:93
77+#, qt-format
78+msgid "(%1)"
79+msgstr ""
80+
81+#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
82+#: Dash/Previews/Header.qml:105
83+#, qt-format
84+msgid "%1 review"
85+msgid_plural "%1 reviews"
86+msgstr[0] ""
87+msgstr[1] ""
88+
89+#: Dash/Previews/Reviews.qml:74
90+msgid "Add a review"
91+msgstr ""
92+
93+#: Dash/Previews/Reviews.qml:86
94+msgid "Review"
95+msgstr ""
96+
97+#: Dash/Previews/Reviews.qml:121
98+msgid "Send"
99+msgstr ""
100+
101+#: Dash/Previews/Reviews.qml:139
102+msgid "Comments:"
103+msgstr ""
104+
105+#: Greeter/Lockscreen.qml:149
106+msgid "Emergency Call"
107+msgstr ""
108+
109+#: Greeter/PassphraseLockscreen.qml:59
110+#, qt-format
111+msgid "Hello %1"
112+msgstr ""
113+
114+#: Greeter/PassphraseLockscreen.qml:59
115+msgid "Hello"
116+msgstr ""
117+
118+#: Hud/HudParametrizedActionsPage.qml:132
119+msgid "Confirm"
120+msgstr ""
121+
122+#: Hud/Hud.qml:61 Hud/Hud.qml:99 Hud/Hud.qml:372
123+msgid "Type or say a command"
124+msgstr ""
125+
126+#: Hud/Hud.qml:84
127+msgid "Loading. Please Wait..."
128+msgstr ""
129+
130+#: Hud/Hud.qml:89
131+msgid "Speak Now..."
132+msgstr ""
133+
134+#: Hud/Hud.qml:94
135+msgid "Speaking..."
136+msgstr ""
137+
138+#: Notifications/NotificationMenuItemFactory.qml:89
139+msgid "Show password"
140+msgstr ""
141+
142+#: Panel/SearchIndicator.qml:27
143+msgid "Search"
144+msgstr ""
145+
146+#: plugins/Unity/Launcher/launcheritem.cpp:43
147+#: plugins/Unity/Launcher/launcheritem.cpp:73
148+msgid "Lock to Launcher"
149+msgstr ""
150+
151+#: plugins/Unity/Launcher/launcheritem.cpp:73
152+msgid "Unlock from Launcher"
153+msgstr ""
154+
155+#: Shell.qml:439
156+#, qt-format
157+msgid "Please enter %1"
158+msgstr ""
po/ru.po+11−8
@@ -8,16 +8,16 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11−"PO-Revision-Date: 2013-10-01 05:55+0000\n"
11+"PO-Revision-Date: 2014-01-14 19:17+0000\n"
12−"Last-Translator: Zonov Roman <Unknown>\n"
12+"Last-Translator: alex <sos987@gmail.com>\n"
13"Language-Team: Russian <ru@li.org>\n"
13"Language-Team: Russian <ru@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
19+"X-Launchpad-Export-Date: 2014-01-15 06:35+0000\n"
20−"X-Generator: Launchpad (build 16877)\n"
20+"X-Generator: Launchpad (build 16890)\n"
21
21
22#: Components/EdgeDemoOverlay.qml:151
22#: Components/EdgeDemoOverlay.qml:151
23msgid "Skip intro"
23msgid "Skip intro"
@@ -69,6 +69,8 @@ msgid ""
69"You have now mastered the edge gestures and can start using the "
69"You have now mastered the edge gestures and can start using the "
70"phone<br><br>Tap anywhere to start"
70"phone<br><br>Tap anywhere to start"
71msgstr ""
71msgstr ""
72+"Теперь, когда вы изучили основные движения, вы можете начать использовать "
73+"телефон.<br><br>Нажмите, чтобы начать"
72
74
73#: Components/PageHeader.qml:364
75#: Components/PageHeader.qml:364
74msgid "Recent searches"
76msgid "Recent searches"
@@ -89,7 +91,8 @@ msgstr "(%1)"
89#, qt-format
91#, qt-format
90msgid "%1 review"
92msgid "%1 review"
91msgid_plural "%1 reviews"
93msgid_plural "%1 reviews"
92−msgstr[0] "%1 отзыв"
94+msgstr[0] "%1 отзыва"
95+msgstr[1] "%1 отзывов"
93
96
94#: Dash/Previews/Reviews.qml:74
97#: Dash/Previews/Reviews.qml:74
95msgid "Add a review"
98msgid "Add a review"
@@ -142,7 +145,7 @@ msgstr "Говорите..."
142
145
143#: Notifications/NotificationMenuItemFactory.qml:89
146#: Notifications/NotificationMenuItemFactory.qml:89
144msgid "Show password"
147msgid "Show password"
145−msgstr ""
148+msgstr "Показать пароль"
146
149
147#: Panel/SearchIndicator.qml:27
150#: Panel/SearchIndicator.qml:27
148msgid "Search"
151msgid "Search"
@@ -151,11 +154,11 @@ msgstr "Поиск"
151#: plugins/Unity/Launcher/launcheritem.cpp:43
154#: plugins/Unity/Launcher/launcheritem.cpp:43
152#: plugins/Unity/Launcher/launcheritem.cpp:73
155#: plugins/Unity/Launcher/launcheritem.cpp:73
153msgid "Lock to Launcher"
156msgid "Lock to Launcher"
154−msgstr ""
157+msgstr "Закрепить на панели"
155
158
156#: plugins/Unity/Launcher/launcheritem.cpp:73
159#: plugins/Unity/Launcher/launcheritem.cpp:73
157msgid "Unlock from Launcher"
160msgid "Unlock from Launcher"
158−msgstr ""
161+msgstr "Убрать с панели"
159
162
160#: Shell.qml:439
163#: Shell.qml:439
161#, qt-format
164#, qt-format
po/sw.ponew file+158
@@ -0,0 +1,158 @@
1+# Swahili translation for unity8
2+# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11+"PO-Revision-Date: 2014-01-14 19:28+0000\n"
12+"Last-Translator: Damas Mlabwa <Unknown>\n"
13+"Language-Team: Swahili <sw@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2014-01-15 06:35+0000\n"
19+"X-Generator: Launchpad (build 16890)\n"
20+
21+#: Components/EdgeDemoOverlay.qml:151
22+msgid "Skip intro"
23+msgstr ""
24+
25+#: Components/EdgeDemo.qml:112
26+msgid "Right edge"
27+msgstr ""
28+
29+#: Components/EdgeDemo.qml:113
30+msgid "Try swiping from the right edge to unlock the phone"
31+msgstr ""
32+
33+#: Components/EdgeDemo.qml:144
34+msgid "Top edge"
35+msgstr ""
36+
37+#: Components/EdgeDemo.qml:145
38+msgid "Try swiping from the top edge to access the indicators"
39+msgstr ""
40+
41+#: Components/EdgeDemo.qml:170
42+msgid "Close"
43+msgstr "Funga"
44+
45+#: Components/EdgeDemo.qml:171
46+msgid "Swipe up again to close the settings screen"
47+msgstr ""
48+
49+#: Components/EdgeDemo.qml:198
50+msgid "Left edge"
51+msgstr ""
52+
53+#: Components/EdgeDemo.qml:199
54+msgid "Swipe from the left to reveal the launcher for quick access to apps"
55+msgstr ""
56+
57+#: Components/EdgeDemo.qml:226
58+msgid "Well done"
59+msgstr ""
60+
61+#: Components/EdgeDemo.qml:227
62+msgid ""
63+"You have now mastered the edge gestures and can start using the "
64+"phone<br><br>Tap anywhere to start"
65+msgstr ""
66+
67+#: Components/PageHeader.qml:364
68+msgid "Recent searches"
69+msgstr ""
70+
71+#: Dash/Apps/AppPreview.qml:135
72+msgid "Rate this"
73+msgstr ""
74+
75+#. TRANSLATORS: Number of persons who rated this app/video/whatever
76+#: Dash/Previews/Header.qml:93
77+#, qt-format
78+msgid "(%1)"
79+msgstr ""
80+
81+#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
82+#: Dash/Previews/Header.qml:105
83+#, qt-format
84+msgid "%1 review"
85+msgid_plural "%1 reviews"
86+msgstr[0] ""
87+msgstr[1] ""
88+
89+#: Dash/Previews/Reviews.qml:74
90+msgid "Add a review"
91+msgstr ""
92+
93+#: Dash/Previews/Reviews.qml:86
94+msgid "Review"
95+msgstr ""
96+
97+#: Dash/Previews/Reviews.qml:121
98+msgid "Send"
99+msgstr "Tuma"
100+
101+#: Dash/Previews/Reviews.qml:139
102+msgid "Comments:"
103+msgstr "Maoni:"
104+
105+#: Greeter/Lockscreen.qml:149
106+msgid "Emergency Call"
107+msgstr ""
108+
109+#: Greeter/PassphraseLockscreen.qml:59
110+#, qt-format
111+msgid "Hello %1"
112+msgstr ""
113+
114+#: Greeter/PassphraseLockscreen.qml:59
115+msgid "Hello"
116+msgstr ""
117+
118+#: Hud/HudParametrizedActionsPage.qml:132
119+msgid "Confirm"
120+msgstr "Thibitisha"
121+
122+#: Hud/Hud.qml:61 Hud/Hud.qml:99 Hud/Hud.qml:372
123+msgid "Type or say a command"
124+msgstr ""
125+
126+#: Hud/Hud.qml:84
127+msgid "Loading. Please Wait..."
128+msgstr ""
129+
130+#: Hud/Hud.qml:89
131+msgid "Speak Now..."
132+msgstr ""
133+
134+#: Hud/Hud.qml:94
135+msgid "Speaking..."
136+msgstr ""
137+
138+#: Notifications/NotificationMenuItemFactory.qml:89
139+msgid "Show password"
140+msgstr ""
141+
142+#: Panel/SearchIndicator.qml:27
143+msgid "Search"
144+msgstr "Tafuta"
145+
146+#: plugins/Unity/Launcher/launcheritem.cpp:43
147+#: plugins/Unity/Launcher/launcheritem.cpp:73
148+msgid "Lock to Launcher"
149+msgstr ""
150+
151+#: plugins/Unity/Launcher/launcheritem.cpp:73
152+msgid "Unlock from Launcher"
153+msgstr ""
154+
155+#: Shell.qml:439
156+#, qt-format
157+msgid "Please enter %1"
158+msgstr ""
po/tr.po+6−4
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11−"PO-Revision-Date: 2013-12-12 16:06+0000\n"
11+"PO-Revision-Date: 2014-01-14 19:05+0000\n"
12−"Last-Translator: Arda Ünlü <aarda.uunlu@gmail.com>\n"
12+"Last-Translator: Osman Coşkun <Unknown>\n"
13"Language-Team: Turkish <tr@li.org>\n"
13"Language-Team: Turkish <tr@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-12-29 06:04+0000\n"
18+"X-Launchpad-Export-Date: 2014-01-15 06:35+0000\n"
19−"X-Generator: Launchpad (build 16877)\n"
19+"X-Generator: Launchpad (build 16890)\n"
20
20
21#: Components/EdgeDemoOverlay.qml:151
21#: Components/EdgeDemoOverlay.qml:151
22msgid "Skip intro"
22msgid "Skip intro"
@@ -64,6 +64,8 @@ msgid ""
64"You have now mastered the edge gestures and can start using the "
64"You have now mastered the edge gestures and can start using the "
65"phone<br><br>Tap anywhere to start"
65"phone<br><br>Tap anywhere to start"
66msgstr ""
66msgstr ""
67+"Kenar hareketlerini öğrendiniz ve herhangi bir yere dokunarak telefonunuzu "
68+"<br><br> kullanmaya başlayabilirsiniz"
67
69
68#: Components/PageHeader.qml:364
70#: Components/PageHeader.qml:364
69msgid "Recent searches"
71msgid "Recent searches"
po/vi.ponew file+158
@@ -0,0 +1,158 @@
1+# Vietnamese translation for unity8
2+# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11+"PO-Revision-Date: 2014-01-15 01:19+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Vietnamese <vi@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18+"X-Launchpad-Export-Date: 2014-01-15 06:35+0000\n"
19+"X-Generator: Launchpad (build 16890)\n"
20+
21+#: Components/EdgeDemoOverlay.qml:151
22+msgid "Skip intro"
23+msgstr ""
24+
25+#: Components/EdgeDemo.qml:112
26+msgid "Right edge"
27+msgstr ""
28+
29+#: Components/EdgeDemo.qml:113
30+msgid "Try swiping from the right edge to unlock the phone"
31+msgstr ""
32+
33+#: Components/EdgeDemo.qml:144
34+msgid "Top edge"
35+msgstr ""
36+
37+#: Components/EdgeDemo.qml:145
38+msgid "Try swiping from the top edge to access the indicators"
39+msgstr ""
40+
41+#: Components/EdgeDemo.qml:170
42+msgid "Close"
43+msgstr ""
44+
45+#: Components/EdgeDemo.qml:171
46+msgid "Swipe up again to close the settings screen"
47+msgstr ""
48+
49+#: Components/EdgeDemo.qml:198
50+msgid "Left edge"
51+msgstr ""
52+
53+#: Components/EdgeDemo.qml:199
54+msgid "Swipe from the left to reveal the launcher for quick access to apps"
55+msgstr ""
56+
57+#: Components/EdgeDemo.qml:226
58+msgid "Well done"
59+msgstr ""
60+
61+#: Components/EdgeDemo.qml:227
62+msgid ""
63+"You have now mastered the edge gestures and can start using the "
64+"phone<br><br>Tap anywhere to start"
65+msgstr ""
66+
67+#: Components/PageHeader.qml:364
68+msgid "Recent searches"
69+msgstr ""
70+
71+#: Dash/Apps/AppPreview.qml:135
72+msgid "Rate this"
73+msgstr ""
74+
75+#. TRANSLATORS: Number of persons who rated this app/video/whatever
76+#: Dash/Previews/Header.qml:93
77+#, qt-format
78+msgid "(%1)"
79+msgstr ""
80+
81+#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
82+#: Dash/Previews/Header.qml:105
83+#, qt-format
84+msgid "%1 review"
85+msgid_plural "%1 reviews"
86+msgstr[0] ""
87+msgstr[1] ""
88+
89+#: Dash/Previews/Reviews.qml:74
90+msgid "Add a review"
91+msgstr ""
92+
93+#: Dash/Previews/Reviews.qml:86
94+msgid "Review"
95+msgstr ""
96+
97+#: Dash/Previews/Reviews.qml:121
98+msgid "Send"
99+msgstr ""
100+
101+#: Dash/Previews/Reviews.qml:139
102+msgid "Comments:"
103+msgstr ""
104+
105+#: Greeter/Lockscreen.qml:149
106+msgid "Emergency Call"
107+msgstr ""
108+
109+#: Greeter/PassphraseLockscreen.qml:59
110+#, qt-format
111+msgid "Hello %1"
112+msgstr ""
113+
114+#: Greeter/PassphraseLockscreen.qml:59
115+msgid "Hello"
116+msgstr ""
117+
118+#: Hud/HudParametrizedActionsPage.qml:132
119+msgid "Confirm"
120+msgstr ""
121+
122+#: Hud/Hud.qml:61 Hud/Hud.qml:99 Hud/Hud.qml:372
123+msgid "Type or say a command"
124+msgstr ""
125+
126+#: Hud/Hud.qml:84
127+msgid "Loading. Please Wait..."
128+msgstr ""
129+
130+#: Hud/Hud.qml:89
131+msgid "Speak Now..."
132+msgstr ""
133+
134+#: Hud/Hud.qml:94
135+msgid "Speaking..."
136+msgstr ""
137+
138+#: Notifications/NotificationMenuItemFactory.qml:89
139+msgid "Show password"
140+msgstr ""
141+
142+#: Panel/SearchIndicator.qml:27
143+msgid "Search"
144+msgstr ""
145+
146+#: plugins/Unity/Launcher/launcheritem.cpp:43
147+#: plugins/Unity/Launcher/launcheritem.cpp:73
148+msgid "Lock to Launcher"
149+msgstr ""
150+
151+#: plugins/Unity/Launcher/launcheritem.cpp:73
152+msgid "Unlock from Launcher"
153+msgstr ""
154+
155+#: Shell.qml:439
156+#, qt-format
157+msgid "Please enter %1"
158+msgstr ""
qml/Components/PageHeader.qml+13−37
@@ -21,17 +21,12 @@ import Ubuntu.Components.ListItems 0.1 as ListItem
21import Unity 0.1
21import Unity 0.1
22
22
23Item {
23Item {
24− /*!
24+ id: root
25− \preliminary
26− The text that is shown inside the Page Header
27− \qmlproperty string text
28− */
29− property alias text: label.text
30−
31 property bool searchEntryEnabled: false
25 property bool searchEntryEnabled: false
32 property alias searchQuery: searchField.text
26 property alias searchQuery: searchField.text
33 property ListModel searchHistory
27 property ListModel searchHistory
34 property Scope scope
28 property Scope scope
29+ property alias childItem: itemContainer.children
35
30
36 height: units.gu(8.5)
31 height: units.gu(8.5)
37 implicitHeight: units.gu(8.5)
32 implicitHeight: units.gu(8.5)
@@ -82,29 +77,10 @@ Item {
82 height: childrenRect.height
77 height: childrenRect.height
83
78
84 Item {
79 Item {
85− id: textContainer
80+ id: itemContainer
86
81
87− width: header.width
82+ width: searchContainer.narrowMode ? header.width : header.width - searchContainer.width
88 height: header.height
83 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− }
108 }
84 }
109
85
110 Item {
86 Item {
@@ -115,12 +91,12 @@ Item {
115 property bool popoverShouldOpen: false
91 property bool popoverShouldOpen: false
116 property bool popoverShouldClose: false
92 property bool popoverShouldClose: false
117
93
118− property bool narrowMode: parent.width < label.contentWidth + units.gu(50)
94+ property bool narrowMode: parent.width < units.gu(80)
119
95
120 property bool active: searchField.text != "" || searchField.activeFocus
96 property bool active: searchField.text != "" || searchField.activeFocus
121 property var popover: null
97 property var popover: null
122
98
123− anchors.right: textContainer.right
99+ anchors.right: headerContainer.right
124 height: header.height
100 height: header.height
125
101
126 state:
102 state:
@@ -255,13 +231,13 @@ Item {
255 states: [
231 states: [
256 State {
232 State {
257 name: "wide"
233 name: "wide"
258− AnchorChanges { target: textContainer; anchors.top: headerContainer.top }
234+ AnchorChanges { target: itemContainer; anchors.top: headerContainer.top }
259− AnchorChanges { target: searchContainer; anchors.left: undefined; anchors.top: textContainer.top }
235+ AnchorChanges { target: searchContainer; anchors.left: undefined; anchors.top: itemContainer.top }
260 },
236 },
261 State {
237 State {
262 name: "narrow"
238 name: "narrow"
263 PropertyChanges { target: searchField; highlighted: true }
239 PropertyChanges { target: searchField; highlighted: true }
264− AnchorChanges { target: textContainer; anchors.top: searchContainer.bottom }
240+ AnchorChanges { target: itemContainer; anchors.top: searchContainer.bottom }
265 AnchorChanges { target: searchContainer; anchors.left: headerContainer.left; anchors.top: headerContainer.top }
241 AnchorChanges { target: searchContainer; anchors.left: headerContainer.left; anchors.top: headerContainer.top }
266 },
242 },
267 State {
243 State {
@@ -299,7 +275,7 @@ Item {
299 ParallelAnimation {
275 ParallelAnimation {
300 NumberAnimation { targets: [searchContainer, searchField]; property: "width"; duration: 200; easing.type: Easing.InOutQuad }
276 NumberAnimation { targets: [searchContainer, searchField]; property: "width"; duration: 200; easing.type: Easing.InOutQuad }
301 PropertyAction { target: primaryImage; property: "source" }
277 PropertyAction { target: primaryImage; property: "source" }
302− AnchorAnimation { targets: [searchContainer, textContainer]; duration: 200; easing.type: Easing.InOutQuad }
278+ AnchorAnimation { targets: [searchContainer, itemContainer]; duration: 200; easing.type: Easing.InOutQuad }
303 }
279 }
304 ScriptAction { script: if (searchContainer.popoverShouldOpen) { searchContainer.openPopover(); } }
280 ScriptAction { script: if (searchContainer.popoverShouldOpen) { searchContainer.openPopover(); } }
305 }
281 }
@@ -308,14 +284,14 @@ Item {
308 to: "inactive"
284 to: "inactive"
309 ScriptAction { script: if (searchContainer.popoverShouldClose) { searchContainer.closePopover(); } }
285 ScriptAction { script: if (searchContainer.popoverShouldClose) { searchContainer.closePopover(); } }
310 NumberAnimation { targets: [searchContainer, searchField] ; property: "width"; duration: 200; easing.type: Easing.InOutQuad }
286 NumberAnimation { targets: [searchContainer, searchField] ; property: "width"; duration: 200; easing.type: Easing.InOutQuad }
311− AnchorAnimation { targets: [searchContainer, textContainer]; duration: 200; easing.type: Easing.InOutQuad }
287+ AnchorAnimation { targets: [searchContainer, itemContainer]; duration: 200; easing.type: Easing.InOutQuad }
312 },
288 },
313 Transition {
289 Transition {
314 to: "narrowActive"
290 to: "narrowActive"
315 SequentialAnimation {
291 SequentialAnimation {
316 ParallelAnimation {
292 ParallelAnimation {
317 NumberAnimation { targets: [searchContainer, searchField] ; property: "width"; duration: 200; easing.type: Easing.OutQuad }
293 NumberAnimation { targets: [searchContainer, searchField] ; property: "width"; duration: 200; easing.type: Easing.OutQuad }
318− AnchorAnimation { targets: [searchContainer, textContainer]; duration: 200; easing.type: Easing.InOutQuad }
294+ AnchorAnimation { targets: [searchContainer, itemContainer]; duration: 200; easing.type: Easing.InOutQuad }
319 }
295 }
320 ScriptAction { script: if (searchContainer.popoverShouldOpen) { searchContainer.openPopover(); } }
296 ScriptAction { script: if (searchContainer.popoverShouldOpen) { searchContainer.openPopover(); } }
321 }
297 }
@@ -324,7 +300,7 @@ Item {
324 to: "narrowInactive"
300 to: "narrowInactive"
325 ScriptAction { script: if (searchContainer.popoverShouldClose) { searchContainer.closePopover(); } }
301 ScriptAction { script: if (searchContainer.popoverShouldClose) { searchContainer.closePopover(); } }
326 NumberAnimation { targets: [searchContainer, searchField] ; property: "width"; duration: 200; easing.type: Easing.OutQuad }
302 NumberAnimation { targets: [searchContainer, searchField] ; property: "width"; duration: 200; easing.type: Easing.OutQuad }
327− AnchorAnimation { targets: [searchContainer, textContainer]; duration: 200; easing.type: Easing.InOutQuad }
303+ AnchorAnimation { targets: [searchContainer, itemContainer]; duration: 200; easing.type: Easing.InOutQuad }
328 }
304 }
329 ]
305 ]
330
306
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/Card.qmlnew file+83
@@ -0,0 +1,83 @@
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+Item {
21+ id: root
22+ property var template
23+ property var components
24+ property var cardData
25+
26+ width: {
27+ if (template !== undefined) {
28+ if (template["card-layout"] === "horizontal") return units.gu(38);
29+ switch (template['card-size']) {
30+ case "small": return units.gu(12);
31+ case "large": return units.gu(38);
32+ }
33+ }
34+ return units.gu(18.5);
35+ }
36+ height: childrenRect.height
37+
38+ UbuntuShape {
39+ id: artShape
40+ objectName: "artShape"
41+ width: image.fillMode === Image.PreserveAspectCrop || aspect < image.aspect ? image.width : height * image.aspect
42+ height: image.fillMode === Image.PreserveAspectCrop || aspect > image.aspect ? image.height : width / image.aspect
43+ anchors.horizontalCenter: template && template["card-layout"] === "horizontal" ? undefined : parent.horizontalCenter
44+
45+ property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1
46+
47+ image: Image {
48+ width: template && template["card-layout"] === "horizontal" ? height * artShape.aspect : root.width
49+ height: template && template["card-layout"] === "horizontal" ? header.height : width / artShape.aspect
50+ objectName: "artImage"
51+ source: cardData && cardData["art"] || ""
52+ // FIXME uncomment when having investigated / fixed the crash
53+ //sourceSize.width: width > height ? width : 0
54+ //sourceSize.height: height > width ? height : 0
55+ fillMode: components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop
56+
57+ property real aspect: implicitWidth / implicitHeight
58+ }
59+ }
60+
61+ CardHeader {
62+ id: header
63+ objectName: "cardHeader"
64+ anchors {
65+ top: template && template["card-layout"] === "horizontal" ? artShape.top : artShape.bottom
66+ left: template && template["card-layout"] === "horizontal" ? artShape.right : parent.left
67+ right: parent.right
68+ }
69+
70+ mascot: cardData && cardData["mascot"] || ""
71+ title: cardData && cardData["title"] || ""
72+ subtitle: cardData && cardData["subtitle"] || ""
73+ }
74+
75+ Label {
76+ objectName: "summaryLabel"
77+ anchors { top: header.bottom; left: parent.left; right: parent.right }
78+ wrapMode: Text.Wrap
79+ maximumLineCount: 5
80+ elide: Text.ElideRight
81+ text: cardData && cardData["summary"] || ""
82+ }
83+}
qml/Dash/CardHeader.qmlnew file+112
@@ -0,0 +1,112 @@
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+Item {
21+ id: root
22+ property alias mascot: mascotImage.source
23+ property alias title: titleLabel.text
24+ property alias subtitle: subtitleLabel.text
25+ property alias price: priceLabel.text
26+ property alias oldPrice: oldPriceLabel.text
27+ property alias altPrice: altPriceLabel.text
28+
29+ visible: mascotImage.status === Image.Ready || title || price
30+ height: row.height > 0 ? row.height + row.spacing * 2 : 0
31+
32+ Row {
33+ id: row
34+ objectName: "outerRow"
35+
36+ anchors { top: parent.top; left: parent.left; right: parent.right; margins: spacing }
37+ spacing: units.gu(1)
38+
39+ UbuntuShape {
40+ id: mascotShape
41+ objectName: "mascotShape"
42+
43+ width: units.gu(8)
44+ height: units.gu(8)
45+ visible: image.status === Image.Ready
46+
47+ image: Image {
48+ id: mascotImage
49+ sourceSize { width: mascotShape.width; height: mascotShape.height }
50+ }
51+ }
52+
53+ Column {
54+ objectName: "column"
55+ width: parent.width - x
56+
57+ Label {
58+ id: titleLabel
59+ anchors { left: parent.left; right: parent.right }
60+ elide: Text.ElideRight
61+ objectName: "titleLabel"
62+ font.weight: Font.DemiBold
63+ wrapMode: Text.Wrap
64+ maximumLineCount: 2
65+ }
66+
67+ Label {
68+ id: subtitleLabel
69+ anchors { left: parent.left; right: parent.right }
70+ elide: Text.ElideRight
71+ visible: titleLabel.text && text
72+ }
73+
74+ Row {
75+ id: prices
76+ objectName: "prices"
77+ anchors { left: parent.left; right: parent.right }
78+
79+ property int labels: {
80+ var labels = 1; // price always visible
81+ if (oldPriceLabel.text !== "") labels += 1;
82+ if (altPriceLabel.text !== "") labels += 1;
83+ return labels;
84+ }
85+ property real labelWidth: width / labels
86+
87+ Label {
88+ id: priceLabel
89+ width: parent.labelWidth
90+ elide: Text.ElideRight
91+ font.weight: Font.DemiBold
92+ color: Theme.palette.selected.foreground
93+ }
94+
95+ Label {
96+ id: oldPriceLabel
97+ objectName: "oldPriceLabel"
98+ width: parent.labelWidth
99+ elide: Text.ElideRight
100+ horizontalAlignment: parent.labels === 3 ? Text.AlignHCenter : Text.AlignRight
101+ }
102+
103+ Label {
104+ id: altPriceLabel
105+ width: parent.labelWidth
106+ elide: Text.ElideRight
107+ horizontalAlignment: Text.AlignRight
108+ }
109+ }
110+ }
111+ }
112+}
qml/Dash/Dash.qml−18
@@ -66,11 +66,6 @@ Showable {
66 model: filteredScopes
66 model: filteredScopes
67 scopes: scopes
67 scopes: scopes
68 searchHistory: dash.searchHistory
68 searchHistory: dash.searchHistory
69− onMovementStarted: dashbar.startNavigation()
70− onMovementEnded: dashbar.stopNavigation()
71− onContentFlickStarted: dashbar.finishNavigation()
72− onContentEndReached: dashbar.finishNavigation()
73− onPreviewShown: dashbar.finishNavigation()
74 onScopeLoaded: {
69 onScopeLoaded: {
75 if (scopeId == dash.showScopeOnLoaded) {
70 if (scopeId == dash.showScopeOnLoaded) {
76 dash.setCurrentScope(scopeId, false, false)
71 dash.setCurrentScope(scopeId, false, false)
@@ -80,17 +75,4 @@ Showable {
80 scale: dash.contentScale
75 scale: dash.contentScale
81 clip: scale != 1.0
76 clip: scale != 1.0
82 }
77 }
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− }
96}
78}
qml/Dash/DashApps.qml−3
@@ -29,9 +29,6 @@ GenericScopeView {
29 property var mainStageApplicationsModel: shell.applicationManager.mainStageApplications
29 property var mainStageApplicationsModel: shell.applicationManager.mainStageApplications
30 property var sideStageApplicationModel: shell.applicationManager.sideStageApplications
30 property var sideStageApplicationModel: shell.applicationManager.sideStageApplications
31
31
32− //FIXME an alias to itself shouldn't be required. More than likely a Qt bug. Try removing with newer Qt.
33− property alias searchHistory: scopeView.searchHistory
34−
35 ListModel {
32 ListModel {
36 id: dummyVisibilityModifier
33 id: dummyVisibilityModifier
37
34
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 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import Unity 0.1
19import Unity 0.1
20+import "../Components"
20
21
21Item {
22Item {
22 id: dashContent
23 id: dashContent
@@ -33,7 +34,6 @@ Item {
33 signal movementEnded()
34 signal movementEnded()
34 signal contentFlickStarted()
35 signal contentFlickStarted()
35 signal contentEndReached()
36 signal contentEndReached()
36− signal previewShown()
37 signal scopeLoaded(string scopeId)
37 signal scopeLoaded(string scopeId)
38 signal positionedAtBeginning()
38 signal positionedAtBeginning()
39
39
@@ -80,77 +80,138 @@ Item {
80 }
80 }
81 }
81 }
82
82
83− ListView {
83+ Item {
84− id: dashContentList
84+ id: dashContentListHolder
85− objectName: "dashContentList"
86−
87− interactive: dashContent.scopes.loaded && !currentItem.previewShown && !currentItem.moving
88−
89 anchors.fill: parent
85 anchors.fill: parent
86+
87+ ListView {
88+ id: dashContentList
89+ objectName: "dashContentList"
90+
91+ interactive: dashContent.scopes.loaded && !previewListView.onScreen && !currentItem.moving
92+
93+ anchors.fill: parent
90− model: dashContent.model
94+ model: dashContent.model
91− orientation: ListView.Horizontal
95+ orientation: ListView.Horizontal
92− boundsBehavior: Flickable.DragAndOvershootBounds
96+ boundsBehavior: Flickable.DragAndOvershootBounds
93− flickDeceleration: units.gu(625)
97+ flickDeceleration: units.gu(625)
94− maximumFlickVelocity: width * 5
98+ maximumFlickVelocity: width * 5
95− snapMode: ListView.SnapOneItem
99+ snapMode: ListView.SnapOneItem
96− highlightMoveDuration: 250
100+ highlightMoveDuration: 250
97− highlightRangeMode: ListView.StrictlyEnforceRange
101+ highlightRangeMode: ListView.StrictlyEnforceRange
98− /* FIXME: workaround rendering issue due to use of ShaderEffectSource in
102+ // TODO Investigate if we can switch to a smaller cache buffer when/if UbuntuShape gets more performant
99− UbuntuShape. While switching from the home scope to the People scope the
100− rendering would block midway.
101− */
102− cacheBuffer: 2147483647
103+ cacheBuffer: 1073741823
103− onMovementStarted: dashContent.movementStarted()
104+ onMovementStarted: dashContent.movementStarted()
104− onMovementEnded: dashContent.movementEnded()
105+ onMovementEnded: dashContent.movementEnded()
105−
106+
106− // If the number of items is less than the current index, then need to reset to another item.
107+ // If the number of items is less than the current index, then need to reset to another item.
107− onCountChanged: {
108+ onCountChanged: {
108− if (count > 0) {
109+ if (count > 0) {
109− if (currentIndex >= count) {
110+ if (currentIndex >= count) {
110− dashContent.setCurrentScopeAtIndex(count-1, true, true)
111+ dashContent.setCurrentScopeAtIndex(count-1, true, true)
111− } else if (currentIndex < 0) {
112+ } else if (currentIndex < 0) {
112− // setting currentIndex directly, cause we don't want to loose set_current_index
113+ // setting currentIndex directly, cause we don't want to loose set_current_index
113− dashContent.currentIndex = 0
114+ dashContent.currentIndex = 0
115+ }
114 }
116 }
115 }
117 }
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+ }
116 }
155 }
117
156
118− delegate:
157+ PageHeader {
119− Loader {
158+ id: pageHeader
120− width: ListView.view.width
159+ width: parent.width
121− height: ListView.view.height
160+ searchEntryEnabled: true
122− asynchronous: true
161+ searchHistory: dashContent.searchHistory
123− source: scopeMapper.map(scope.id)
162+
163+ childItem: TabBar {
164+ id: tabBar
124− objectName: scope.id + " loader"
165+ objectName: "tabbar"
125−
166+ height: units.gu(7)
126− readonly property bool previewShown: item ? item.previewShown : false
167+ width: parent.width
127− readonly property bool moving: item ? item.moving : false
168+ selectionMode: false
128−
169+ style: DashContentTabBarStyle {}
129− // these are needed for autopilot tests
170+
130− readonly property string scopeId: scope.id
171+ model: dashContentList.model
131− readonly property bool isCurrent: ListView.isCurrentItem
172+
132− readonly property bool isLoaded: status == Loader.Ready
173+ onSelectedIndexChanged: {
133−
174+ dashContentList.currentIndex = selectedIndex;
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)
141 }
175 }
176+
142 Connections {
177 Connections {
143− target: item
178+ target: dashContentList
144− ignoreUnknownSignals: true
179+ onCurrentIndexChanged: {
145− onEndReached: contentEndReached()
180+ tabBar.selectedIndex = dashContentList.currentIndex
146− onPreviewShownChanged: {
147− if (item.previewShown) {
148− dashContent.previewShown()
149− }
150 }
181 }
151 }
182 }
152
183
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+ }
154 }
196 }
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
155 }
216 }
156}
217}
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 {
27 property Scope scope
27 property Scope scope
28 property SortFilterProxyModel categories: categoryFilter
28 property SortFilterProxyModel categories: categoryFilter
29 property bool isCurrent
29 property bool isCurrent
30− property ListModel searchHistory
31 property alias moving: categoryView.moving
30 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
32
35
33 signal endReached
36 signal endReached
34 signal movementStarted
37 signal movementStarted
35 signal positionedAtBeginning
38 signal positionedAtBeginning
36
39
37− readonly property alias previewShown: previewListView.onScreen
38 property bool enableHeightBehaviorOnNextCreation: false
40 property bool enableHeightBehaviorOnNextCreation: false
41+ property var categoryView: categoryView
39
42
40 // FIXME delay the search so that daemons have time to settle, note that
43 // FIXME delay the search so that daemons have time to settle, note that
41 // removing this will break ScopeView::test_changeScope
44 // removing this will break ScopeView::test_changeScope
@@ -84,12 +87,14 @@ FocusScope {
84 target: scopeView.scope
87 target: scopeView.scope
85 property: "searchQuery"
88 property: "searchQuery"
86 value: pageHeader.searchQuery
89 value: pageHeader.searchQuery
90+ when: isCurrent
87 }
91 }
88
92
89 Binding {
93 Binding {
90 target: pageHeader
94 target: pageHeader
91 property: "searchQuery"
95 property: "searchQuery"
92 value: scopeView.scope.searchQuery
96 value: scopeView.scope.searchQuery
97+ when: isCurrent
93 }
98 }
94
99
95 Connections {
100 Connections {
@@ -119,6 +124,10 @@ FocusScope {
119 property string expandedCategoryId: ""
124 property string expandedCategoryId: ""
120 signal correctExpandedCategory();
125 signal correctExpandedCategory();
121
126
127+ onContentYChanged: pageHeader.positionRealHeader();
128+ onOriginYChanged: pageHeader.positionRealHeader();
129+ onContentHeightChanged: pageHeader.positionRealHeader();
130+
122 Behavior on contentY {
131 Behavior on contentY {
123 enabled: previewListView.open
132 enabled: previewListView.open
124 UbuntuNumberAnimation {}
133 UbuntuNumberAnimation {}
@@ -176,7 +185,7 @@ FocusScope {
176 target: rendererLoader.item
185 target: rendererLoader.item
177 onClicked: {
186 onClicked: {
178 // Prepare the preview in case activate() triggers a preview only
187 // Prepare the preview in case activate() triggers a preview only
179− effect.positionPx = Math.max(mapToItem(categoryView, 0, itemY).y, pageHeader.height + categoryView.stickyHeaderHeight);
188+ openEffect.positionPx = Math.max(mapToItem(categoryView, 0, itemY).y, pageHeader.height + categoryView.stickyHeaderHeight);
180 previewListView.categoryId = categoryId
189 previewListView.categoryId = categoryId
181 previewListView.categoryDelegate = rendererLoader.item
190 previewListView.categoryDelegate = rendererLoader.item
182 previewListView.model = target.model;
191 previewListView.model = target.model;
@@ -197,7 +206,7 @@ FocusScope {
197 }
206 }
198 }
207 }
199 onPressAndHold: {
208 onPressAndHold: {
200− effect.positionPx = Math.max(mapToItem(categoryView, 0, itemY).y, pageHeader.height + categoryView.stickyHeaderHeight);
209+ openEffect.positionPx = Math.max(mapToItem(categoryView, 0, itemY).y, pageHeader.height + categoryView.stickyHeaderHeight);
201 previewListView.categoryId = categoryId
210 previewListView.categoryId = categoryId
202 previewListView.categoryDelegate = rendererLoader.item
211 previewListView.categoryDelegate = rendererLoader.item
203 previewListView.model = target.model;
212 previewListView.model = target.model;
@@ -288,14 +297,20 @@ FocusScope {
288 categoryView.expandedCategoryId = "";
297 categoryView.expandedCategoryId = "";
289 }
298 }
290 }
299 }
291− pageHeader: PageHeader {
300+ pageHeader: Item {
292− id: pageHeader
301+ implicitHeight: scopeView.tabBarHeight
293− objectName: "pageHeader"
302+ onHeightChanged: {
294− width: categoryView.width
303+ if (scopeView.pageHeader) {
295− text: scopeView.scope.name
304+ scopeView.pageHeader.height = height;
296− searchEntryEnabled: true
305+ }
306+ }
307+ onYChanged: positionRealHeader();
308+
309+ function positionRealHeader() {
297− scope: scopeView.scope
310+ if (scopeView.pageHeader) {
298− searchHistory: scopeView.searchHistory
311+ scopeView.pageHeader.y = y + parent.y;
312+ }
313+ }
299 }
314 }
300 }
315 }
301
316
@@ -346,44 +361,6 @@ FocusScope {
346 }
361 }
347 }
362 }
348
363
349− OpenEffect {
350− id: effect
351− objectName: "openEffect"
352− anchors {
353− fill: parent
354− bottomMargin: -bottomOverflow
355− }
356− sourceItem: categoryView
357−
358− enabled: gap > 0.0
359−
360− topGapPx: (1 - gap) * positionPx
361− topOpacity: (1 - gap * 1.2)
362− bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
363− bottomOverflow: units.gu(20)
364− live: !expansionAnimation.running
365−
366− property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
367− property real gap: previewListView.open ? 1.0 : 0.0
368−
369− Behavior on gap {
370− NumberAnimation {
371− id: expansionAnimation
372− duration: 200
373− easing.type: Easing.InOutQuad
374− onRunningChanged: {
375− if (!previewListView.open && !running) {
376− previewListView.onScreen = false
377− }
378− }
379− }
380− }
381− Behavior on positionPx {
382− enabled: previewListView.open
383− UbuntuNumberAnimation {}
384− }
385− }
386−
387 Connections {
364 Connections {
388 target: scopeView.scope
365 target: scopeView.scope
389 onPreviewReady: {
366 onPreviewReady: {
@@ -402,190 +379,13 @@ FocusScope {
402 }
379 }
403 }
380 }
404
381
405− PreviewDelegateMapper {
406− id: previewDelegateMapper
407− }
408−
409− ListView {
410− id: previewListView
411− objectName: "previewListView"
412− height: effect.bottomGapPx - effect.topGapPx
413− anchors {
414− top: parent.top
415− topMargin: effect.topGapPx
416− left: parent.left
417− right: parent.right
418− }
419− orientation: ListView.Horizontal
420− highlightRangeMode: ListView.StrictlyEnforceRange
421− snapMode: ListView.SnapOneItem
422− boundsBehavior: Flickable.DragAndOvershootBounds
423− highlightMoveDuration: 250
424− flickDeceleration: units.gu(625)
425− maximumFlickVelocity: width * 5
426− cacheBuffer: 0
427−
428− // To be set before opening the preview
429− property string categoryId: ""
430− property var categoryDelegate
431−
432− // because the ListView is built asynchronous, setting the
433− // currentIndex directly won't work. We need to refresh it
434− // when the first preview is ready to be displayed.
435− property bool init: true
436−
437− onCurrentIndexChanged: {
438− var row = Math.floor(currentIndex / categoryDelegate.columns);
439− if (categoryDelegate.collapsedRowCount <= row) {
440− categoryView.expandedCategoryId = categoryId
441− }
442−
443− if (open) {
444− categoryDelegate.highlightIndex = currentIndex
445− }
446−
447− if (!init && model !== undefined) {
448− var item = model.get(currentIndex)
449− scopeView.scope.preview(item.uri, item.icon, item.category, 0, item.mimetype, item.title, item.comment, item.dndUri, item.metadata)
450− }
451−
452− // Adjust contentY in case we need to change to it to show the next row
453− if (categoryDelegate.rows > 1) {
454− var itemY = categoryView.contentItem.mapFromItem(categoryDelegate.currentItem).y;
455−
456− // Find new contentY and effect.postionPx
457− var newContentY = itemY - effect.positionPx - categoryDelegate.verticalSpacing;
458−
459− // Make sure the item is not covered by a header. Move the effect split down if necessary
460− var headerHeight = pageHeader.height + categoryView.stickyHeaderHeight;
461− var effectAdjust = Math.max(effect.positionPx, headerHeight);
462−
463− // Make sure we don't overscroll the listview. If yes, adjust effect position
464− if (newContentY < 0) {
465− effectAdjust += newContentY;
466− newContentY = 0;
467− }
468− if (newContentY > Math.max(0, categoryView.contentHeight - categoryView.height)) {
469− effectAdjust += -(categoryView.contentHeight - categoryView.height) + newContentY
470− newContentY = categoryView.contentHeight - categoryView.height;
471− }
472−
473− effect.positionPx = effectAdjust;
474− categoryView.contentY = newContentY;
475− }
476− }
477−
478− property bool open: false
479− property bool onScreen: false
480−
481− onOpenChanged: {
482− if (open) {
483− onScreen = true;
484− categoryDelegate.highlightIndex = currentIndex;
485− pageHeader.unfocus();
486− } else {
487− // Cancel any pending preview requests or actions
488− if (previewListView.currentItem.previewData !== undefined) {
489− previewListView.currentItem.previewData.cancelAction();
490− }
491− scopeView.scope.cancelActivation();
492− model = undefined;
493− categoryView.correctExpandedCategory();
494− categoryDelegate.highlightIndex = -1;
495− }
496− }
497−
498− Rectangle {
499− anchors.fill: parent
500− color: Qt.rgba(0, 0, 0, .3)
501− z: -1
502− }
503−
504− delegate: Loader {
505− id: previewLoader
506− objectName: "previewLoader" + index
507− height: previewListView.height
508− width: previewListView.width
509− asynchronous: true
510− source: previewListView.onScreen ?
511− (previewData !== undefined ? previewDelegateMapper.map(previewData.rendererName) : "DashPreviewPlaceholder.qml") : ""
512−
513− onPreviewDataChanged: {
514− if (previewData !== undefined && source.toString().indexOf("DashPreviewPlaceholder.qml") != -1) {
515− previewLoader.opacity = 0;
516− }
517− }
518−
519− onSourceChanged: {
520− if (previewData !== undefined) {
521− fadeIn.start()
522− }
523− }
524−
525− PropertyAnimation {
526− id: fadeIn
527− target: previewLoader
528− property: "opacity"
529− from: 0.0
530− to: 1.0
531− duration: UbuntuAnimation.BriskDuration
532− }
533−
534− property var previewData
535− property bool valid: item !== null
536−
537− onLoaded: {
538− if (previewListView.onScreen && previewData !== undefined) {
539− item.previewData = Qt.binding(function() { return previewData })
540− item.isCurrent = Qt.binding(function() { return ListView.isCurrentItem })
541− }
542− }
543−
544− Connections {
545− ignoreUnknownSignals: true
546− target: item
547− onClose: {
548− previewListView.open = false
549− }
550− }
551−
552− function closePreviewSpinner() {
553− if (item) {
554− item.showProcessingAction = false;
555− }
556− }
557− }
558− }
559−
560− Image {
561− objectName: "pointerArrow"
562− anchors {
563− top: previewListView.bottom
564− left: parent.left
565− leftMargin: previewListView.categoryDelegate !== undefined && previewListView.categoryDelegate.currentItem ?
566− previewListView.categoryDelegate.currentItem.center + (-width + margins) / 2 : 0
567−
568− Behavior on leftMargin {
569− SmoothedAnimation {
570− duration: UbuntuAnimation.FastDuration
571− }
572− }
573− }
574− height: units.gu(1)
575− width: units.gu(2)
576− property int margins: previewListView.categoryDelegate ? previewListView.categoryDelegate.margins : 0
577− opacity: previewListView.open ? .5 : 0
578−
579− source: "graphics/tooltip_arrow.png"
580− }
581−
582 // TODO: Move as InverseMouseArea to DashPreview
382 // TODO: Move as InverseMouseArea to DashPreview
583 MouseArea {
383 MouseArea {
584 objectName: "closePreviewMouseArea"
384 objectName: "closePreviewMouseArea"
585 enabled: previewListView.onScreen
385 enabled: previewListView.onScreen
586 anchors {
386 anchors {
587 fill: parent
387 fill: parent
588− topMargin: effect.bottomGapPx
388+ topMargin: openEffect.bottomGapPx
589 }
389 }
590 onClicked: {
390 onClicked: {
591 previewListView.open = false;
391 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 {
89 anchors.left: parent.left
89 anchors.left: parent.left
90 anchors.right: parent.right
90 anchors.right: parent.right
91
91
92− PageHeader {
92+ PageHeaderLabel {
93 id: header
93 id: header
94 anchors.top: parent.top
94 anchors.top: parent.top
95 anchors.left: parent.left
95 anchors.left: parent.left
qml/Panel/IndicatorItem.qml+6−12
@@ -23,10 +23,10 @@ Item {
23 id: indicatorItem
23 id: indicatorItem
24
24
25 property alias widgetSource: loader.source
25 property alias widgetSource: loader.source
26− property bool highlighted: false
27 property bool dimmed: false
26 property bool dimmed: false
28 property var indicatorProperties: undefined
27 property var indicatorProperties: undefined
29 property bool indicatorVisible: loader.item ? loader.item.enabled : false
28 property bool indicatorVisible: loader.item ? loader.item.enabled : false
29+ property string identifier
30
30
31 opacity: dimmed ? 0.4 : 1
31 opacity: dimmed ? 0.4 : 1
32 Behavior on opacity { StandardAnimation {} }
32 Behavior on opacity { StandardAnimation {} }
@@ -45,17 +45,11 @@ Item {
45 }
45 }
46 }
46 }
47 }
47 }
48− }
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
48
58− opacity: highlighted ? 1 : 0
49+ Binding {
59− Behavior on opacity { StandardAnimation {} }
50+ target: loader.item
51+ property: "objectName"
52+ value: identifier + "-widget"
53+ }
60 }
54 }
61}
55}
qml/Panel/IndicatorRow.qml+40−8
@@ -29,6 +29,8 @@ Item {
29 property var visibleIndicators: defined
29 property var visibleIndicators: defined
30 property int overFlowWidth: width
30 property int overFlowWidth: width
31 property bool showAll: false
31 property bool showAll: false
32+ property real currentItemOffset: 0.0
33+ property real unitProgress: 0.0
32
34
33 width: units.gu(40)
35 width: units.gu(40)
34 height: units.gu(3)
36 height: units.gu(3)
@@ -79,23 +81,24 @@ Item {
79 height: indicatorRow.height
81 height: indicatorRow.height
80 width: indicatorItem.width
82 width: indicatorItem.width
81 visible: indicatorItem.indicatorVisible
83 visible: indicatorItem.indicatorVisible
82− opacity: 1.0
84+ opacity: 1.0 * opacityMultiplier
83 y: 0
85 y: 0
84 state: "standard"
86 state: "standard"
85
87
86 property int ownIndex: index
88 property int ownIndex: index
87− property alias highlighted: indicatorItem.highlighted
89+ property bool highlighted: indicatorRow.state != "initial" ? ownIndex == indicatorRow.currentItemIndex : false
88− property alias dimmed: indicatorItem.dimmed
90+ property bool dimmed: indicatorRow.state != "initial" ? ownIndex != indicatorRow.currentItemIndex : false
89
91
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")
91 property bool overflow: row.width - itemWrapper.x > overFlowWidth
93 property bool overflow: row.width - itemWrapper.x > overFlowWidth
94+ property real opacityMultiplier: highlighted ? 1 : (1 - indicatorRow.unitProgress)
92
95
93 IndicatorItem {
96 IndicatorItem {
94 id: indicatorItem
97 id: indicatorItem
98+ identifier: model.identifier
95 height: parent.height
99 height: parent.height
96
100
97− highlighted: indicatorRow.state != "initial" ? itemWrapper.ownIndex == indicatorRow.currentItemIndex : false
101+ dimmed: itemWrapper.dimmed
98− dimmed: indicatorRow.state != "initial" ? itemWrapper.ownIndex != indicatorRow.currentItemIndex : false
99
102
100 widgetSource: model.widgetSource
103 widgetSource: model.widgetSource
101 indicatorProperties : model.indicatorProperties
104 indicatorProperties : model.indicatorProperties
@@ -135,14 +138,43 @@ Item {
135
138
136 transitions: [
139 transitions: [
137 Transition {
140 Transition {
138− StandardAnimation {
141+ UbuntuNumberAnimation {
139 target: itemWrapper
142 target: itemWrapper
140 property: "opacity"
143 property: "opacity"
141− duration: 300
144+ duration: UbuntuAnimation.BriskDuration
142 }
145 }
143 }
146 }
144 ]
147 ]
145 }
148 }
146 }
149 }
147 }
150 }
151+
152+ Rectangle {
153+ id: highlight
154+ color: Theme.palette.selected.foreground
155+ objectName: "highlight"
156+ height: units.dp(2)
157+ anchors.top: row.bottom
158+ visible: indicatorRow.currentItem != null
159+ x: row.x + (indicatorRow.currentItem != null ? indicatorRow.currentItem.x + centerOffset : 0)
160+ width: indicatorRow.currentItem != null ? indicatorRow.currentItem.width : 0
161+
162+ property real centerOffset: {
163+ if (indicatorRow.currentItemOffset > 0.1) {
164+ return (indicatorRow.currentItemOffset - 0.1) * units.gu(0.4);
165+ } else if (indicatorRow.currentItemOffset < -0.1) {
166+ return (indicatorRow.currentItemOffset + 0.1) * units.gu(0.4);
167+ }
168+ return 0.0;
169+ }
170+
171+ Behavior on width {
172+ enabled: unitProgress > 0;
173+ UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
174+ }
175+ Behavior on x {
176+ enabled: unitProgress > 0;
177+ UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
178+ }
179+ }
148}
180}
qml/Panel/Indicators.qml+22−11
@@ -39,6 +39,7 @@ Showable {
39 property bool contentEnabled: true
39 property bool contentEnabled: true
40 property bool initalizeItem: true
40 property bool initalizeItem: true
41 readonly property alias content: menuContent
41 readonly property alias content: menuContent
42+ property real unitProgress: 0.0
42
43
43 // TODO: Perhaps we need a animation standard for showing/hiding? Each showable seems to
44 // TODO: Perhaps we need a animation standard for showing/hiding? Each showable seems to
44 // use its own values. Need to ask design about this.
45 // use its own values. Need to ask design about this.
@@ -128,23 +129,31 @@ Showable {
128 rowCoordinates = indicatorRow.mapToItem(indicatorRow.row, xValue, 0);
129 rowCoordinates = indicatorRow.mapToItem(indicatorRow.row, xValue, 0);
129 // get the current delegate
130 // get the current delegate
130 currentItem = indicatorRow.row.childAt(rowCoordinates.x, 0);
131 currentItem = indicatorRow.row.childAt(rowCoordinates.x, 0);
131− if (currentItem && currentItem != indicatorRow.currentItem ) {
132+ if (currentItem) {
132 itemCoordinates = indicatorRow.row.mapToItem(currentItem, rowCoordinates.x, 0);
133 itemCoordinates = indicatorRow.row.mapToItem(currentItem, rowCoordinates.x, 0);
133 distanceFromRightEdge = (currentItem.width - itemCoordinates.x) / (currentItem.width);
134 distanceFromRightEdge = (currentItem.width - itemCoordinates.x) / (currentItem.width);
135+ if (currentItem != indicatorRow.currentItem) {
134− if (Math.abs(currentItem.ownIndex - indicatorRow.currentItemIndex) > 1) {
136+ 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) {
140 bufferExceeded = true;
137 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+ }
141 }
144 }
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);
142 }
152 }
143− if ((!useBuffer || (useBuffer && bufferExceeded)) || indicatorRow.currentItem < 0 || indicatorRow.currentItem == null) {
153+ indicatorRow.currentItemOffset = 1 - (distanceFromRightEdge * 2);
144− indicatorRow.currentItem = currentItem;
154+ } else {
145− }
146− } else if (!currentItem) {
147 indicatorRow.setDefaultItem();
155 indicatorRow.setDefaultItem();
156+ indicatorRow.currentItemOffset = 0;
148 }
157 }
149 initalizeItem = indicatorRow.currentItem == null;
158 initalizeItem = indicatorRow.currentItem == null;
150 }
159 }
@@ -268,6 +277,7 @@ Showable {
268 height: indicators.panelHeight
277 height: indicators.panelHeight
269 indicatorsModel: indicatorsModel
278 indicatorsModel: indicatorsModel
270 state: indicators.state
279 state: indicators.state
280+ unitProgress: indicators.unitProgress
271
281
272 Connections {
282 Connections {
273 property bool enableIndexChangeSignal: true
283 property bool enableIndexChangeSignal: true
@@ -326,6 +336,7 @@ Showable {
326 if (hideAnimation.running) {
336 if (hideAnimation.running) {
327 indicators.state = "initial";
337 indicators.state = "initial";
328 initalizeItem = true;
338 initalizeItem = true;
339+ menuContent.animateNextMenuChange = false;
329 }
340 }
330 }
341 }
331 }
342 }
qml/Panel/MenuContent.qml+3−1
@@ -33,6 +33,7 @@ MainView {
33 property int contentReleaseInterval: 20000
33 property int contentReleaseInterval: 20000
34 property bool activeHeader: false
34 property bool activeHeader: false
35 property alias visibleIndicators: visibleIndicatorsModel.visible
35 property alias visibleIndicators: visibleIndicatorsModel.visible
36+ property bool animateNextMenuChange: false
36
37
37 width: units.gu(40)
38 width: units.gu(40)
38 height: units.gu(42)
39 height: units.gu(42)
@@ -41,11 +42,12 @@ MainView {
41 var filteredIndex = filteredIndicators.mapFromSource(index)
42 var filteredIndex = filteredIndicators.mapFromSource(index)
42
43
43 if (tabs.selectedTabIndex !== filteredIndex) {
44 if (tabs.selectedTabIndex !== filteredIndex) {
44− if (tabs.selectedTabIndex == -1) {
45+ if (tabs.selectedTabIndex == -1 || !animateNextMenuChange) {
45 tabs.tabBar.animate = false;
46 tabs.tabBar.animate = false;
46 }
47 }
47 tabs.selectedTabIndex = filteredIndex;
48 tabs.selectedTabIndex = filteredIndex;
48 tabs.tabBar.animate = true;
49 tabs.tabBar.animate = true;
50+ animateNextMenuChange = true;
49 }
51 }
50 }
52 }
51
53
qml/Panel/Panel.qml+1−1
@@ -107,7 +107,7 @@ Item {
107 pinnedMode: !fullscreenMode
107 pinnedMode: !fullscreenMode
108 overFlowWidth: search.state=="hidden" ? parent.width : parent.width - search.width
108 overFlowWidth: search.state=="hidden" ? parent.width : parent.width - search.width
109
109
110− property real unitProgress: (height - panelHeight) / (openedHeight - panelHeight)
110+ unitProgress: (height - panelHeight) / (openedHeight - panelHeight)
111 }
111 }
112
112
113 PanelSeparatorLine {
113 PanelSeparatorLine {
qml/ScopeTool.qmlnew file+175
@@ -0,0 +1,175 @@
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 Utils 0.1
21+import Unity 0.1
22+import "Components"
23+import "Dash"
24+
25+Rectangle {
26+ id: root
27+ width: units.gu(80)
28+ height: units.gu(72)
29+ color: "#88FFFFFF"
30+
31+ // Fake shell object
32+ QtObject {
33+ id: shell
34+ }
35+
36+ // Fake greeter object
37+ QtObject {
38+ id: greeter
39+ property bool shown
40+ }
41+
42+ // Fake panel object
43+ QtObject {
44+ id: panel
45+ signal searchClicked
46+ }
47+
48+ SortFilterProxyModel {
49+ id: filteredScopes
50+ model: Scopes {
51+ id: scopes
52+ }
53+ dynamicSortFilter: true
54+
55+ filterRole: Scopes.RoleVisible
56+ filterRegExp: RegExp("^true$")
57+ }
58+
59+ Rectangle {
60+ anchors.fill: dashContent
61+ color: "#FCFCFC"
62+ }
63+
64+ DashContent {
65+ id: dashContent
66+
67+ model: filteredScopes
68+ searchHistory: SearchHistoryModel { }
69+ property Scope scope: scopes.get(currentIndex)
70+
71+ anchors {
72+ top: parent.top
73+ bottom: parent.bottom
74+ left: parent.left
75+ right: controls.left
76+ }
77+ }
78+
79+ Rectangle {
80+ id: controls
81+ color: "lightgrey"
82+ width: units.gu(40)
83+ anchors {
84+ top: parent.top
85+ bottom: parent.bottom
86+ right: parent.right
87+ }
88+
89+ MouseArea {
90+ anchors.fill: parent
91+ }
92+
93+ Column {
94+ anchors { fill: parent; margins: units.gu(1) }
95+ spacing: units.gu(1)
96+
97+ Label {
98+ text: "Search query"
99+ }
100+
101+ TextField {
102+ id: searchField
103+ anchors { left: parent.left; right: parent.right }
104+
105+ onTextChanged: dashContent.scope.searchQuery = text
106+
107+ Connections {
108+ target: dashContent.scope
109+ onSearchQueryChanged: searchField.text = dashContent.scope.searchQuery
110+ }
111+ }
112+
113+ Label {
114+ text: "Category"
115+ height: units.gu(4)
116+ verticalAlignment: Text.AlignBottom
117+ }
118+
119+ OptionSelector {
120+ id: categorySelector
121+ anchors { left: parent.left; right: parent.right }
122+ model: dashContent.scope ? dashContent.scope.categories : null
123+
124+ property Item selectedItem
125+
126+ delegate: OptionSelectorDelegate {
127+ id: categoryDelegate
128+ text: model.name
129+ property string categoryId: model.categoryId
130+ property string template: JSON.stringify(JSON.parse(model.rawRendererTemplate), null, " ");
131+
132+ onSelectedChanged: if (selected) categorySelector.selectedItem = categoryDelegate
133+ }
134+ }
135+
136+ TextArea {
137+ id: categoryJson
138+ width: parent.width
139+ autoSize: true
140+ readOnly: true
141+ text: categorySelector.selectedItem && categorySelector.selectedItem.template
142+ }
143+
144+ Button {
145+ width: parent.width
146+ text: "Override category"
147+ onClicked: {
148+ PopupUtils.open(categoryEditor)
149+ }
150+ }
151+ }
152+ }
153+
154+ Component {
155+ id: categoryEditor
156+
157+ ComposerSheet {
158+ id: sheet
159+ title: "Editing category definition"
160+
161+ TextArea {
162+ id: categoryEditorArea
163+ anchors.fill: parent
164+ wrapMode: Text.WordWrap
165+ text: categoryJson.text
166+ }
167+
168+ onCancelClicked: PopupUtils.close(sheet)
169+ onConfirmClicked: {
170+ PopupUtils.close(sheet);
171+ dashContent.scope.categories.overrideCategoryJson(categorySelector.selectedItem.categoryId, categoryEditorArea.text);
172+ }
173+ }
174+ }
175+}
src/CMakeLists.txt+12−1
@@ -5,11 +5,22 @@ include_directories(
5 ${UNITY-MIR_INCLUDE_DIRS}
5 ${UNITY-MIR_INCLUDE_DIRS}
6)
6)
7
7
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+
8add_executable(${SHELL_APP}
18add_executable(${SHELL_APP}
9 ApplicationArguments.h
19 ApplicationArguments.h
10 main.cpp
20 main.cpp
11 MouseTouchAdaptor.cpp
21 MouseTouchAdaptor.cpp
22+ ${QML_FILES} # This is to make qml and image files appear in the IDE's project tree
12− )
23+)
13
24
14qt5_use_modules(${SHELL_APP} Gui Qml Quick Test)
25qt5_use_modules(${SHELL_APP} Gui Qml Quick Test)
15pkg_check_modules(XCB REQUIRED xcb)
26pkg_check_modules(XCB REQUIRED xcb)
tests/autopilot/unity8/indicators/__init__.pynew file

No changes to the content.

tests/autopilot/unity8/indicators/tests/__init__.pynew file

No changes to the content.

tests/autopilot/unity8/indicators/tests/test_indicators.pynew file+50
@@ -0,0 +1,50 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity - Indicators Autopilot Test Suite
4+# Copyright (C) 2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+from __future__ import absolute_import
21+
22+from autopilot import platform
23+from testtools import skipIf
24+
25+from unity8.process_helpers import unlock_unity
26+from unity8.shell.tests import UnityTestCase
27+
28+
29+class IndicatorTestCase(UnityTestCase):
30+
31+ scenarios = [
32+ ('Bluetooth', dict(indicator_name='indicator-bluetooth')),
33+ ('Datetime', dict(indicator_name='indicator-datetime')),
34+ ('Location', dict(indicator_name='indicator-location')),
35+ ('Messaging', dict(indicator_name='indicator-messages')),
36+ ('Network', dict(indicator_name='indicator-network')),
37+ ('Power', dict(indicator_name='indicator-power')),
38+ ('Sound', dict(indicator_name='indicator-sound')),
39+ ]
40+
41+ @skipIf(platform.image_codename() == 'Desktop',
42+ "Unity8/phablet-only test!")
43+ def setUp(self):
44+ super(IndicatorTestCase, self).setUp()
45+
46+ def test_indicator_exists(self):
47+ unity_proxy = self.launch_unity()
48+ unlock_unity(unity_proxy)
49+ indicator = self.main_window.get_indicator(self.indicator_name)
50+ self.assertIsNotNone(indicator)
tests/autopilot/unity8/shell/emulators/__init__.py+11−4
@@ -1,7 +1,7 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2#
2#
3# Unity Autopilot Test Suite
3# Unity Autopilot Test Suite
4−# Copyright (C) 2012-2013 Canonical
4+# Copyright (C) 2012, 2013, 2014 Canonical
5#
5#
6# This program is free software: you can redistribute it and/or modify
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
7# it under the terms of the GNU General Public License as published by
@@ -17,9 +17,16 @@
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18#
18#
19
19
20+from autopilot import input, introspection
20
21
21−from autopilot.introspection import CustomEmulatorBase
22
22
23−
24−class UnityEmulatorBase(CustomEmulatorBase):
23+class UnityEmulatorBase(introspection.CustomEmulatorBase):
25 """A base class for all unity emulators."""
24 """A base class for all unity emulators."""
25+
26+ def __init__(self, *args):
27+ super(UnityEmulatorBase, self).__init__(*args)
28+ self.pointing_device = input.Touch.create()
29+
30+
31+class UnityEmulatorException(Exception):
32+ """Exception raised when there is an error with the Unity emulators."""
tests/autopilot/unity8/shell/emulators/create_interactive_notification.py+1−1
@@ -87,7 +87,7 @@ if __name__ == '__main__':
87 action_id,
87 action_id,
88 action_label,
88 action_label,
89 action_callback,
89 action_callback,
90− None,
90+ (None, ),
91 None
91 None
92 )
92 )
93
93
tests/autopilot/unity8/shell/emulators/dash.py+102−8
@@ -1,7 +1,7 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2#
2#
3# Unity Autopilot Test Suite
3# Unity Autopilot Test Suite
4−# Copyright (C) 2012-2013 Canonical
4+# Copyright (C) 2012, 2013, 2014 Canonical
5#
5#
6# This program is free software: you can redistribute it and/or modify
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
7# it under the terms of the GNU General Public License as published by
@@ -17,13 +17,25 @@
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18#
18#
19
19
20−from unity8.shell.emulators import UnityEmulatorBase
20+import logging
21
21
22+from unity8.shell import emulators
22
23
23−class Dash(UnityEmulatorBase):
24+from autopilot import logging as autopilot_logging
25+from autopilot.introspection import dbus
24
26
27+
28+logger = logging.getLogger(__name__)
29+
30+
31+class Dash(emulators.UnityEmulatorBase):
25 """An emulator that understands the Dash."""
32 """An emulator that understands the Dash."""
26
33
34+ def __init__(self, *args):
35+ super(Dash, self).__init__(*args)
36+ self.dash_content_list = self.wait_select_single(
37+ 'QQuickListView', objectName='dashContentList')
38+
27 def get_home_applications_grid(self):
39 def get_home_applications_grid(self):
28 get_grid = self.get_scope('home').wait_select_single(
40 get_grid = self.get_scope('home').wait_select_single(
29 "GenericFilterGrid",
41 "GenericFilterGrid",
@@ -43,9 +55,91 @@ class Dash(UnityEmulatorBase):
43 return resp_grid.select_single('Tile', text=text)
55 return resp_grid.select_single('Tile', text=text)
44
56
45 def get_scope(self, scope_name='home'):
57 def get_scope(self, scope_name='home'):
46− dash_content = self.wait_select_single(
47− 'QQuickListView',
48− objectName='dashContentList'
49− )
50 scope_id = "%s.scope" % scope_name
58 scope_id = "%s.scope" % scope_name
59+ return self.dash_content_list.select_single(
51− return dash_content.select_single('QQuickLoader', scopeId=scope_id)
60+ 'QQuickLoader', scopeId=scope_id)
61+
62+ @autopilot_logging.log_action(logger.info)
63+ def open_scope(self, scope_name):
64+ """Open a dash scope.
65+
66+ :parameter scope_name: The name of the scope.
67+ :return: The scope.
68+
69+ """
70+ scope_id = "%s.scope" % scope_name
71+ scope_loader = self._get_scope_loader(scope_id)
72+ if scope_loader.isCurrent:
73+ logger.info('The scope is already open.')
74+ return self._get_scope_from_loader(scope_loader)
75+ else:
76+ return self._open_scope_scrolling(scope_loader)
77+
78+ def _get_scope_loader(self, scope_id):
79+ try:
80+ return self.dash_content_list.select_single(
81+ 'QQuickLoader', scopeId=scope_id)
82+ except dbus.StateNotFoundError:
83+ raise emulators.UnityEmulatorException(
84+ 'No scope found with id {0}'.format(scope_id))
85+
86+ def _get_scope_from_loader(self, loader):
87+ if loader.scopeId == 'applications.scope':
88+ return loader.select_single(DashApps)
89+ else:
90+ return loader.select_single(GenericScopeView)
91+
92+ def _open_scope_scrolling(self, scope_loader):
93+ scroll = self._get_scroll_direction(scope_loader)
94+
95+ while not scope_loader.isCurrent:
96+ scroll()
97+
98+ scope = self._get_scope_from_loader(scope_loader)
99+ scope.moving.wait_for(False)
100+ scope.isCurrent.wait_for(True)
101+ return scope
102+
103+ def _get_scroll_direction(self, scope_loader):
104+ current_scope_loader = self.dash_content_list.select_single(
105+ 'QQuickLoader', isCurrent=True)
106+ if scope_loader.globalRect.x < current_scope_loader.globalRect.x:
107+ return self._scroll_to_left_scope
108+ elif scope_loader.globalRect.x > current_scope_loader.globalRect.x:
109+ return self._scroll_to_right_scope
110+ else:
111+ raise emulators.UnityEmulatorException('The scope is already open')
112+
113+ @autopilot_logging.log_action(logger.info)
114+ def _scroll_to_left_scope(self):
115+ original_index = self.dash_content_list.currentIndex
116+ # Scroll on the border of the page header, because some scopes have
117+ # contents that can be scrolled horizontally.
118+ page_header = self.select_single('PageHeader')
119+ border = page_header.select_single('QQuickBorderImage')
120+ start_x = border.width / 3
121+ stop_x = border.width / 3 * 2
122+ start_y = stop_y = border.globalRect.y + border.height / 2
123+ self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
124+ self.dash_content_list.currentIndex.wait_for(original_index - 1)
125+
126+ @autopilot_logging.log_action(logger.info)
127+ def _scroll_to_right_scope(self):
128+ original_index = self.dash_content_list.currentIndex
129+ # Scroll on the border of the page header, because some scopes have
130+ # contents that can be scrolled horizontally.
131+ page_header = self.select_single('PageHeader')
132+ border = page_header.select_single('QQuickBorderImage')
133+ start_x = border.width / 3 * 2
134+ stop_x = border.width / 3
135+ start_y = stop_y = border.globalRect.y + border.height / 2
136+ self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
137+ self.dash_content_list.currentIndex.wait_for(original_index + 1)
138+
139+
140+class GenericScopeView(emulators.UnityEmulatorBase):
141+ """Autopilot emulators for generic scopes."""
142+
143+
144+class DashApps(emulators.UnityEmulatorBase):
145+ """Autopilot emulators for the applications scope."""
tests/autopilot/unity8/shell/emulators/greeter.py+2−5
@@ -1,7 +1,7 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2#
2#
3# Unity Autopilot Test Suite
3# Unity Autopilot Test Suite
4−# Copyright (C) 2012-2013 Canonical
4+# Copyright (C) 2012, 2013, 2014 Canonical
5#
5#
6# This program is free software: you can redistribute it and/or modify
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
7# it under the terms of the GNU General Public License as published by
@@ -18,24 +18,21 @@
18#
18#
19
19
20from unity8.shell.emulators import UnityEmulatorBase
20from unity8.shell.emulators import UnityEmulatorBase
21−from autopilot.input import Touch
22
21
23
22
24class Greeter(UnityEmulatorBase):
23class Greeter(UnityEmulatorBase):
25−
26 """An emulator that understands the greeter screen."""
24 """An emulator that understands the greeter screen."""
27
25
28 def swipe(self):
26 def swipe(self):
29 """Swipe the greeter screen away."""
27 """Swipe the greeter screen away."""
30 self.created.wait_for(True)
28 self.created.wait_for(True)
31− touch = Touch.create()
32
29
33 rect = self.globalRect
30 rect = self.globalRect
34 start_x = rect[0] + rect[2] - 3
31 start_x = rect[0] + rect[2] - 3
35 start_y = int(rect[1] + rect[3] / 2)
32 start_y = int(rect[1] + rect[3] / 2)
36 stop_x = int(rect[0] + rect[2] * 0.2)
33 stop_x = int(rect[0] + rect[2] * 0.2)
37 stop_y = start_y
34 stop_y = start_y
38− touch.drag(start_x, start_y, stop_x, stop_y)
35+ self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
39
36
40 self.created.wait_for(False)
37 self.created.wait_for(False)
41
38
tests/autopilot/unity8/shell/emulators/main_window.py+6
@@ -91,5 +91,11 @@ class MainWindow(object):
91 def get_pinentryField(self):
91 def get_pinentryField(self):
92 return self.app.select_single(objectName="pinentryField")
92 return self.app.select_single(objectName="pinentryField")
93
93
94+ def get_indicator(self, indicator_name):
95+ return self.app.select_single(
96+ 'DefaultIndicatorWidget',
97+ objectName=indicator_name+'-widget'
98+ )
99+
94 def get_shell_background(self):
100 def get_shell_background(self):
95 return self.app.select_single("CrossFadeImage", objectName="backgroundImage")
101 return self.app.select_single("CrossFadeImage", objectName="backgroundImage")
tests/autopilot/unity8/shell/tests/__init__.py+1
@@ -147,6 +147,7 @@ class UnityTestCase(AutopilotTestCase):
147 if model() != "Desktop":
147 if model() != "Desktop":
148 from autopilot.input import _uinput
148 from autopilot.input import _uinput
149 _uinput._touch_device = _uinput.create_touch_device()
149 _uinput._touch_device = _uinput.create_touch_device()
150+ self.addCleanup(_uinput._touch_device.close)
150 ####
151 ####
151
152
152 self.touch = Touch.create()
153 self.touch = Touch.create()
tests/autopilot/unity8/shell/tests/test_emulators.pynew file+120
@@ -0,0 +1,120 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2014 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+import mock
21+
22+from unity8 import process_helpers
23+from unity8.shell import emulators, tests
24+from unity8.shell.emulators import dash as dash_emulators
25+
26+
27+class DashEmulatorTestCase(tests.UnityTestCase):
28+
29+ scenarios = tests._get_device_emulation_scenarios()
30+
31+ def setUp(self):
32+ super(DashEmulatorTestCase, self).setUp()
33+ unity_proxy = self.launch_unity()
34+ process_helpers.unlock_unity(unity_proxy)
35+ self.dash = self.main_window.get_dash()
36+
37+ def test_open_unexisting_scope(self):
38+ scope_name = 'unexisting'
39+ with mock.patch.object(self.dash, 'pointing_device') as mock_pointer:
40+ exception = self.assertRaises(
41+ emulators.UnityEmulatorException,
42+ self.dash.open_scope, scope_name)
43+
44+ self.assertEqual(
45+ 'No scope found with id unexisting.scope', str(exception))
46+ self.assertFalse(mock_pointer.called)
47+
48+ def test_open_already_opened_scope(self):
49+ scope_id = self._get_current_scope_id()
50+ with mock.patch.object(self.dash, 'pointing_device') as mock_pointer:
51+ scope = self.dash.open_scope(self._get_scope_name_from_id(
52+ scope_id))
53+
54+ self.assertFalse(mock_pointer.called)
55+ self._assert_scope_is_opened(scope, scope_id)
56+
57+ def _assert_scope_is_opened(self, scope, scope_id):
58+ self.assertTrue(scope.isCurrent)
59+ scope_loader = scope.get_parent()
60+ self.assertEqual(scope_loader.scopeId, scope_id)
61+
62+ def _get_current_scope_id(self):
63+ scope = self.dash.dash_content_list.select_single(
64+ 'QQuickLoader', isCurrent=True)
65+ return scope.scopeId
66+
67+ def test_open_scope_to_the_right(self):
68+ leftmost_scope = self._get_scope_name_from_id(
69+ self._get_leftmost_scope_id())
70+ self.dash.open_scope(leftmost_scope)
71+
72+ scope_id = self._get_rightmost_scope_id()
73+ scope = self.dash.open_scope(self._get_scope_name_from_id(scope_id))
74+ self._assert_scope_is_opened(scope, scope_id)
75+
76+ def _get_leftmost_scope_id(self):
77+ scope_loaders = self._get_scope_loaders()
78+ leftmost_scope_loader = scope_loaders[0]
79+ for loader in scope_loaders[1:]:
80+ if loader.globalRect.x < leftmost_scope_loader.globalRect.x:
81+ leftmost_scope_loader = loader
82+ return leftmost_scope_loader.scopeId
83+
84+ def _get_scope_loaders(self):
85+ item = self.dash.dash_content_list.get_children_by_type(
86+ 'QQuickItem')[0]
87+ return item.get_children_by_type('QQuickLoader')
88+
89+ def _get_scope_name_from_id(self, scope_id):
90+ if scope_id.endswith('.scope'):
91+ return scope_id[:-6]
92+
93+ def _get_rightmost_scope_id(self):
94+ scope_loaders = self._get_scope_loaders()
95+ rightmost_scope_loader = scope_loaders[0]
96+ for loader in scope_loaders[1:]:
97+ if loader.globalRect.x > rightmost_scope_loader.globalRect.x:
98+ rightmost_scope_loader = loader
99+ return rightmost_scope_loader.scopeId
100+
101+ def test_open_scope_to_the_left(self):
102+ rightmost_scope = self._get_scope_name_from_id(
103+ self._get_rightmost_scope_id())
104+ self.dash.open_scope(rightmost_scope)
105+
106+ scope_id = self._get_leftmost_scope_id()
107+ scope = self.dash.open_scope(self._get_scope_name_from_id(scope_id))
108+ self._assert_scope_is_opened(scope, scope_id)
109+
110+ def test_open_generic_scope(self):
111+ scope_id = 'home.scope'
112+ scope = self.dash.open_scope(self._get_scope_name_from_id(scope_id))
113+ self._assert_scope_is_opened(scope, scope_id)
114+ self.assertIsInstance(scope, dash_emulators.GenericScopeView)
115+
116+ def test_open_applications_scope(self):
117+ scope_id = 'applications.scope'
118+ scope = self.dash.open_scope(self._get_scope_name_from_id(scope_id))
119+ self._assert_scope_is_opened(scope, scope_id)
120+ self.assertIsInstance(scope, dash_emulators.DashApps)
tests/mocks/LightDM/CMakeLists.txt+1−1
@@ -33,7 +33,7 @@ add_library(MockLightDM-qml MODULE
33# installed on the system. So we make sure we link to our full fake version
33# installed on the system. So we make sure we link to our full fake version
34# At run time, we can point to whichever version we happen to be using via
34# At run time, we can point to whichever version we happen to be using via
35# LD_LIBRARY_PATH.
35# LD_LIBRARY_PATH.
36−add_dependencies(MockLightDM-qml MockLightDM-full)
36+add_dependencies(MockLightDM-qml MockLightDM-full MockUserMetrics)
37target_link_libraries(MockLightDM-qml
37target_link_libraries(MockLightDM-qml
38 -L${CMAKE_CURRENT_BINARY_DIR}/full
38 -L${CMAKE_CURRENT_BINARY_DIR}/full
39 -llightdm-qt5-2
39 -llightdm-qt5-2
tests/mocks/Unity/fake_scopes.cpp+3
@@ -35,6 +35,7 @@ Scopes::Scopes(QObject *parent)
35 m_roles[Scopes::RoleScope] = "scope";
35 m_roles[Scopes::RoleScope] = "scope";
36 m_roles[Scopes::RoleId] = "id";
36 m_roles[Scopes::RoleId] = "id";
37 m_roles[Scopes::RoleVisible] = "visible";
37 m_roles[Scopes::RoleVisible] = "visible";
38+ m_roles[Scopes::RoleTitle] = "title";
38
39
39 QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
40 QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
40 QObject::connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
41 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
110 return QVariant::fromValue(scope->id());
111 return QVariant::fromValue(scope->id());
111 } else if (role == Scopes::RoleVisible) {
112 } else if (role == Scopes::RoleVisible) {
112 return QVariant::fromValue(scope->visible());
113 return QVariant::fromValue(scope->visible());
114+ } else if (role == Scopes::RoleTitle) {
115+ return QVariant::fromValue(scope->name());
113 } else {
116 } else {
114 return QVariant();
117 return QVariant();
115 }
118 }
tests/mocks/Unity/fake_scopes.h+2−1
@@ -41,7 +41,8 @@ public:
41 enum Roles {
41 enum Roles {
42 RoleScope,
42 RoleScope,
43 RoleId,
43 RoleId,
44− RoleVisible
44+ RoleVisible,
45+ RoleTitle
45 };
46 };
46
47
47 Q_INVOKABLE int rowCount(const QModelIndex& parent = QModelIndex()) const;
48 Q_INVOKABLE int rowCount(const QModelIndex& parent = QModelIndex()) const;
tests/plugins/DashViews/CMakeLists.txt+36−9
@@ -40,15 +40,41 @@ qt5_use_modules(listviewwithpageheadertestsectionexternalmodelExec Test Core Qml
40target_link_libraries(listviewwithpageheadertestsectionexternalmodelExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
40target_link_libraries(listviewwithpageheadertestsectionexternalmodelExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
41add_custom_target(testlistviewwithpageheadersectionexternalmodel ${CMAKE_CURRENT_BINARY_DIR}/listviewwithpageheadertestsectionexternalmodelExec -o ${CMAKE_BINARY_DIR}/testlistviewwithpageheadersectionexternalmodel.xml,xunitxml -o -,txt)
41add_custom_target(testlistviewwithpageheadersectionexternalmodel ${CMAKE_CURRENT_BINARY_DIR}/listviewwithpageheadertestsectionexternalmodelExec -o ${CMAKE_BINARY_DIR}/testlistviewwithpageheadersectionexternalmodel.xml,xunitxml -o -,txt)
42
42
43−add_executable(verticaljournaltestExec verticaljournaltest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/verticaljournal.cpp)
43+macro(add_journal_try_test FILENAME)
44−qt5_use_modules(verticaljournaltestExec Test Core Qml)
44+ # Capitalite the first letter for the test/try name targets
45−target_link_libraries(verticaljournaltestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
45+ string(SUBSTRING ${FILENAME} 0 1 FIRST_LETTER)
46−add_custom_target(testVerticalJournal ${CMAKE_CURRENT_BINARY_DIR}/verticaljournaltestExec -o ${CMAKE_BINARY_DIR}/testVerticalJournal.xml,xunitxml -o -,txt)
46+ string(TOUPPER ${FIRST_LETTER} FIRST_LETTER)
47+ string(REGEX REPLACE "^.(.*)" "${FIRST_LETTER}\\1" CUSTOM_TARGET_NAME "${FILENAME}")
47
48
49+ add_executable(${FILENAME}testExec
50+ ${FILENAME}test.cpp
51+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
48−add_executable(verticaljournaltryExec verticaljournaltry.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/verticaljournal.cpp)
52+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractjournal.cpp
53+ )
49−qt5_use_modules(verticaljournaltryExec Test Core Qml)
54+ qt5_use_modules(${FILENAME}testExec Test Core Qml)
50−target_link_libraries(verticaljournaltryExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
55+ target_link_libraries(${FILENAME}testExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
51−add_custom_target(tryVerticalJournal ${CMAKE_CURRENT_BINARY_DIR}/verticaljournaltryExec -o ${CMAKE_BINARY_DIR}/testVerticalJournal.xml,xunitxml -o -,txt)
56+ add_custom_target(test${CUSTOM_TARGET_NAME}
57+ ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME}testExec
58+ -o ${CMAKE_BINARY_DIR}/test${CUSTOM_TARGET_NAME}.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+ DEPENDS ${FILENAME}tryExec DashViews-qml
73+ )
74+endmacro()
75+
76+add_journal_try_test(verticaljournal)
77+add_journal_try_test(horizontaljournal)
52
78
53set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
79set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
54add_qml_test(. listviewwithpageheaderqml IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
80add_qml_test(. listviewwithpageheaderqml IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
@@ -57,4 +83,5 @@ add_dependencies(qmluitests testlistviewwithpageheader)
57add_dependencies(qmluitests testlistviewwithpageheadersection)
83add_dependencies(qmluitests testlistviewwithpageheadersection)
58add_dependencies(qmluitests testlistviewwithpageheadersectionexternalmodel)
84add_dependencies(qmluitests testlistviewwithpageheadersectionexternalmodel)
59add_dependencies(qmluitests testlistviewwithpageheaderqml)
85add_dependencies(qmluitests testlistviewwithpageheaderqml)
60−add_dependencies(qmluitests testVerticalJournal)
86+add_dependencies(qmluitests testVerticaljournal)
87+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+5−5
@@ -30,7 +30,7 @@
30
30
31#include "verticaljournal.h"
31#include "verticaljournal.h"
32
32
33−class QHeightModel : public QAbstractListModel {
33+class HeightModel : public QAbstractListModel {
34public:
34public:
35 QHash<int, QByteArray> roleNames() const
35 QHash<int, QByteArray> roleNames() const
36 {
36 {
@@ -127,7 +127,7 @@ private Q_SLOTS:
127 view->setResizeMode(QQuickView::SizeRootObjectToView);
127 view->setResizeMode(QQuickView::SizeRootObjectToView);
128 view->engine()->addImportPath(BUILT_PLUGINS_DIR);
128 view->engine()->addImportPath(BUILT_PLUGINS_DIR);
129
129
130− model = new QHeightModel();
130+ model = new HeightModel();
131 QStringList heightList;
131 QStringList heightList;
132 heightList << "100" << "50" << "125" << "10" << "40" << "70" << "200" << "110" << "160" << "20" << "20" << "65" << "80" << "200" << "300" << "130" << "400" << "300" << "500" << "10";
132 heightList << "100" << "50" << "125" << "10" << "40" << "70" << "200" << "110" << "160" << "20" << "20" << "65" << "80" << "200" << "300" << "130" << "400" << "300" << "500" << "10";
133 model->setStringList(heightList);
133 model->setStringList(heightList);
@@ -323,7 +323,7 @@ private Q_SLOTS:
323
323
324 void testChangeModel()
324 void testChangeModel()
325 {
325 {
326− QHeightModel *model2 = new QHeightModel();
326+ HeightModel *model2 = new HeightModel();
327 QStringList list2;
327 QStringList list2;
328 list2 << "100" << "50" << "25" << "25" << "50" << "50";
328 list2 << "100" << "50" << "25" << "25" << "50" << "50";
329 model2->setStringList(list2);
329 model2->setStringList(list2);
@@ -393,7 +393,7 @@ private Q_SLOTS:
393
393
394 void testModelAppendRemoveLast()
394 void testModelAppendRemoveLast()
395 {
395 {
396− QHeightModel *model2 = new QHeightModel();
396+ HeightModel *model2 = new HeightModel();
397 QStringList list2;
397 QStringList list2;
398 list2 << "100" << "50" << "25" << "25" << "50" << "50";
398 list2 << "100" << "50" << "25" << "25" << "50" << "50";
399 model2->setStringList(list2);
399 model2->setStringList(list2);
@@ -462,7 +462,7 @@ private Q_SLOTS:
462private:
462private:
463 QQuickView *view;
463 QQuickView *view;
464 VerticalJournal *vj;
464 VerticalJournal *vj;
465− QHeightModel *model;
465+ HeightModel *model;
466};
466};
467
467
468QTEST_MAIN(VerticalJournalTest)
468QTEST_MAIN(VerticalJournalTest)
tests/plugins/LightDM/CMakeLists.txt+1
@@ -11,6 +11,7 @@ include_directories(
11 ${LIBLIGHTDM_INCLUDE_DIRS}
11 ${LIBLIGHTDM_INCLUDE_DIRS}
12 )
12 )
13
13
14+add_dependencies(test-lightdm-dbus MockLightDM-full)
14target_link_libraries(test-lightdm-dbus
15target_link_libraries(test-lightdm-dbus
15 -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full
16 -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full
16 -llightdm-qt5-2
17 -llightdm-qt5-2
tests/qmltests/CMakeLists.txt+3−2
@@ -37,12 +37,13 @@ add_qml_test(Components Stage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
37 ${CMAKE_BINARY_DIR}/tests/mocks
37 ${CMAKE_BINARY_DIR}/tests/mocks
38 ${CMAKE_BINARY_DIR}/plugins)
38 ${CMAKE_BINARY_DIR}/plugins)
39add_qml_test(Components Tile)
39add_qml_test(Components Tile)
40−add_qml_test(Components PageHeader)
40+add_qml_test(Components PageHeaderLabel)
41add_qml_test(Dash Dash IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
41add_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})
43add_qml_test(Dash DashContent IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
42add_qml_test(Dash DashContent IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
44add_qml_test(Dash DashPreview)
43add_qml_test(Dash DashPreview)
45add_qml_test(Dash GenericPreview)
44add_qml_test(Dash GenericPreview)
45+add_qml_test(Dash Card)
46+add_qml_test(Dash CardHeader)
46add_qml_test(Dash GenericScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
47add_qml_test(Dash GenericScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
47add_qml_test(Dash FilterGrids IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_SOURCE_DIR}/plugins
48add_qml_test(Dash FilterGrids IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_SOURCE_DIR}/plugins
48 ${CMAKE_BINARY_DIR}/tests/mocks)
49 ${CMAKE_BINARY_DIR}/tests/mocks)
tests/qmltests/Components/tst_PageHeader.qml → tests/qmltests/Components/tst_PageHeaderLabel.qmlrenamed+2−2
@@ -31,7 +31,7 @@ Item {
31 }
31 }
32
32
33 UT.UnityTestCase {
33 UT.UnityTestCase {
34− name: "PageHeaderTest"
34+ name: "PageHeaderLabelTest"
35 when: windowShown
35 when: windowShown
36
36
37 property alias searchEnabled : pageHeader.searchEntryEnabled
37 property alias searchEnabled : pageHeader.searchEntryEnabled
@@ -204,7 +204,7 @@ Item {
204 anchors.fill: parent
204 anchors.fill: parent
205 spacing: units.gu(1)
205 spacing: units.gu(1)
206
206
207− PageHeader {
207+ PageHeaderLabel {
208 id: pageHeader
208 id: pageHeader
209 anchors {
209 anchors {
210 left: parent.left
210 left: parent.left
tests/qmltests/Dash/CardHelpers.jsnew file+75
@@ -0,0 +1,75 @@
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+.pragma library
18+
19+var components = ["title", "art", "subtitle", "mascot", "emblem", "old-price", "price", "alt-price", "rating", "alt-rating", "summary"]
20+
21+function tryParse(json, errorLabel) {
22+ var o = undefined;
23+ if (errorLabel !== undefined) {
24+ errorLabel.text = "";
25+ }
26+ try {
27+ o = JSON.parse(json)
28+ } catch(err) {
29+ if (errorLabel !== undefined) {
30+ errorLabel.text = err + "";
31+ } else {
32+ console.debug(err);
33+ }
34+ }
35+ return o;
36+}
37+
38+function mapData(json, layout, errorLabel) {
39+ var o = tryParse(json, errorLabel);
40+ var d = undefined;
41+
42+ if (o !== undefined) {
43+ d = Object();
44+ for (var k in components) {
45+ try {
46+ if (typeof layout[components[k]] == "object") {
47+ d[components[k]] = o[layout[components[k]]['field']];
48+ } else {
49+ d[components[k]] = o[layout[components[k]]];
50+ }
51+ } catch(err) {
52+ d[components[k]] = undefined;
53+ }
54+ }
55+ }
56+ return d;
57+}
58+
59+function update(object, overrides) {
60+ for (var k in overrides) {
61+ if (typeof object[k] == "string" && typeof overrides[k] == "object") {
62+ if (!overrides[k].hasOwnProperty('field')) overrides[k]['field'] = object[k];
63+ }
64+ if (object[k] === null) {
65+ object[k] = overrides[k];
66+ } else if (typeof object[k] == "object" && typeof overrides[k] == "string") {
67+ object[k]['field'] = overrides[k];
68+ } else if (typeof object[k] == "object") {
69+ update(object[k], overrides[k]);
70+ } else {
71+ object[k] = overrides[k];
72+ }
73+ }
74+ return object;
75+}
tests/qmltests/Dash/artwork/avatar@12.pngnew file

Binary file not shown.

tests/qmltests/Dash/artwork/music-player-design.pngnew file

Binary file not shown.

tests/qmltests/Dash/qml/FakeScopeView.qml+1
@@ -23,6 +23,7 @@ GenericScopeView {
23 id: fakeScopeView
23 id: fakeScopeView
24
24
25 property alias backColor : back.color
25 property alias backColor : back.color
26+ property var categoryView: null
26
27
27 onEndReached: {
28 onEndReached: {
28 if (scope != undefined && shell != undefined && shell.scopeStatus != undefined) {
29 if (scope != undefined && shell != undefined && shell.scopeStatus != undefined) {
tests/qmltests/Dash/tst_Card.qmlnew file+337
@@ -0,0 +1,337 @@
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 QtTest 1.0
19+import Ubuntu.Components 0.1
20+import Unity.Test 0.1 as UT
21+import "../../../qml/Dash"
22+import "CardHelpers.js" as Helpers
23+
24+Rectangle {
25+ id: root
26+ width: units.gu(80)
27+ height: units.gu(72)
28+ color: "#88FFFFFF"
29+
30+ property string defaultLayout: '
31+ {
32+ "schema-version": 1,
33+ "template": {
34+ "category-layout": "grid",
35+ "card-layout": "vertical",
36+ "card-size": "medium",
37+ "overlay-mode": null,
38+ "collapsed-rows": 2
39+ },
40+ "components": {
41+ "title": null,
42+ "art": {
43+ "aspect-ratio": 1.0,
44+ "fill-mode": "crop"
45+ },
46+ "subtitle": null,
47+ "mascot": null,
48+ "emblem": null,
49+ "old-price": null,
50+ "price": null,
51+ "alt-price": null,
52+ "rating": {
53+ "type": "stars",
54+ "range": [0, 5],
55+ "full": "image://theme/rating-star-full",
56+ "half": "image://theme/rating-star-half",
57+ "empty": "image://theme/rating-star-empty"
58+ },
59+ "alt-rating": null,
60+ "summary": null
61+ },
62+ "resources": {}
63+ }'
64+
65+ property string cardData: '
66+ {
67+ "art": "../../tests/qmltests/Dash/artwork/music-player-design.png",
68+ "mascot": "../../tests/qmltests/Dash/artwork/avatar.png",
69+ "title": "foo",
70+ "subtitle": "bar",
71+ "summary": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
72+ }'
73+
74+ property string fullMapping: '
75+ {
76+ "title": "title",
77+ "art": "art",
78+ "subtitle": "subtitle",
79+ "mascot": "mascot",
80+ "summary": "summary"
81+ }'
82+
83+ property var cardsModel: [
84+ {
85+ "name": "Art, header, summary - vertical",
86+ "layout": { "components": JSON.parse(fullMapping) }
87+ },
88+ {
89+ "name": "Art, header, summary - vertical, small",
90+ "layout": { "template": { "card-size": "small" }, "components": JSON.parse(fullMapping) }
91+ },
92+ {
93+ "name": "Art, header, summary - vertical, large",
94+ "layout": { "template": { "card-size": "large" }, "components": JSON.parse(fullMapping) }
95+ },
96+ {
97+ "name": "Art, header, summary - vertical, wide",
98+ "layout": { "components": Helpers.update(JSON.parse(root.fullMapping), { "art": { "aspect-ratio": 2 } }) }
99+ },
100+ {
101+ "name": "Art, title - vertical, fitted",
102+ "layout": { "components": Helpers.update(JSON.parse(root.fullMapping), { "art": { "fill-mode": "fit" } }) }
103+ },
104+ {
105+ "name": "Art, header, summary - horizontal",
106+ "layout": { "template": { "card-layout": "horizontal" },
107+ "components": JSON.parse(fullMapping) }
108+ },
109+ ]
110+
111+ Card {
112+ id: card
113+ anchors { top: parent.top; left: parent.left; margins: units.gu(1) }
114+
115+ template: Helpers.update(JSON.parse(root.defaultLayout), Helpers.tryParse(layoutArea.text, layoutError))['template'];
116+ components: Helpers.update(JSON.parse(root.defaultLayout), Helpers.tryParse(layoutArea.text, layoutError))['components'];
117+ cardData: Helpers.mapData(dataArea.text, components, dataError)
118+ }
119+
120+ Rectangle {
121+ anchors { top: parent.top; bottom: parent.bottom; right: parent.right}
122+ width: units.gu(40)
123+ color: "lightgrey"
124+
125+ Column {
126+ anchors { fill: parent; margins: units.gu(1) }
127+ spacing: units.gu(1)
128+
129+ OptionSelector {
130+ id: selector
131+ model: cardsModel
132+ delegate: OptionSelectorDelegate { text: modelData.name }
133+ onSelectedIndexChanged: updateAreas()
134+ Component.onCompleted: updateAreas()
135+
136+ function updateAreas() {
137+ var element = cardsModel[selectedIndex];
138+ if (element) {
139+ layoutArea.text = JSON.stringify(element.layout, undefined, 2) || "{}";
140+ // FIXME: don't overwrite data
141+ var data = JSON.parse(root.cardData);
142+ Helpers.update(data, element.data);
143+ dataArea.text = JSON.stringify(data, undefined, 2) || "{}";
144+ } else {
145+ layoutArea.text = "";
146+ dataArea.text = "";
147+ }
148+
149+ }
150+ }
151+
152+ TextArea {
153+ id: layoutArea
154+ anchors { left: parent.left; right: parent.right }
155+ height: units.gu(25)
156+ }
157+
158+ Label {
159+ id: layoutError
160+ anchors { left: parent.left; right: parent.right }
161+ height: units.gu(4)
162+ color: "orange"
163+ }
164+
165+ TextArea {
166+ id: dataArea
167+ anchors { left: parent.left; right: parent.right }
168+ height: units.gu(25)
169+ }
170+
171+ Label {
172+ id: dataError
173+ anchors { left: parent.left; right: parent.right }
174+ height: units.gu(4)
175+ color: "orange"
176+ }
177+ }
178+ }
179+
180+ UT.UnityTestCase {
181+ id: testCase
182+ name: "Card"
183+
184+ when: windowShown
185+
186+ property Item header: findChild(card, "cardHeader")
187+ property Item art: findChild(card, "artShape")
188+ property Item artImage: findChild(card, "artImage")
189+ property Item summary: findChild(card, "summaryLabel")
190+
191+ function initTestCase() {
192+ verify(testCase.header !== undefined, "Couldn't find header object.");
193+ }
194+
195+ function cleanup() {
196+ selector.selectedIndex = -1;
197+ }
198+
199+
200+ function test_header_binding_data() {
201+ return [
202+ { tag: "Mascot", property: "mascot", value: Qt.resolvedUrl("artwork/avatar.png"), index: 0 },
203+ { tag: "Title", property: "title", value: "foo", index: 0 },
204+ { tag: "Subtitle", property: "subtitle", value: "bar", index: 0 },
205+ ];
206+ }
207+
208+ function test_header_binding(data) {
209+ selector.selectedIndex = data.index;
210+ tryCompare(testCase.header, data.property, data.value);
211+ }
212+
213+ function test_card_binding_data() {
214+ return [
215+ { tag: "Art", object: artImage, property: "source", value: Qt.resolvedUrl("artwork/music-player-design.png"), index: 0 },
216+ { tag: "Summary", object: summary, property: "text", field: "summary", index: 0 },
217+ { tag: "Fit", object: art, fill: Image.PreserveAspectFit, index: 4 },
218+ ];
219+ }
220+
221+ function test_card_binding(data) {
222+ selector.selectedIndex = data.index;
223+
224+ if (data.hasOwnProperty('value')) {
225+ tryCompare(data.object, data.property, data.value);
226+ }
227+
228+ if (data.hasOwnProperty('field')) {
229+ tryCompare(data.object, data.property, card.cardData[data.field]);
230+ }
231+ }
232+
233+ function test_card_size_data() {
234+ return [
235+ { tag: "Medium", width: units.gu(18.5), index: 0 },
236+ { tag: "Small", width: units.gu(12), size: "small", index: 0 },
237+ { tag: "Large", width: units.gu(38), size: "large", index: 0 },
238+ { tag: "Wide", width: units.gu(18.5), aspect: 0.5, index: 0 },
239+ { tag: "Horizontal", width: units.gu(38), index: 5 },
240+ ]
241+ }
242+
243+ function test_card_size(data) {
244+ selector.selectedIndex = data.index;
245+
246+ if (data.hasOwnProperty("size")) {
247+ card.template['card-size'] = data.size;
248+ card.templateChanged();
249+ }
250+
251+ if (data.hasOwnProperty("aspect")) {
252+ card.components['art']['aspect-ratio'] = data.aspect;
253+ card.componentsChanged();
254+ }
255+
256+ if (data.hasOwnProperty("width")) {
257+ tryCompare(card, "width", data.width);
258+ }
259+
260+ if (data.hasOwnProperty("height")) {
261+ tryCompare(card, "height", data.height);
262+ }
263+ }
264+
265+ function test_art_size_data() {
266+ return [
267+ { tag: "Medium", width: units.gu(18.5), fill: Image.PreserveAspectCrop, index: 0 },
268+ { tag: "Small", width: units.gu(12), index: 1 },
269+ { tag: "Large", width: units.gu(38), index: 2 },
270+ { tag: "Wide", height: units.gu(19), size: "large", index: 3 },
271+ { tag: "Fit", height: units.gu(38), size: "large", width: units.gu(19), index: 4 },
272+ { tag: "VerticalWidth", width: function() { return header.width }, index: 0 },
273+ { tag: "HorizontalHeight", height: function() { return header.height }, index: 5 },
274+ { tag: "HorizontalWidth", width: function() { return header.x }, index: 5 },
275+ ]
276+ }
277+
278+ function test_art_size(data) {
279+ selector.selectedIndex = data.index;
280+
281+ if (data.hasOwnProperty("size")) {
282+ card.template['card-size'] = data.size;
283+ card.templateChanged();
284+ }
285+
286+ if (data.hasOwnProperty("aspect")) {
287+ card.components['art']['aspect-ratio'] = data.aspect;
288+ card.componentsChanged();
289+ }
290+
291+ if (data.hasOwnProperty("width")) {
292+ if (typeof data.width === "function") {
293+ tryCompareFunction(function() { return art.width === data.width() }, true);
294+ } else tryCompare(art, "width", data.width);
295+ }
296+
297+ if (data.hasOwnProperty("height")) {
298+ if (typeof data.height === "function") {
299+ tryCompareFunction(function() { return art.height === data.height() }, true);
300+ } else tryCompare(art, "height", data.height);
301+ }
302+
303+ if (data.hasOwnProperty("fill")) {
304+ tryCompare(artImage, "fillMode", data.fill);
305+ }
306+ }
307+
308+ function test_art_layout_data() {
309+ return [
310+ { tag: "Vertical", left: function() { return 0 }, index: 0},
311+ { tag: "Horizontal", left: function() { return art.width }, index: 5 }
312+ ];
313+ }
314+
315+ function test_art_layout(data) {
316+ selector.selectedIndex = data.index;
317+
318+ tryCompare(testCase.header, "x", data.left());
319+ }
320+
321+ function test_header_layout_data() {
322+ return [
323+ { tag: "Vertical", top: function() { return art.y + art.height },
324+ left: function() { return art.x }, index: 0 },
325+ { tag: "Horizontal", top: function() { return art.y },
326+ left: function() { return art.x + art.width }, index: 5 }
327+ ]
328+ }
329+
330+ function test_header_layout(data) {
331+ selector.selectedIndex = data.index;
332+
333+ tryCompareFunction(function() { return testCase.header.y === data.top() }, true);
334+ tryCompareFunction(function() { return testCase.header.x === data.left() }, true);
335+ }
336+ }
337+}
tests/qmltests/Dash/tst_CardHeader.qmlnew file+138
@@ -0,0 +1,138 @@
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 QtTest 1.0
19+import Ubuntu.Components 0.1
20+import Unity.Test 0.1 as UT
21+import "../../../qml/Dash"
22+
23+
24+Rectangle {
25+ width: units.gu(40)
26+ height: units.gu(72)
27+ color: "lightgrey"
28+
29+ CardHeader {
30+ id: cardHeader
31+ anchors { left: parent.left; right: parent.right }
32+ }
33+
34+ Rectangle {
35+ anchors.fill: cardHeader
36+ color: "lightblue"
37+ opacity: 0.5
38+ }
39+
40+ UT.UnityTestCase {
41+ id: testCase
42+ name: "CardHeader"
43+
44+ when: windowShown
45+
46+ property Item mascot: findChild(cardHeader, "mascotShape")
47+ property Item titleLabel: findChild(cardHeader, "titleLabel")
48+ property Item subtitleLabel: findChild(cardHeader, "subtitleLabel")
49+ property Item prices: findChild(cardHeader, "prices")
50+ property Item oldPriceLabel: findChild(cardHeader, "oldPriceLabel")
51+ property Item outerRow: findChild(cardHeader, "outerRow")
52+ property Item column: findChild(cardHeader, "column")
53+
54+ function initTestCase() {
55+ verify(testCase.mascot !== undefined, "Couldn't find mascot object.");
56+ verify(testCase.titleLabel !== undefined, "Couldn't find titleLabel object.");
57+ verify(testCase.subtitleLabel !== undefined, "Couldn't find subtitleLabel object.");
58+ verify(testCase.prices !== undefined, "Couldn't find prices object.");
59+ verify(testCase.oldPriceLabel !== undefined, "Couldn't find oldPriceLabel object.");
60+ }
61+
62+ function cleanup() {
63+ cardHeader.mascot = "";
64+ cardHeader.title = "";
65+ cardHeader.subtitle = "";
66+ cardHeader.price = "";
67+ cardHeader.oldPrice = "";
68+ cardHeader.altPrice = "";
69+ }
70+
71+ function test_mascot_data() {
72+ return [
73+ { tag: "Empty", source: "", visible: false },
74+ { tag: "Invalid", source: "bad_path", visible: false },
75+ { tag: "Valid", source: Qt.resolvedUrl("artwork/avatar.png"), visible: true },
76+ ]
77+ }
78+
79+ function test_mascot(data) {
80+ cardHeader.mascot = data.source;
81+ tryCompare(testCase.mascot, "visible", data.visible);
82+ }
83+
84+ function test_labels_data() {
85+ return [
86+ { tag: "Empty", visible: false },
87+ { tag: "Title only", title: "Foo", visible: true },
88+ { tag: "Subtitle only", subtitle: "Bar", visible: false },
89+ { tag: "Both", title: "Foo", subtitle: "Bar", visible: true }
90+ ]
91+ }
92+
93+ function test_labels(data) {
94+ cardHeader.title = data.title !== undefined ? data.title : "";
95+ cardHeader.subtitle = data.subtitle !== undefined ? data.subtitle : "";
96+ tryCompare(cardHeader, "visible", data.visible);
97+ }
98+
99+ function test_prices_data() {
100+ return [
101+ { tag: "Main", main: "$1.25", visible: true },
102+ { tag: "Alt", alt: "€1.00", visible: false },
103+ { tag: "Old", old: "€2.00", visible: false },
104+ { tag: "Main and Alt", main: "$1.25", alt: "€1.00", visible: true },
105+ { tag: "Main and Old", main: "$1.25", old: "$2.00", visible: true, oldAlign: Text.AlignRight },
106+ { tag: "Alt and Old", alt: "€1.00", old: "$2.00", visible: false },
107+ { tag: "All", main: "$1.25", alt: "€1.00", old: "$2.00", visible: true, oldAlign: Text.AlignHCenter }
108+ ]
109+ }
110+
111+ function test_prices(data) {
112+ cardHeader.price = data.main !== undefined ? data.main : "";
113+ cardHeader.oldPrice = data.old !== undefined ? data.old : "";
114+ cardHeader.altPrice = data.alt !== undefined ? data.alt : "";
115+ tryCompare(cardHeader, "visible", data.visible);
116+ if (data.hasOwnProperty("oldAlign")) {
117+ compare(testCase.oldPriceLabel.horizontalAlignment, data.oldAlign, "Old price label is aligned wrong.")
118+ }
119+ }
120+
121+ function test_dimensions_data() {
122+ return [
123+ { tag: "Column width", object: column, width: cardHeader.width - testCase.outerRow.spacing * 2 },
124+ { tag: "Column width", object: column, width: cardHeader.width - mascot.width - testCase.outerRow.spacing * 3, mascot: "artwork/avatar.png" }
125+ ]
126+ }
127+
128+ function test_dimensions(data) {
129+ if (data.hasOwnProperty("mascot")) {
130+ cardHeader.mascot = data.mascot;
131+ }
132+
133+ if (data.hasOwnProperty("width")) {
134+ tryCompare(data.object, "width", data.width);
135+ }
136+ }
137+ }
138+}
tests/qmltests/Dash/tst_Dash.qml+6−8
@@ -25,8 +25,6 @@ Item {
25 width: units.gu(40)
25 width: units.gu(40)
26 height: units.gu(80)
26 height: units.gu(80)
27
27
28− property ListModel searchHistory : ListModel {}
29−
30 Dash {
28 Dash {
31 id: dash
29 id: dash
32 anchors.fill: parent
30 anchors.fill: parent
@@ -83,14 +81,14 @@ Item {
83 // wait for scopes to load
81 // wait for scopes to load
84 tryCompare(scopes, "loaded", true);
82 tryCompare(scopes, "loaded", true);
85
83
86− var dashbar = findChild(dash, "dashbar");
84+ var tabbar = findChild(dash, "tabbar");
87− verify(dashbar != undefined)
85+ verify(tabbar != undefined)
88 var dashContent = findChild(dash, "dashContent");
86 var dashContent = findChild(dash, "dashContent");
89 var current_index = dashContent.currentIndex;
87 var current_index = dashContent.currentIndex;
90
88
91 dash.setCurrentScope(data.tag, true /* animate */, false /* reset */);
89 dash.setCurrentScope(data.tag, true /* animate */, false /* reset */);
92 compare(dashContent.currentIndex, data.shouldBeVisible ? data.visualIndex : current_index);
90 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);
94 }
92 }
95
93
96 function test_show_scope_on_load_data() {
94 function test_show_scope_on_load_data() {
@@ -127,12 +125,12 @@ Item {
127 // wait for scopes to load
125 // wait for scopes to load
128 tryCompare(scopes, "loaded", true);
126 tryCompare(scopes, "loaded", true);
129
127
130− var dashbar = findChild(dash, "dashbar");
128+ var tabbar = findChild(dash, "tabbar");
131− verify(dashbar != undefined)
129+ verify(tabbar != undefined)
132 var dashContent = findChild(dash, "dashContent");
130 var dashContent = findChild(dash, "dashContent");
133 var current_index = dashContent.currentIndex;
131 var current_index = dashContent.currentIndex;
134
132
135− dashbar.itemSelected(data.visualIndex);
133+ tabbar.selectedIndex = data.visualIndex;
136 compare(dashContent.currentIndex, data.shouldBeVisible ? data.visualIndex : current_index);
134 compare(dashContent.currentIndex, data.shouldBeVisible ? data.visualIndex : current_index);
137 }
135 }
138 }
136 }
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 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19import "../../../qml/Dash"
19import "../../../qml/Dash"
20+import "../../../qml/Components"
20import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
21import Unity 0.1
22import Unity 0.1
22import Unity.Test 0.1 as UT
23import Unity.Test 0.1 as UT
24+import Utils 0.1
23
25
24Item {
26Item {
25 id: shell
27 id: shell
@@ -54,11 +56,13 @@ Item {
54 id: dashContent
56 id: dashContent
55 anchors.fill: parent
57 anchors.fill: parent
56
58
59+ model: SortFilterProxyModel {
57− model: scopesModel
60+ model: scopesModel
61+ }
58 scopes : scopesModel
62 scopes : scopesModel
59
63
60 scopeMapper : scopeDelegateMapper
64 scopeMapper : scopeDelegateMapper
61− searchHistory: ListModel {}
65+ searchHistory: SearchHistoryModel {}
62 }
66 }
63
67
64 ScopeDelegateMapper {
68 ScopeDelegateMapper {
@@ -109,9 +113,10 @@ Item {
109
113
110 UT.UnityTestCase {
114 UT.UnityTestCase {
111 name: "DashContent"
115 name: "DashContent"
112− when: windowShown
116+ when: scopesModel.loaded
113
117
114 function init() {
118 function init() {
119+ scopesModel.clear();
115 scopesModel.load();
120 scopesModel.load();
116 }
121 }
117
122
@@ -268,6 +273,65 @@ Item {
268 tryCompare(scopesModel.get(2), "isActive", data.active2);
273 tryCompare(scopesModel.get(2), "isActive", data.active2);
269 }
274 }
270
275
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+
271 function checkFlickMovingAndNotInteractive()
335 function checkFlickMovingAndNotInteractive()
272 {
336 {
273 var dashContentList = findChild(dashContent, "dashContentList");
337 var dashContentList = findChild(dashContent, "dashContentList");
@@ -282,7 +346,6 @@ Item {
282 return dashContentList.currentItem.moving && !dashContentList.interactive;
346 return dashContentList.currentItem.moving && !dashContentList.interactive;
283 }
347 }
284
348
285−
286 function test_hswipe_disabled_vswipe() {
349 function test_hswipe_disabled_vswipe() {
287 var dashContentList = findChild(dashContent, "dashContentList");
350 var dashContentList = findChild(dashContent, "dashContentList");
288
351
tests/qmltests/Dash/tst_GenericScopeView.qml+57−27
@@ -41,54 +41,83 @@ Item {
41 signal mainStageFocusedApplicationChanged()
41 signal mainStageFocusedApplicationChanged()
42 }
42 }
43
43
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+
44 GenericScopeView {
62 GenericScopeView {
45 id: genericScopeView
63 id: genericScopeView
46 anchors.fill: parent
64 anchors.fill: parent
47− searchHistory: SearchHistoryModel {}
65+ previewListView: previewListView
66+ openEffect: openEffect
67+ pageHeader: pageHeader
68+ tabBarHeight: pageHeader.implicitHeight
48
69
49 UT.UnityTestCase {
70 UT.UnityTestCase {
50 name: "GenericScopeView"
71 name: "GenericScopeView"
51 when: scopes.loaded
72 when: scopes.loaded
52
73
53− function test_isCurrent() {
74+ function test_isCurrent() {
54− var pageHeader = findChild(genericScopeView, "pageHeader");
55− var previewListView = findChild(genericScopeView, "previewListView");
56 genericScopeView.isCurrent = true
75 genericScopeView.isCurrent = true
57 pageHeader.searchQuery = "test"
76 pageHeader.searchQuery = "test"
58 previewListView.open = true
77 previewListView.open = true
59 genericScopeView.isCurrent = false
78 genericScopeView.isCurrent = false
60 tryCompare(pageHeader, "searchQuery", "")
79 tryCompare(pageHeader, "searchQuery", "")
61− tryCompare(genericScopeView, "previewShown", false);
80+ tryCompare(previewListView, "open", false);
62 }
81 }
63
82
64 function test_showDash() {
83 function test_showDash() {
65− var previewListView = findChild(genericScopeView, "previewListView");
66 previewListView.open = true;
84 previewListView.open = true;
85+ tryCompare(openEffect, "live", true);
67 scopes.get(0).showDash();
86 scopes.get(0).showDash();
68− tryCompare(genericScopeView, "previewShown", false);
87+ tryCompare(previewListView, "open", false);
88+ tryCompare(openEffect, "live", true);
69 }
89 }
70
90
71 function test_hideDash() {
91 function test_hideDash() {
72− var previewListView = findChild(genericScopeView, "previewListView");
73 previewListView.open = true;
92 previewListView.open = true;
74 scopes.get(0).hideDash();
93 scopes.get(0).hideDash();
75− tryCompare(genericScopeView, "previewShown", false);
94+ tryCompare(previewListView, "open", false);
76 }
95 }
77
96
78 function openPreview() {
97 function openPreview() {
79 var categoryListView = findChild(genericScopeView, "categoryListView");
98 var categoryListView = findChild(genericScopeView, "categoryListView");
80 categoryListView.positionAtBeginning();
99 categoryListView.positionAtBeginning();
81
100
101+ tryCompareFunction(function() {
102+ var tile = findChild(findChild(genericScopeView, "0"), "delegate0");
103+ return tile != undefined;
104+ },
105+ true);
82 var tile = findChild(findChild(genericScopeView, "0"), "delegate0");
106 var tile = findChild(findChild(genericScopeView, "0"), "delegate0");
83 mouseClick(tile, tile.width / 2, tile.height / 2);
107 mouseClick(tile, tile.width / 2, tile.height / 2);
84− var openEffect = findChild(genericScopeView, "openEffect");
108+ tryCompare(previewListView, "open", true);
85 tryCompare(openEffect, "gap", 1);
109 tryCompare(openEffect, "gap", 1);
86 }
110 }
87
111
88 function checkArrowPosition(index) {
112 function checkArrowPosition(index) {
113+ tryCompareFunction(function() {
114+ var tile = findChild(findChild(genericScopeView, "0"), "delegate" + index);
115+ return tile != undefined;
116+ },
117+ true);
89 var tile = findChild(findChild(genericScopeView, "0"), "delegate" + index);
118 var tile = findChild(findChild(genericScopeView, "0"), "delegate" + index);
90 var tileCenter = tile.x + tile.width/2;
119 var tileCenter = tile.x + tile.width/2;
91− var pointerArrow = findChild(genericScopeView, "pointerArrow");
120+ var pointerArrow = findChild(previewListView, "pointerArrow");
92 var pointerArrowCenter = pointerArrow.x + pointerArrow.width/2;
121 var pointerArrowCenter = pointerArrow.x + pointerArrow.width/2;
93 compare(pointerArrowCenter, tileCenter, "Pointer did not move to tile");
122 compare(pointerArrowCenter, tileCenter, "Pointer did not move to tile");
94 }
123 }
@@ -97,9 +126,7 @@ Item {
97 var closePreviewMouseArea = findChild(genericScopeView, "closePreviewMouseArea");
126 var closePreviewMouseArea = findChild(genericScopeView, "closePreviewMouseArea");
98 mouseClick(closePreviewMouseArea, closePreviewMouseArea.width / 2, closePreviewMouseArea.height / 2);
127 mouseClick(closePreviewMouseArea, closePreviewMouseArea.width / 2, closePreviewMouseArea.height / 2);
99
128
100− var previewListView = findChild(genericScopeView, "previewListView");
101 tryCompare(previewListView, "open", false);
129 tryCompare(previewListView, "open", false);
102− var openEffect = findChild(genericScopeView, "openEffect");
103 tryCompare(openEffect, "gap", 0);
130 tryCompare(openEffect, "gap", 0);
104
131
105 var categoryListView = findChild(genericScopeView, "categoryListView");
132 var categoryListView = findChild(genericScopeView, "categoryListView");
@@ -108,13 +135,12 @@ Item {
108 }
135 }
109
136
110 function test_previewOpenClose() {
137 function test_previewOpenClose() {
111− var previewListView = findChild(genericScopeView, "previewListView");
112 tryCompare(previewListView, "open", false);
138 tryCompare(previewListView, "open", false);
113
139
114 openPreview();
140 openPreview();
115
141
116 // check for it opening successfully
142 // check for it opening successfully
117− var currentPreviewItem = findChild(genericScopeView, "previewLoader0");
143+ var currentPreviewItem = findChild(previewListView, "previewLoader0");
118 tryCompareFunction(function() {
144 tryCompareFunction(function() {
119 var parts = currentPreviewItem.source.toString().split("/");
145 var parts = currentPreviewItem.source.toString().split("/");
120 var name = parts[parts.length - 1];
146 var name = parts[parts.length - 1];
@@ -138,26 +164,23 @@ Item {
138 var categoryListView = findChild(genericScopeView, "categoryListView");
164 var categoryListView = findChild(genericScopeView, "categoryListView");
139 categoryListView.positionAtBeginning();
165 categoryListView.positionAtBeginning();
140 waitForRendering(categoryListView);
166 waitForRendering(categoryListView);
141− categoryListView.flick(0, -units.gu(80));
167+ categoryListView.flick(0, -units.gu(60));
142 tryCompare(categoryListView.flicking, false);
168 tryCompare(categoryListView.flicking, false);
143
169
144 var tile = findChild(findChild(genericScopeView, "0"), "delegate0");
170 var tile = findChild(findChild(genericScopeView, "0"), "delegate0");
145 mouseClick(tile, tile.width / 2, tile.height - 1);
171 mouseClick(tile, tile.width / 2, tile.height - 1);
146− var openEffect = findChild(genericScopeView, "openEffect");
147 tryCompare(openEffect, "gap", 1);
172 tryCompare(openEffect, "gap", 1);
148
173
149− var pageHeader = findChild(genericScopeView, "pageHeader");
150 verify(openEffect.positionPx >= pageHeader.height + categoryListView.stickyHeaderHeight);
174 verify(openEffect.positionPx >= pageHeader.height + categoryListView.stickyHeaderHeight);
151 }
175 }
152
176
153 function test_previewCycle() {
177 function test_previewCycle() {
154− var previewListView = findChild(genericScopeView, "previewListView");
155 tryCompare(previewListView, "open", false);
178 tryCompare(previewListView, "open", false);
156
179
157 openPreview();
180 openPreview();
158
181
159 // wait for it to be loaded
182 // wait for it to be loaded
160− var currentPreviewItem = findChild(genericScopeView, "previewLoader0");
183+ var currentPreviewItem = findChild(previewListView, "previewLoader0");
161 tryCompareFunction(function() {
184 tryCompareFunction(function() {
162 var parts = currentPreviewItem.source.toString().split("/");
185 var parts = currentPreviewItem.source.toString().split("/");
163 var name = parts[parts.length - 1];
186 var name = parts[parts.length - 1];
@@ -179,7 +202,7 @@ Item {
179 previewListView.height / 2);
202 previewListView.height / 2);
180
203
181 // wait for it to be loaded
204 // wait for it to be loaded
182− var nextPreviewItem = findChild(genericScopeView, "previewLoader" + i);
205+ var nextPreviewItem = findChild(previewListView, "previewLoader" + i);
183 tryCompareFunction(function() {
206 tryCompareFunction(function() {
184 var parts = nextPreviewItem.source.toString().split("/");
207 var parts = nextPreviewItem.source.toString().split("/");
185 var name = parts[parts.length - 1];
208 var name = parts[parts.length - 1];
@@ -206,11 +229,12 @@ Item {
206
229
207 function test_show_spinner() {
230 function test_show_spinner() {
208 openPreview();
231 openPreview();
209− var previewListView = findChild(genericScopeView, "previewListView");
232+ var previewLoader = findChild(previewListView, "previewLoader0");
210− var previewLoader = findChild(genericScopeView, "previewLoader0");
233+ tryCompare(previewLoader, "progress", 1.0);
234+ tryCompareFunction(function() { return previewLoader.item != undefined; }, true);
211
235
212 previewLoader.item.showProcessingAction = true;
236 previewLoader.item.showProcessingAction = true;
213− var waitingForAction = findChild(genericScopeView, "waitingForActionMouseArea");
237+ var waitingForAction = findChild(previewListView, "waitingForActionMouseArea");
214 tryCompare(waitingForAction, "enabled", true);
238 tryCompare(waitingForAction, "enabled", true);
215 previewLoader.closePreviewSpinner();
239 previewLoader.closePreviewSpinner();
216 tryCompare(waitingForAction, "enabled", false);
240 tryCompare(waitingForAction, "enabled", false);
@@ -252,12 +276,10 @@ Item {
252 tryCompareFunction(function() { return findChild(genericScopeView, "carouselDelegate") != undefined; }, true);
276 tryCompareFunction(function() { return findChild(genericScopeView, "carouselDelegate") != undefined; }, true);
253 var tile = findChild(genericScopeView, "carouselDelegate");
277 var tile = findChild(genericScopeView, "carouselDelegate");
254 mouseClick(tile, tile.width / 2, tile.height / 2);
278 mouseClick(tile, tile.width / 2, tile.height / 2);
255− var openEffect = findChild(genericScopeView, "openEffect");
256 tryCompare(openEffect, "gap", 1);
279 tryCompare(openEffect, "gap", 1);
257
280
258 // check for it opening successfully
281 // check for it opening successfully
259− var previewListView = findChild(genericScopeView, "previewListView");
260− var currentPreviewItem = findChild(genericScopeView, "previewLoader0");
282+ var currentPreviewItem = findChild(previewListView, "previewLoader0");
261 tryCompareFunction(function() {
283 tryCompareFunction(function() {
262 var parts = currentPreviewItem.source.toString().split("/");
284 var parts = currentPreviewItem.source.toString().split("/");
263 var name = parts[parts.length - 1];
285 var name = parts[parts.length - 1];
@@ -299,6 +321,14 @@ Item {
299
321
300 categoryListView.positionAtBeginning();
322 categoryListView.positionAtBeginning();
301
323
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+
302 var header0 = findChild(genericScopeView, "dashSectionHeader0")
332 var header0 = findChild(genericScopeView, "dashSectionHeader0")
303 var category0 = findChild(genericScopeView, "dashCategory0")
333 var category0 = findChild(genericScopeView, "dashCategory0")
304 mouseClick(header0, header0.width / 2, header0.height / 2);
334 mouseClick(header0, header0.width / 2, header0.height / 2);
tests/qmltests/Greeter/tst_Lockscreen.qml+1−1
@@ -134,7 +134,7 @@ Rectangle {
134 if (data.pinPadAvailable) {
134 if (data.pinPadAvailable) {
135 compare(findChild(lockscreen, "pinPadButton8").text, "8", "Could not find number 8 on PinPad")
135 compare(findChild(lockscreen, "pinPadButton8").text, "8", "Could not find number 8 on PinPad")
136 } else {
136 } else {
137− compare(findChild(lockscreen, "pinPadButton8"), undefined, "Could find number 8 on PinPad even though it should be only OSK")
137+ compare(findChild(lockscreen, "pinPadButton8"), null, "Could find number 8 on PinPad even though it should be only OSK")
138 }
138 }
139 }
139 }
140
140
tests/qmltests/Panel/tst_IndicatorItem.qml−11
@@ -41,17 +41,6 @@ Rectangle {
41 tryCompareFunction(function(){return indicatorItem.opacity < 1.0}, true);
41 tryCompareFunction(function(){return indicatorItem.opacity < 1.0}, true);
42 }
42 }
43
43
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−
55 function test_empty() {
44 function test_empty() {
56 compare(indicatorItem.indicatorVisible, false, "IndicatorItem should not be visible.");
45 compare(indicatorItem.indicatorVisible, false, "IndicatorItem should not be visible.");
57 indicatorItem.widgetSource = "qrc:/tests/indciators/qml/fake_menu_widget1.qml";
46 indicatorItem.widgetSource = "qrc:/tests/indciators/qml/fake_menu_widget1.qml";
tests/qmltests/Panel/tst_IndicatorRow.qml+18−4
@@ -34,6 +34,7 @@ Item {
34 {
34 {
35 indicatorRow.state = "initial";
35 indicatorRow.state = "initial";
36 indicatorRow.currentItem = null;
36 indicatorRow.currentItem = null;
37+ indicatorRow.unitProgress = 0.0;
37 }
38 }
38
39
39 PanelBackground {
40 PanelBackground {
@@ -83,14 +84,13 @@ Item {
83 function test_current_item_commit() {
84 function test_current_item_commit() {
84 init_test();
85 init_test();
85
86
86− indicatorRow.setCurrentItem(1);
87+ indicatorRow.setCurrentItem(2);
87 indicatorRow.state = "commit";
88 indicatorRow.state = "commit";
88 tryCompare(get_indicator_item_at(0), "opacity", 0.0);
89 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(1), "opacity", 0.0);
90− tryCompare(get_indicator_item_at(2), "opacity", 0.0);
91+ tryCompare(get_indicator_item_at(2), "opacity", 1.0);
91 tryCompare(get_indicator_item_at(3), "opacity", 0.0);
92 tryCompare(get_indicator_item_at(3), "opacity", 0.0);
92 tryCompare(get_indicator_item_at(4), "opacity", 0.0);
93 tryCompare(get_indicator_item_at(4), "opacity", 0.0);
93−
94 }
94 }
95 }
95 }
96
96
@@ -166,6 +166,20 @@ Item {
166 compare(get_indicator_item_at(3).highlighted, false, "Other indicators should not highlight when in locked state");
166 compare(get_indicator_item_at(3).highlighted, false, "Other indicators should not highlight when in locked state");
167 compare(get_indicator_item_at(4).highlighted, false, "Other indicators should not highlight when in locked state");
167 compare(get_indicator_item_at(4).highlighted, false, "Other indicators should not highlight when in locked state");
168 }
168 }
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+ }
169 }
183 }
170
184
171 UT.UnityTestCase {
185 UT.UnityTestCase {
tests/qmltests/Panel/tst_Indicators.qml+1−1
@@ -175,7 +175,7 @@ Item {
175 tryCompare(indicatorItem, "visible", data.visible);
175 tryCompare(indicatorItem, "visible", data.visible);
176
176
177 var indicatorTab = findChild(indicatorTabs, data.name)
177 var indicatorTab = findChild(indicatorTabs, data.name)
178− tryCompareFunction(function() { return indicatorTab !== undefined }, data.visible);
178+ tryCompareFunction(function() { return indicatorTab !== null }, data.visible);
179 }
179 }
180 }
180 }
181}
181}
tests/utils/modules/Unity/Test/UnityTestCase.qml+2−2
@@ -83,7 +83,7 @@ TestCase {
83 }
83 }
84 childs.splice(0, 1);
84 childs.splice(0, 1);
85 }
85 }
86− return undefined;
86+ return null;
87 }
87 }
88
88
89 // Find an object with the given name in the children tree of "obj"
89 // Find an object with the given name in the children tree of "obj"
@@ -103,7 +103,7 @@ TestCase {
103 }
103 }
104 childs.splice(0, 1);
104 childs.splice(0, 1);
105 }
105 }
106− return undefined;
106+ return null;
107 }
107 }
108
108
109 // Type a full string instead of keyClick letter by letter
109 // Type a full string instead of keyClick letter by letter
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−}