on xenial help sign in
Visual changes for indicator RTM polishing Fixes: 1329289, 1349921, 1350308, 1350952, 1354506, 1373319

Approved by: Andrea Cimitan

commit73484f407b3f52516d6e6484aa97c650e481c090 authorNick Dedekind <nick.dedekind@canonical.com> dateFri 26 Sep 2014, 19:12 (12 years ago) parent2abe8314e94927
29 files changed+995−699
debian/control+2−2 plugins/Unity/Indicators/CMakeLists.txt−9 plugins/Unity/Indicators/plugin.cpp−2 plugins/Unity/Indicators/resources/artwork/messaging/default_app.svg−42 plugins/Unity/Indicators/resources/artwork/messaging/default_contact.png plugins/Unity/Indicators/resources/indicators.qrc−6 plugins/Utils/CMakeLists.txt+1 plugins/Utils/plugin.cpp+2 plugins/Utils/relativetimeformatter.cpp+268 plugins/Utils/relativetimeformatter.h+34 plugins/Utils/timeformatter.cpp+6−1 plugins/Utils/timeformatter.h+3−1 po/unity8.pot+29−25 qml/Components/Header.qml+27 qml/Panel/Indicators.qml+2−13 qml/Panel/Indicators/DefaultIndicatorPage.qml+8−49 qml/Panel/Indicators/IndicatorDelegate.qml+2−10 qml/Panel/Indicators/MenuItemFactory.qml+158−47 qml/Panel/Indicators/MessageMenuItemFactory.qml+156−171 qml/Panel/Indicators/RemoveBackground.qml−55 qml/Panel/Indicators/client/IndicatorsClient.qml+4 qml/Panel/MenuContent.qml+89−113 tests/mocks/Unity/Indicators/FakeMenuPage.qml−10 tests/plugins/Utils/CMakeLists.txt+1−1 tests/plugins/Utils/timeformattertest.cpp+104−7 tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+32−16 tests/qmltests/Panel/Indicators/tst_MessageMenuItemFactory.qml+43−53 tests/qmltests/Panel/tst_Indicators.qml+10−10 tests/qmltests/Panel/tst_MenuContent.qml+14−56
debian/control+2−2
@@ -45,7 +45,7 @@ Build-Depends: cmake,
4545 qtdeclarative5-dev-tools,
4646 qtdeclarative5-private-dev (>= 5.2.1),
4747 qtdeclarative5-qtmultimedia-plugin,
48− qtdeclarative5-ubuntu-settings-components (>= 0.3),
48+ qtdeclarative5-ubuntu-settings-components (>= 0.4),
4949 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.49) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 0.1.49),
5050 ttf-ubuntu-font-family,
5151Standards-Version: 3.9.4
@@ -108,7 +108,7 @@ Description: Unity 8 shell
108108Package: unity8-common
109109Architecture: all
110110Depends: qml-module-qtquick-layouts,
111− qtdeclarative5-ubuntu-settings-components (>= 0.3),
111+ qtdeclarative5-ubuntu-settings-components (>= 0.4),
112112 qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
113113 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.49) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 0.1.49),
114114 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
plugins/Unity/Indicators/CMakeLists.txt−9
@@ -30,16 +30,7 @@ set(IndicatorsQML_SOURCES
3030)
3131add_definitions(-DUNITYINDICATORS_LIBRARY)
3232
33−set(IndicatorsQML_RESOURCES
34− resources/indicators.qrc
35−)
36−
37−qt5_add_resources(IndicatorsQML_RESOURCES_RCC
38− ${IndicatorsQML_RESOURCES}
39−)
40−
4133add_library(IndicatorsQml SHARED
42− ${IndicatorsQML_RESOURCES_RCC}
4334 ${IndicatorsQML_SOURCES}
4435)
4536
plugins/Unity/Indicators/plugin.cpp−2
@@ -42,8 +42,6 @@ static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEng
4242
4343void Indicators2Plugin::registerTypes(const char *uri)
4444{
45− Q_INIT_RESOURCE(indicators);
46−
4745 qmlRegisterType<IndicatorsManager>(uri, 0, 1, "IndicatorsManager");
4846 qmlRegisterType<IndicatorsModel>(uri, 0, 1, "IndicatorsModel");
4947 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
plugins/Unity/Indicators/resources/artwork/messaging/default_app.svgdeleted−42
@@ -1,42 +0,0 @@
1−<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2−<!-- Created with Inkscape (http://www.inkscape.org/) -->
3−<svg id="svg15137" xmlns="http://www.w3.org/2000/svg" height="48" width="48" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
4− <defs id="defs15139">
5− <radialGradient id="radialGradient2612" gradientUnits="userSpaceOnUse" cy="4.625" cx="62.625" gradientTransform="matrix(1 0 0 .34118 0 3.0471)" r="10.625">
6− <stop id="stop8840" offset="0"/>
7− <stop id="stop8842" style="stop-opacity:0" offset="1"/>
8− </radialGradient>
9− <linearGradient id="linearGradient15133" y2=".24326" gradientUnits="userSpaceOnUse" x2="62.2" gradientTransform="matrix(2.1499 0 0 2.1499 -110.12 31.935)" y1="-12.489" x1="62.2">
10− <stop id="stop4875" style="stop-color:#fff" offset="0"/>
11− <stop id="stop4877" style="stop-color:#fff;stop-opacity:0" offset="1"/>
12− </linearGradient>
13− <radialGradient id="radialGradient15127" gradientUnits="userSpaceOnUse" cy="-8.7256" cx="62.2" gradientTransform="matrix(2.4129 0 0 2.413 -126.48 34.231)" r="9.7553">
14− <stop id="stop8649" style="stop-color:#8fb1dc" offset="0"/>
15− <stop id="stop8651" style="stop-color:#3465a4" offset="1"/>
16− </radialGradient>
17− <radialGradient id="radialGradient2418" gradientUnits="userSpaceOnUse" cy="24.149" cx="17.814" gradientTransform="matrix(-2.644 0 2.9365e-8 2.5344 78.725 -37.986)" r="9.125">
18− <stop id="stop4847" style="stop-color:#fff" offset="0"/>
19− <stop id="stop4849" style="stop-color:#b6b6b6" offset="1"/>
20− </radialGradient>
21− <radialGradient id="radialGradient5145" gradientUnits="userSpaceOnUse" cy="13.067" cx="23.86" gradientTransform="matrix(-1.5425 0 0 -.84553 60.718 23.829)" r="18.098">
22− <stop id="stop14112" style="stop-color:#557695" offset="0"/>
23− <stop id="stop14114" style="stop-color:#15395c" offset="1"/>
24− </radialGradient>
25− <radialGradient id="radialGradient5147" gradientUnits="userSpaceOnUse" cy="-3.442" cx="62.225" gradientTransform="matrix(1.1035 0 0 1.0265 -4.1555 -1.8158)" r="10.081">
26− <stop id="stop14118" style="stop-color:#9e9e9e" offset="0"/>
27− <stop id="stop14120" style="stop-color:#9e9e9e;stop-opacity:0" offset="1"/>
28− </radialGradient>
29− </defs>
30− <g id="layer1">
31− <path id="path8836" style="opacity:.4;fill-rule:evenodd;fill:url(#radialGradient2612)" d="m73.25 4.625a10.625 3.625 0 1 1 -21.25 0 10.625 3.625 0 1 1 21.25 0z" transform="matrix(2.1647 0 0 2.5637 -111.56 26.85)"/>
32− <g id="g15122" transform="translate(.79042 -.78563)">
33− <path id="path6495" style="stroke:#204a87;fill:url(#radialGradient15127)" d="m43.505 24.387c0 10.99-8.9091 19.899-19.897 19.899-10.989 0-19.898-8.9091-19.898-19.899 0-10.989 8.9086-19.897 19.898-19.897 10.988 0 19.897 8.9081 19.897 19.897z"/>
34− <path id="path6534" style="stroke:url(#radialGradient5147);stroke-width:1.1035;fill:url(#radialGradient5145)" d="m23.726 5.3141c-1.5283 0.12067-3.0328 0.36544-4.4919 0.84567-1.23 0.48843 0.88689-0.42988-0.37486-0.60468-0.75942-0.12994-1.0776 0.65251-1.7561 0.67046-0.7741 0.023841-1.3048 0.71572-2.0319 0.79898-0.26187 1.4432-0.59924-0.56894-0.83949 0.13173 0.19047 0.7212-0.07796 1.1928-0.63582 1.6119-0.60156 0.68249-0.03584 1.2934 0.54212 0.69395 0.2775-0.83446 1.1825-1.7618 2.0655-1.2049 0.8837 0.48448 1.7582 1.6525 2.4938 0.40935-0.75488-0.090735-1.0462-0.73637-1.4079-1.2096-0.40023 0.031761-2.0794-0.32019-1.0059-0.24024 0.5284-0.17249 1.549-0.68558 1.9239-0.66537-0.94013 0.49026 0.35015 0.81268 0.73202 1.2035 0.68671 0.20357 0.99776 1.0757 0.37414 1.527-0.80181 0.4162 0.05573 1.1017 0.221 1.6748 0.12828 0.99827 0.99623 0.43282 1.2716-0.18046 0.39803-1.1153 1.6733-1.0767 2.5958-1.5522 1.008-0.51754 1.0248-0.53575 1.5034 0.20195 0.36764 0.21151 1.5947-0.55825 0.78901-0.929-0.68957 0.03713-1.142 0.46029-0.56364-0.60801 0.13915-0.57404 0.6791-1.3235 0.58092-1.7937-0.662-0.2603-1.324-0.5207-1.986-0.7811zm-9.3732 3.6945c0.35612 0.3643 1.5564 1.0234 1.4368 0.11255-0.14419-0.61744-1.0418-0.62308-1.4368-0.11255zm18.165-0.44471c-0.82412 0.16922-1.6412 0.35467-2.292 0.92363 0.19457 0.7853-0.8341 0.89786-1.2851 1.3281-0.12036 0.25242-0.02467 1.8403 0.51105 1.1573 0.58162-0.77246 0.83368 0.40868 1.4478 0.52687 0.4451-0.14324 0.5002-1.1438 0.33275-1.6043 0.28525-0.46984 0.75058-1.1195 1.3539-1.0659-0.37419 0.57099-0.80163 2.0315 0.3911 1.4345 0.34518-0.006-0.93958 0.75452-1.0166 1.273-0.48258 0.51284-0.9934 0.90587-1.6905 0.50847-0.33355-0.28162-0.50305-1.0992-0.71414-0.17566 0.43341 0.73252-1.089 0.04718-1.0514 1.0356-0.31972 0.61514-0.89623 0.8671-1.563 0.91149 0.24198 0.19141 1.1924 1.3314 0.24953 1.1701-0.60352 0.06628-1.4857 0.40701-1.2414 1.1653-0.27486 0.9444 0.4249 0.70654 0.85522 1.2977 0.70116-0.19916 1.1488-0.62908 1.2634-1.3621 0.65618-0.19273 0.73557-0.93195 1.456-0.91702 0.7182-0.40381 0.88559 0.73738 1.5587 0.82181 0.07058 0.4087-0.33464 1.281 0.45659 0.4693 0.76441-0.55387-0.79512-1.2716-0.30963-1.4066 0.69099 0.22071 0.94006 0.87671 1.051 1.519 0.56306 0.9634 0.70185 0.44404 0.74672-0.40362 0.87723-0.39439 1.1473 1.641 2.3881 1.1397 0.92678 0.19174 0.59949 0.8304-0.01904 1.2203-0.50066 0.47112-1.3382 0.17338-1.9265-0.01558-0.56472-0.27756-1.3196-0.24813-1.6214 0.38347-0.46529-0.21764-0.94642-0.91135-1.4178-1.3118-1.1647-0.5139-2.4314-0.07571-3.5919 0.23946-0.99563 1.1632-2.1949 2.219-2.6236 3.7359-0.62778 0.63853 0.54336 1.0082-0.24392 1.556-0.6428 0.51279 0.10805 1.1629 0.2458 1.7527 0.60445 0.74395 1.6306 3.0723 2.5708 2.1452 0.52705-0.80245 0.81963 0.61582 1.4399-0.30194 0.4952-0.39949 0.9695 0.45802 1.6897 0.14331 0.69372 0.4284 0.56095 1.0433 0.27484 1.6928 0.15583 0.54087 0.93098 0.9344 0.62978 1.6677-0.27366 0.68309 0.58312 1.201 0.41038 1.7987-0.5748 1.1512-0.43928 2.5747 0.09386 3.7157 0.42668 1.0325 0.67699 2.1484 0.64366 3.2692 0.74277 0.2222 1.1367-0.72912 1.8776-0.51313 0.72333 0.09324 0.99191-0.82974 1.5008-1.2158 0.56704-0.46747-0.07223-1.1873 0.82794-1.5185 0.87331-0.42523-1.106-1.0213 0.0152-1.4024 0.46515-0.51761 1.4132-0.69776 1.6008-1.3735-0.33834-1.0398-0.21797-2.1801-0.16738-3.2436 0.32552-0.84167 0.72975-1.6312 1.4262-2.2272 0.28082-0.92669 0.94924-1.7777 0.88942-2.7709-1.6476 0.50399-1.1482-0.21491-0.22962-0.87318 0.63508-0.42199 1.4051-0.84564 1.7006-1.5617 0.14435-0.81048-0.45227-2.1297-1.0079-0.90496-0.24511-0.4707-1.1542-0.71744-1.113-1.3307-0.17659-1.2625 0.34462-0.14427 0.86051 0.10789 1.1146-0.60521 1.6818 1.7766 2.3893 0.35503-0.618-4.471-3.052-8.528-6.34-11.567-0.571-1.0949-0.921 0.75-1.501 0.68-0.464-0.485-1.33-1.1394-0.122-0.914 1.034-0.3028 0.333-0.7027-0.425-0.6842-0.574-0.0697-1.101-0.2706-1.635-0.4789zm-20.56 0.4447c-0.275 1.3534-2.0109 1.1954-2.6162 2.5304-2.9783 3.153-4.8166 7.302-5.32 11.598 0.26584 1.0071 1.1105 1.4908 1.9601 1.9373 0.72184 0.41932 1.7801 0.2342 2.1848 1.1124 0.45187 0.36983 0.93948 1.0997 1.5856 0.87722 0.93664-0.09099 0.36019 0.85823 0.19973 1.3392-0.059277 0.607-0.80778 0.99372-0.52016 1.5778-0.24136 0.66166-0.43304 1.3177 0.10262 1.8906 0.45551 0.83988 0.82632 1.7478 1.6708 2.2751 0.59217 0.49728 1.2625 1.1553 1.0304 1.9974-0.12338 0.68167 0.04306 1.3099 0.3725 1.908-0.11816 0.67146-0.36073 1.3584-0.4105 2.0183 0.87949 0.62206 2.6705 2.0606 2.8031 0.49489 0.50176-0.45161 0.5321-1.1784 1.3743-1.1804 0.3909-0.47777 0.52549-1.2283 0.81706-1.8117-0.42572-0.5384 0.16631-1.3697 0.8046-1.3477 0.81594 0.06128 0.78593-0.89448 1.1111-1.4232-0.0073-0.77224-0.38892-1.6471 0.30542-2.227 0.52242-0.47344 0.46827-1.1675 0.5498-1.8097-1.171-0.7244-2.712-1.0911-3.6386-2.0687-0.18364-0.76504-0.7598-1.0229-1.4925-1.0784-0.30936-0.43256-0.25697-1.4628-0.79753-1.5131-0.69628 0.13369-1.3857 0.24089-1.8917-0.3644-0.573-0.776-1.069 0.2-1.593 0.546-0.492 0.856-1.8683-0.043-1.7038-1.007 0.4839-1.334-1.6425 0.148-0.7699-1.425 0.4151-1.715-0.7067-0.396-1.2952-0.011-0.71822 0.0081-0.98953-0.70747-0.86146-1.3625-0.091117-0.78733 0.48283-1.1877 1.0669-1.5822 0.78161 0.12038 0.97989-0.56085 1.5654-0.4722 0.73603-0.03767 1.0049 0.66782 1.4232 1.1357 0.17514-0.66797-0.18345-1.6152 0.55606-1.9879 0.56414-0.42781 1.137-0.84927 1.2721-1.5904 0.13304-0.77598 1.2893-0.46761 1.4837-1.2526 0.26877-0.8206 1.5114-0.15823 1.7899-0.96804 0.8733-0.82086-0.27242-0.57178-0.89854-0.59806-1.2353 0.12695 0.1228-0.32763 0.58598-0.58155 0.68608 0.09073 1.3195-0.13384 1.9499-0.37629 0.08445 0.85843 0.24597 1.3112 0.68417 0.30788-0.85421-0.20211-0.11618-1.0402-0.95973-1.2614-0.66764 0.04697 0.0066-1.4189-0.81571-1.2577-1.0697 0.70782-0.11401-0.50251-0.9854-0.66347-0.68736-0.74222-0.85436 0.47657-1.2276 0.9183 0.24207 0.84702-0.79095 0.66525-0.97976 1.3606-0.41814 1.2534-0.24143-0.08931-0.74055-0.41035-0.61653-0.24732-1.3592-0.3784-1.3637-1.1899 0.82658-0.60512 2.0667-0.96189 2.5994-1.9148 0.3095-0.74008-0.26215-1.1445-0.96618-1.0889l-0.000005-2e-7zm14.675 1.642c-0.12491 0.65746 0.5475 1.009 0.30788 1.7104-0.21487 0.76842-0.27728 1.3789 0.68289 0.93046 0.99668-0.51183 0.36052-0.90148-0.03867-1.5728-0.27187-0.44613-0.24574-1.275-0.9521-1.0681zm-0.75259 1.0947c-0.79414-0.24012-0.61745 1.4493-0.12164 1.0118 0.7276-0.17916 0.23703-0.5252 0.12164-1.0118zm10.023 8.9969c0.95184 0.84444 1.277 2.0946 1.8858 3.1596 0.90428 0.39972 0.0415 1.0795 0.44521 1.5712 0.84897 0.92228-1.1752 0.24516-0.90606-0.67899-0.1971-1.2139-1.0559-2.044-1.4678-3.1045-0.04294-0.3526-0.38552-0.71788 0.04284-0.94732z"/>
35− <path id="path8655" style="opacity:.4;stroke:url(#linearGradient15133);fill:none" d="m42.409 24.387c0 10.384-8.4184 18.803-18.801 18.803-10.384 0-18.802-8.4184-18.802-18.803 0-10.384 8.418-18.801 18.802-18.801 10.383 0 18.801 8.4175 18.801 18.801z"/>
36− </g>
37− <g id="g2414" transform="matrix(.99991 -.013701 .013701 .99991 -.28870 .53585)">
38− <path id="path3970" style="stroke-linejoin:round;fill-rule:evenodd;stroke:#666;fill:url(#radialGradient2418)" d="m30.5 20.937 17 16.5-7.75 0.25s3.25 6.75 3.25 6.75c1 3-3.5 4.125-4.25 1.875l-3-6.75-5.5 5.875 0.25-24.5z"/>
39− <path id="path4853" style="opacity:.4;stroke:#fff;fill:none" d="m31.657 23.379 13.476 13.186-6.9219 0.27746l3.8721 7.7566c0.40273 1.6501-2.0283 2.4126-2.5071 1.1529l-3.6831-7.845-4.4247 4.7083 0.18907-19.236z"/>
40− </g>
41− </g>
42−</svg>
plugins/Unity/Indicators/resources/artwork/messaging/default_contact.pngdeleted

Binary file not shown.

plugins/Unity/Indicators/resources/indicators.qrcdeleted−6
@@ -1,6 +0,0 @@
1−<RCC>
2− <qresource prefix="/indicators">
3− <file>artwork/messaging/default_contact.png</file>
4− <file>artwork/messaging/default_app.svg</file>
5− </qresource>
6−</RCC>
plugins/Utils/CMakeLists.txt+1
@@ -13,6 +13,7 @@ include_directories(
1313set(QMLPLUGIN_SRC
1414 qlimitproxymodelqml.cpp
1515 qsortfilterproxymodelqml.cpp
16+ relativetimeformatter.cpp
1617 timeformatter.cpp
1718 unitymenumodelpaths.cpp
1819 windowkeysfilter.cpp
plugins/Utils/plugin.cpp+2
@@ -28,6 +28,7 @@
2828// local
2929#include "qlimitproxymodelqml.h"
3030#include "qsortfilterproxymodelqml.h"
31+#include "relativetimeformatter.h"
3132#include "timeformatter.h"
3233#include "unitymenumodelpaths.h"
3334#include "windowkeysfilter.h"
@@ -44,6 +45,7 @@ void UtilsPlugin::registerTypes(const char *uri)
4445 qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
4546 qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
4647 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
48+ qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
4749}
4850
4951void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
plugins/Utils/relativetimeformatter.cppnew file+268
@@ -0,0 +1,268 @@
1+/*
2+ * Copyright 2014 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+// Local
19+#include "relativetimeformatter.h"
20+
21+// Qt
22+#include <QDateTime>
23+
24+// Other
25+#include <glib.h>
26+#include <glib/gi18n.h>
27+#include <locale.h>
28+#include <langinfo.h>
29+#include <string.h>
30+
31+RelativeTimeFormatter::RelativeTimeFormatter(QObject *parent)
32+ : GDateTimeFormatter(parent)
33+{
34+}
35+
36+ /* Check the system locale setting to see if the format is 24-hour
37+ time or 12-hour time */
38+gboolean
39+is_locale_12h(void)
40+{
41+ int i;
42+ static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", nullptr};
43+ const char* t_fmt = nl_langinfo(T_FMT);
44+
45+ for (i=0; formats_24h[i]!=nullptr; i++)
46+ if (strstr(t_fmt, formats_24h[i]) != nullptr)
47+ return FALSE;
48+
49+ return TRUE;
50+}
51+
52+typedef enum
53+{
54+ DATE_PROXIMITY_YESTERDAY,
55+ DATE_PROXIMITY_TODAY,
56+ DATE_PROXIMITY_TOMORROW,
57+ DATE_PROXIMITY_LAST_WEEK,
58+ DATE_PROXIMITY_NEXT_WEEK,
59+ DATE_PROXIMITY_FAR
60+} date_proximity_t;
61+
62+static date_proximity_t
63+getDateProximity(GDateTime* now, GDateTime* time)
64+{
65+ date_proximity_t prox = DATE_PROXIMITY_FAR;
66+ gint now_year, now_month, now_day;
67+ gint time_year, time_month, time_day;
68+
69+ // does it happen today?
70+ g_date_time_get_ymd(now, &now_year, &now_month, &now_day);
71+ g_date_time_get_ymd(time, &time_year, &time_month, &time_day);
72+ if ((now_year == time_year) && (now_month == time_month) && (now_day == time_day)) {
73+ return DATE_PROXIMITY_TODAY;
74+ }
75+
76+ // did it happen yesterday?
77+ GDateTime* tomorrow = g_date_time_add_days(now, -1);
78+ gint tom_year, tom_month, tom_day;
79+ g_date_time_get_ymd(tomorrow, &tom_year, &tom_month, &tom_day);
80+ g_date_time_unref(tomorrow);
81+ if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day)) {
82+ return DATE_PROXIMITY_YESTERDAY;
83+ }
84+
85+ // does it happen tomorrow?
86+ if (prox == DATE_PROXIMITY_FAR)
87+ {
88+ GDateTime* tomorrow = g_date_time_add_days(now, 1);
89+
90+ gint tom_year, tom_month, tom_day;
91+ g_date_time_get_ymd(tomorrow, &tom_year, &tom_month, &tom_day);
92+ g_date_time_unref(tomorrow);
93+ if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day)) {
94+ return DATE_PROXIMITY_TOMORROW;
95+ }
96+ }
97+
98+ // does it happen this week?
99+ if (prox == DATE_PROXIMITY_FAR) {
100+ if (g_date_time_compare(time, now) < 0) {
101+ GDateTime* last_week = g_date_time_add_days(now, -6);
102+ GDateTime* last_week_bound = g_date_time_new_local(g_date_time_get_year(last_week),
103+ g_date_time_get_month(last_week),
104+ g_date_time_get_day_of_month(last_week),
105+ 0, 0, 0);
106+ if (g_date_time_compare(time, last_week_bound) >= 0)
107+ prox = DATE_PROXIMITY_LAST_WEEK;
108+
109+ g_date_time_unref(last_week);
110+ g_date_time_unref(last_week_bound);
111+ } else {
112+ GDateTime* next_week = g_date_time_add_days(now, 6);
113+ GDateTime* next_week_bound = g_date_time_new_local(g_date_time_get_year(next_week),
114+ g_date_time_get_month(next_week),
115+ g_date_time_get_day_of_month(next_week),
116+ 23, 59, 59.9);
117+ if (g_date_time_compare(time, next_week_bound) <= 0)
118+ prox = DATE_PROXIMITY_NEXT_WEEK;
119+
120+ g_date_time_unref(next_week);
121+ g_date_time_unref(next_week_bound);
122+ }
123+
124+ }
125+
126+ return prox;
127+}
128+
129+const char*
130+dgettext_datetime(const char *text)
131+{
132+ return dgettext("indicator-datetime", text);
133+}
134+
135+/**
136+ * _ a time yesterday should be shown as (e.g. “Yesterday 3:55 PM”)
137+ * _ a time today should be shown as just the time (e.g. “3:55 PM”)
138+ * _ a time tomorrow should be shown as(e.g. “Tomorrow 3:55 PM”)
139+ * _ a time any other day this week should be shown as the short version of the
140+ * day and time (e.g. “Wed 3:55 PM”)
141+ * weekday (e.g. “Friday”)
142+ * _ a time after this week should be shown as the short version of the day,
143+ * date, and time (e.g. “Wed 21 Apr 3:55 PM”)
144+ * _ in addition, when presenting the times of upcoming events, the time should
145+ * be followed by the timezone if it is different from the one the computer
146+ * is currently set to. For example, “Wed 3:55 PM UTC−5”.
147+ *
148+ * TODO - keep inline with indicator-datetime
149+ */
150+char* generate_full_format_string_at_time (GDateTime* now,
151+ GDateTime* then)
152+{
153+ GString* ret = g_string_new (nullptr);
154+
155+ if (then != nullptr) {
156+ const date_proximity_t prox = getDateProximity(now, then);
157+
158+ if (is_locale_12h()) {
159+ switch (prox) {
160+ case DATE_PROXIMITY_YESTERDAY:
161+ /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
162+ This format string is used for showing, on a 12-hour clock, times that happen yesterday.
163+ (\u2003 is a unicode em space which is slightly wider than a normal space.)
164+ en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM" */
165+ g_string_assign (ret, dgettext_datetime("Yesterday\u2003%l:%M %p"));
166+ break;
167+
168+ case DATE_PROXIMITY_TODAY:
169+ /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
170+ This format string is used for showing, on a 12-hour clock, times that happened today.
171+ en_US example: "%l:%M %p" --> "1:00 PM" */
172+ g_string_assign (ret, dgettext_datetime("%l:%M %p"));
173+ break;
174+
175+ case DATE_PROXIMITY_TOMORROW:
176+ /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
177+ This format string is used for showing, on a 12-hour clock, events/appointments that happen tomorrow.
178+ (\u2003 is a unicode em space which is slightly wider than a normal space.)
179+ en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 1:00 PM" */
180+ g_string_assign (ret, dgettext_datetime("Tomorrow\u2003%l:%M %p"));
181+ break;
182+
183+ case DATE_PROXIMITY_LAST_WEEK:
184+ case DATE_PROXIMITY_NEXT_WEEK:
185+ /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
186+ This format string is used for showing, on a 12-hour clock, times that happened in the last week.
187+ (\u2003 is a unicode em space which is slightly wider than a normal space.)
188+ en_US example: "%a\u2003%l:%M %p" --> "Fri 1:00 PM" */
189+ g_string_assign (ret, dgettext_datetime("%a\u2003%l:%M %p"));
190+ break;
191+
192+ case DATE_PROXIMITY_FAR:
193+ /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
194+ This format string is used for showing, on a 12-hour clock, times that happened before a week from now.
195+ (\u2003 is a unicode em space which is slightly wider than a normal space.)
196+ en_US example: "%a %d %b\u2003%l:%M %p" --> "Fri Oct 31 1:00 PM"
197+ en_GB example: "%a %b %d\u2003%l:%M %p" --> "Fri 31 Oct 1:00 PM" */
198+ g_string_assign (ret, dgettext_datetime("%a %d %b\u2003%l:%M %p"));
199+ break;
200+ }
201+ } else {
202+ switch (prox) {
203+
204+ case DATE_PROXIMITY_YESTERDAY:
205+ /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
206+ This format string is used for showing, on a 24-hour clock, times that happen yesterday.
207+ (\u2003 is a unicode em space which is slightly wider than a normal space.)
208+ en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00" */
209+ g_string_assign (ret, dgettext_datetime("Yesterday\u2003%H:%M"));
210+ break;
211+
212+ case DATE_PROXIMITY_TODAY:
213+ /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
214+ This format string is used for showing, on a 24-hour clock, times that happened today.
215+ en_US example: "%H:%M" --> "13:00" */
216+ g_string_assign (ret, dgettext_datetime("%H:%M"));
217+ break;
218+
219+ case DATE_PROXIMITY_TOMORROW:
220+ /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
221+ This format string is used for showing, on a 24-hour clock, events/appointments that happen tomorrow.
222+ (\u2003 is a unicode em space which is slightly wider than a normal space.)
223+ en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 13:00" */
224+ g_string_assign (ret, dgettext_datetime("Tomorrow\u2003%H:%M"));
225+ break;
226+
227+ case DATE_PROXIMITY_LAST_WEEK:
228+ case DATE_PROXIMITY_NEXT_WEEK:
229+ /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
230+ This format string is used for showing, on a 24-hour clock, times that happened in the last week.
231+ (\u2003 is a unicode em space which is slightly wider than a normal space.)
232+ en_US example: "%a\u2003%H:%M" --> "Fri 13:00" */
233+ g_string_assign (ret, dgettext_datetime("%a\u2003%H:%M"));
234+ break;
235+
236+ case DATE_PROXIMITY_FAR:
237+ /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
238+ This format string is used for showing, on a 24-hour clock, times that happened before a week from now.
239+ (\u2003 is a unicode em space which is slightly wider than a normal space.)
240+ en_US example: "%a %d %b\u2003%H:%M" --> "Fri Oct 31 13:00"
241+ en_GB example: "%a %b %d\u2003%H:%M" --> "Fri 31 Oct 13:00" */
242+ g_string_assign (ret, dgettext_datetime("%a %d %b\u2003%H:%M"));
243+ break;
244+ }
245+ }
246+ }
247+
248+ return g_string_free (ret, FALSE);
249+}
250+
251+QString RelativeTimeFormatter::format() const
252+{
253+ GDateTime* now = g_date_time_new_from_unix_utc(QDateTime::currentMSecsSinceEpoch() / 1000);
254+ if (!now) { return QString(); }
255+
256+ GDateTime* then = g_date_time_new_from_unix_local(time());
257+ if (!then) { return QString(); }
258+
259+ char* time_format = generate_full_format_string_at_time(now, then);
260+
261+ QString str(QString::fromUtf8(time_format));
262+ g_free(time_format);
263+
264+ g_date_time_unref(now);
265+ g_date_time_unref(then);
266+
267+ return str;
268+}
plugins/Utils/relativetimeformatter.hnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright 2014 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+#ifndef RELATIVETIMEFORMATTER_H
19+#define RELATIVETIMEFORMATTER_H
20+
21+#include "timeformatter.h"
22+
23+// TODO - move this to the sdk
24+// https://blueprints.launchpad.net/ubuntu-ui-toolkit/+spec/time-formatter
25+class RelativeTimeFormatter : public GDateTimeFormatter
26+{
27+ Q_OBJECT
28+public:
29+ RelativeTimeFormatter(QObject *parent = 0);
30+
31+ QString format() const override;
32+};
33+
34+#endif // RELATIVETIMEFORMATTER_H
plugins/Utils/timeformatter.cpp+6−1
@@ -121,6 +121,11 @@ TimeFormatter::TimeFormatter(QObject *parent): QObject(parent)
121121 g_bus_get (G_BUS_TYPE_SYSTEM, priv->cancellable, got_bus, priv);
122122}
123123
124+TimeFormatter::TimeFormatter(const QString &initialFormat, QObject *parent): TimeFormatter(parent)
125+{
126+ priv->format = initialFormat;
127+}
128+
124129TimeFormatter::~TimeFormatter()
125130{
126131 if (priv->system_bus) {
@@ -177,7 +182,7 @@ QString TimeFormatter::formatTime() const
177182}
178183
179184GDateTimeFormatter::GDateTimeFormatter(QObject* parent)
180−: TimeFormatter(parent)
185+: TimeFormatter("%d-%m-%Y %I:%M%p", parent)
181186{
182187}
183188
plugins/Utils/timeformatter.h+3−1
@@ -33,7 +33,7 @@ public:
3333 TimeFormatter(QObject *parent = 0);
3434 virtual ~TimeFormatter();
3535
36− QString format() const;
36+ virtual QString format() const;
3737 QString timeString() const;
3838 qint64 time() const;
3939
@@ -48,6 +48,8 @@ Q_SIGNALS:
4848 void timeChanged(qint64 time);
4949
5050protected:
51+ TimeFormatter(const QString &initialFormat, QObject *parent = 0);
52+
5153 virtual QString formatTime() const;
5254
5355private:
po/unity8.pot+29−25
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2014-09-18 20:12+0800\n"
11+"POT-Creation-Date: 2014-09-24 10:43+0100\n"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -21,12 +21,12 @@ msgstr ""
2121msgid "Password: "
2222msgstr ""
2323
24−#: plugins/Unity/Launcher/launcheritem.cpp:44
25−#: plugins/Unity/Launcher/launcheritem.cpp:74
24+#: plugins/Unity/Launcher/launcheritem.cpp:45
25+#: plugins/Unity/Launcher/launcheritem.cpp:91
2626msgid "Pin shortcut"
2727msgstr ""
2828
29−#: plugins/Unity/Launcher/launcheritem.cpp:74
29+#: plugins/Unity/Launcher/launcheritem.cpp:91
3030msgid "Unpin shortcut"
3131msgstr ""
3232
@@ -157,15 +157,15 @@ msgstr ""
157157msgid "Hello"
158158msgstr ""
159159
160−#: qml/Dash/GenericScopeView.qml:364 qml/Dash/GenericScopeView.qml:483
160+#: qml/Dash/GenericScopeView.qml:411 qml/Dash/GenericScopeView.qml:533
161161msgid "See less"
162162msgstr ""
163163
164−#: qml/Dash/GenericScopeView.qml:364
164+#: qml/Dash/GenericScopeView.qml:411
165165msgid "See all"
166166msgstr ""
167167
168−#: qml/Dash/GenericScopeView.qml:427 qml/Dash/PageHeader.qml:258
168+#: qml/Dash/GenericScopeView.qml:473 qml/Dash/PageHeader.qml:258
169169#: qml/Panel/SearchIndicator.qml:27
170170msgid "Search"
171171msgstr ""
@@ -203,7 +203,7 @@ msgid "Send"
203203msgstr ""
204204
205205#: qml/Dash/ScopesOverview.qml:206
206−msgid "Manage Dash"
206+msgid "Manage Scopes"
207207msgstr ""
208208
209209#: qml/Dash/ScopesOverview.qml:428
@@ -266,27 +266,31 @@ msgstr ""
266266msgid "Conference"
267267msgstr ""
268268
269−#: qml/Panel/Indicators/MenuItemFactory.qml:651
269+#: qml/Panel/Indicators/MenuItemFactory.qml:600
270+msgid "Nothing is playing"
271+msgstr ""
272+
273+#: qml/Panel/Indicators/MenuItemFactory.qml:748
270274msgid "In queue…"
271275msgstr ""
272276
273−#: qml/Panel/Indicators/MenuItemFactory.qml:655
277+#: qml/Panel/Indicators/MenuItemFactory.qml:752
274278msgid "Downloading"
275279msgstr ""
276280
277−#: qml/Panel/Indicators/MenuItemFactory.qml:657
281+#: qml/Panel/Indicators/MenuItemFactory.qml:754
278282msgid "Paused, tap to resume"
279283msgstr ""
280284
281−#: qml/Panel/Indicators/MenuItemFactory.qml:659
285+#: qml/Panel/Indicators/MenuItemFactory.qml:756
282286msgid "Canceled"
283287msgstr ""
284288
285−#: qml/Panel/Indicators/MenuItemFactory.qml:661
289+#: qml/Panel/Indicators/MenuItemFactory.qml:758
286290msgid "Finished"
287291msgstr ""
288292
289−#: qml/Panel/Indicators/MenuItemFactory.qml:663
293+#: qml/Panel/Indicators/MenuItemFactory.qml:760
290294msgid "Failed, tap to retry"
291295msgstr ""
292296
@@ -298,51 +302,51 @@ msgstr ""
298302msgid "Roaming"
299303msgstr ""
300304
301−#: qml/Shell.qml:292
305+#: qml/Shell.qml:320
302306msgid "Enter your passphrase"
303307msgstr ""
304308
305−#: qml/Shell.qml:293
309+#: qml/Shell.qml:321
306310msgid "Sorry, incorrect passphrase"
307311msgstr ""
308312
309−#: qml/Shell.qml:295
313+#: qml/Shell.qml:323
310314msgid "Enter your passcode"
311315msgstr ""
312316
313−#: qml/Shell.qml:296
317+#: qml/Shell.qml:324
314318msgid "Sorry, incorrect passcode"
315319msgstr ""
316320
317−#: qml/Shell.qml:299
321+#: qml/Shell.qml:327
318322#, qt-format
319323msgid "Enter your %1"
320324msgstr ""
321325
322−#: qml/Shell.qml:300
326+#: qml/Shell.qml:328
323327#, qt-format
324328msgid "Sorry, incorrect %1"
325329msgstr ""
326330
327−#: qml/Shell.qml:335
331+#: qml/Shell.qml:363
328332msgid "Sorry, incorrect passphrase."
329333msgstr ""
330334
331−#: qml/Shell.qml:336
335+#: qml/Shell.qml:364
332336msgid "Sorry, incorrect passcode."
333337msgstr ""
334338
335−#: qml/Shell.qml:337
339+#: qml/Shell.qml:365
336340msgid "This will be your last attempt."
337341msgstr ""
338342
339−#: qml/Shell.qml:339
343+#: qml/Shell.qml:367
340344msgid ""
341345"If passphrase is entered incorrectly, your phone will conduct a factory "
342346"reset and all personal data will be deleted."
343347msgstr ""
344348
345−#: qml/Shell.qml:340
349+#: qml/Shell.qml:368
346350msgid ""
347351"If passcode is entered incorrectly, your phone will conduct a factory reset "
348352"and all personal data will be deleted."
qml/Components/Header.qmlnew file+27
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (C) 2014 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 1.1
18+import Ubuntu.Components.Themes.Ambiance 1.1
19+
20+PageHeadStyle {
21+ id: root
22+
23+ property var styledItem: root
24+ property string title
25+ property var config: PageHeadConfiguration {}
26+ property var contents: null
27+}
qml/Panel/Indicators.qml+2−13
@@ -84,10 +84,6 @@ Showable {
8484 indicators.state = "locked";
8585 }
8686 }
87−
88− if (enableRelease && revealProgress === 0) {
89− menuContent.releaseContent();
90− }
9187 }
9288
9389 function calculateCurrentItem(xValue, useBuffer) {
@@ -189,8 +185,7 @@ Showable {
189185 }
190186 indicatorsModel: visibleIndicators.model
191187 visible: indicators.partiallyOpened || indicators.fullyOpened
192− clip: !indicators.fullyOpened
193− activeHeader: indicators.state == "hint" || indicators.state == "reveal"
188+ clip: indicators.partiallyOpened
194189 enabled: contentEnabled
195190
196191 //small shadow gradient at bottom of menu
@@ -212,7 +207,7 @@ Showable {
212207 Rectangle {
213208 id: handle
214209
215− color: menuContent.backgroundColor
210+ color: menuContent.color
216211
217212 anchors {
218213 left: parent.left
@@ -358,12 +353,6 @@ Showable {
358353 maxTotalDragDistance: openedHeight - panelHeight
359354 distanceThreshold: panelHeight
360355
361− onStatusChanged: {
362− if (status === DirectionalDragArea.Recognized) {
363− menuContent.activateContent();
364− }
365− }
366−
367356 onTapped: showTapped(Qt.point(touchSceneX, touchSceneY));
368357 }
369358
qml/Panel/Indicators/DefaultIndicatorPage.qml+8−49
@@ -26,14 +26,12 @@ IndicatorBase {
2626 id: main
2727
2828 //const
29− property bool contentActive: false
3029 property string title: rootActionState.title
31− property alias emptyText: emptyLabel.text
3230 property alias highlightFollowsCurrentItem : mainMenu.highlightFollowsCurrentItem
3331
3432 Indicators.UnityMenuModelStack {
3533 id: menuStack
36− head: contentActive ? main.menuModel : null
34+ head: main.menuModel
3735
3836 property var rootMenu: null
3937
@@ -88,11 +86,13 @@ IndicatorBase {
8886 }
8987 }
9088
91− // Ensure all delegates are cached in order to improve smoothness of scrolling
92− cacheBuffer: 10000
89+ // Don't load all the delegates (only max of 3 pages worth -1/0/+1)
90+ cacheBuffer: Math.max(height * 3, units.gu(70))
9391
9492 // Only allow flicking if the content doesn't fit on the page
9593 interactive: contentHeight > height
94+ // FIXME - https://bugreports.qt-project.org/browse/QTBUG-41207
95+ boundsBehavior: Flickable.StopAtBounds
9696
9797 property int selectedIndex: -1
9898 property bool blockCurrentIndexChange: false
@@ -125,17 +125,12 @@ IndicatorBase {
125125 delegate: Loader {
126126 id: loader
127127 objectName: "menuItem" + index
128− asynchronous: false
128+ asynchronous: true
129+ width: ListView.view.width
129130 visible: status == Loader.Ready
130131
131132 property int modelIndex: index
132−
133− anchors {
134− left: parent.left
135− right: parent.right
136− }
137−
138− sourceComponent: factory.load(model)
133+ sourceComponent: factory.load(model, identifier)
139134
140135 onLoaded: {
141136 if (item.hasOwnProperty("selected")) {
@@ -180,44 +175,8 @@ IndicatorBase {
180175 menuModel: mainMenu.model ? mainMenu.model : null
181176 }
182177
183− Components.Label {
184− id: emptyLabel
185− objectName: "emptyLabel"
186− visible: mainMenu.count == 0
187− anchors {
188− top: parent.top
189− left: parent.left
190− right: parent.right
191− topMargin: units.gu(2)
192− }
193− wrapMode: Text.WordWrap
194− horizontalAlignment: Text.AlignHCenter
195−
196− //style
197− color: "#e8e1d0"
198− fontSize: "medium"
199−
200− text: "Empty!"
201− }
202−
203− function start()
204− {
205− reset()
206− if (!contentActive) {
207− contentActive = true;
208− }
209− }
210−
211− function stop()
212− {
213− if (contentActive) {
214− contentActive = false;
215− }
216− }
217−
218178 function reset()
219179 {
220− mainMenu.selectedIndex = -1;
221180 mainMenu.positionViewAtBeginning();
222181 }
223182}
qml/Panel/Indicators/IndicatorDelegate.qml+2−10
@@ -20,14 +20,6 @@
2020import QtQuick 2.0
2121
2222IndicatorBase {
23− enabled: false
24−
25− onRootActionStateChanged: {
26− if (rootActionState == undefined) {
27− enabled = false;
28− return;
29− }
30−
31− enabled = rootActionState.visible;
32− }
23+ enabled: rootActionState.visible
24+ property string title: rootActionState.title
3325}
qml/Panel/Indicators/MenuItemFactory.qml+158−47
@@ -19,11 +19,10 @@
1919
2020import QtQuick 2.0
2121import Ubuntu.Settings.Menus 0.1 as Menus
22−import Ubuntu.Settings.Components 0.1 as SettingsComponents
2322import QMenuModel 0.1
2423import Utils 0.1 as Utils
2524import Ubuntu.Components.ListItems 0.1 as ListItems
26−import Ubuntu.Components 0.1
25+import Ubuntu.Components 1.1
2726
2827Item {
2928 id: menuFactory
@@ -32,32 +31,38 @@ Item {
3231 property var menuModel: null
3332
3433 property var _map: {
35− "unity.widgets.systemsettings.tablet.volumecontrol" : sliderMenu,
36− "unity.widgets.systemsettings.tablet.switch" : switchMenu,
37−
38− "com.canonical.indicator.button" : buttonMenu,
39− "com.canonical.indicator.div" : separatorMenu,
40− "com.canonical.indicator.section" : sectionMenu,
41− "com.canonical.indicator.progress" : progressMenu,
42− "com.canonical.indicator.slider" : sliderMenu,
43− "com.canonical.indicator.switch" : switchMenu,
44− "com.canonical.indicator.alarm" : alarmMenu,
45− "com.canonical.indicator.appointment" : appointmentMenu,
46− "com.canonical.indicator.transfer" : transferMenu,
47− "com.canonical.indicator.button-section" : buttonSectionMenu,
48−
49− "com.canonical.indicator.messages.messageitem" : messageItem,
50− "com.canonical.indicator.messages.sourceitem" : groupedMessage,
51−
52− "com.canonical.unity.slider" : sliderMenu,
53− "com.canonical.unity.switch" : switchMenu,
54−
55− "com.canonical.unity.media-player" : mediaPayerMenu,
56− "com.canonical.unity.playback-item" : playbackItemMenu,
57−
58− "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
59− "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
60− "com.canonical.indicator.network.modeminfoitem" : modeminfoitem,
34+ "default": {
35+ "unity.widgets.systemsettings.tablet.volumecontrol" : sliderMenu,
36+ "unity.widgets.systemsettings.tablet.switch" : switchMenu,
37+
38+ "com.canonical.indicator.button" : buttonMenu,
39+ "com.canonical.indicator.div" : separatorMenu,
40+ "com.canonical.indicator.section" : sectionMenu,
41+ "com.canonical.indicator.progress" : progressMenu,
42+ "com.canonical.indicator.slider" : sliderMenu,
43+ "com.canonical.indicator.switch" : switchMenu,
44+ "com.canonical.indicator.alarm" : alarmMenu,
45+ "com.canonical.indicator.appointment" : appointmentMenu,
46+ "com.canonical.indicator.transfer" : transferMenu,
47+ "com.canonical.indicator.button-section" : buttonSectionMenu,
48+ "com.canonical.indicator.link" : linkMenu,
49+
50+ "com.canonical.indicator.messages.messageitem" : messageItem,
51+ "com.canonical.indicator.messages.sourceitem" : groupedMessage,
52+
53+ "com.canonical.unity.slider" : sliderMenu,
54+ "com.canonical.unity.switch" : switchMenu,
55+
56+ "com.canonical.unity.media-player" : mediaPayerMenu,
57+ "com.canonical.unity.playback-item" : playbackItemMenu,
58+
59+ "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
60+ "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
61+ "com.canonical.indicator.network.modeminfoitem" : modeminfoitem,
62+ },
63+ "indicator-messages" : {
64+ "com.canonical.indicator.button" : messagesButtonMenu
65+ }
6166 }
6267
6368 function getExtendedProperty(object, propertyName, defaultValue) {
@@ -100,6 +105,7 @@ Item {
100105 return maximum;
101106 }
102107 enabled: menuData && menuData.sensitive || false
108+ highlightWhenPressed: false
103109
104110 onMenuModelChanged: {
105111 loadAttributes();
@@ -138,12 +144,43 @@ Item {
138144
139145 buttonText: menuData && menuData.label || ""
140146 enabled: menuData && menuData.sensitive || false
147+ highlightWhenPressed: false
141148
142149 onTriggered: {
143150 menuModel.activate(menuIndex);
144151 }
145152 }
146153 }
154+
155+ Component {
156+ id: messagesButtonMenu;
157+
158+ Item {
159+ objectName: "messagesButtonMenu"
160+ property QtObject menuData: null
161+ property var menuModel: menuFactory.menuModel
162+ property int menuIndex: -1
163+
164+ implicitHeight: units.gu(5)
165+ enabled: menuData && menuData.sensitive || false
166+
167+ Label {
168+ id: buttonMenuLabel
169+ text: menuData && menuData.label || ""
170+ anchors.centerIn: parent
171+ font.bold: true
172+ }
173+
174+ MouseArea {
175+ anchors {
176+ fill: buttonMenuLabel
177+ margins: units.gu(-1)
178+ }
179+ onClicked: menuModel.activate(menuIndex);
180+ }
181+ }
182+ }
183+
147184 Component {
148185 id: sectionMenu;
149186
@@ -169,6 +206,7 @@ Item {
169206 iconSource: menuData && menuData.icon || ""
170207 value : menuData && menuData.actionState || 0.0
171208 enabled: menuData && menuData.sensitive || false
209+ highlightWhenPressed: false
172210 }
173211 }
174212
@@ -183,10 +221,58 @@ Item {
183221 text: menuData && menuData.label || ""
184222 iconSource: menuData && menuData.icon || ""
185223 enabled: menuData && menuData.sensitive || false
224+ highlightWhenPressed: false
225+
226+ onTriggered: {
227+ menuModel.activate(menuIndex);
228+ }
229+
230+ // FIXME : At the moment, the indicators aren't using
231+ // com.canonical.indicators.link for settings menu. Need to fudge it.
232+ property bool settingsMenu: menuData && menuData.action.indexOf("settings") > -1 || false
233+ backColor: settingsMenu ? Qt.rgba(1,1,1,0.07) : "transparent"
234+ component: settingsMenu ? buttonForSettings : undefined
235+ Component {
236+ id: buttonForSettings
237+ Icon {
238+ name: "settings"
239+ height: units.gu(3)
240+ width: height
241+ color: Theme.palette.selected.backgroundText
242+ }
243+ }
244+ }
245+ }
246+
247+ Component {
248+ id: linkMenu;
249+
250+ Menus.StandardMenu {
251+ objectName: "linkMenu"
252+ property QtObject menuData: null
253+ property int menuIndex: -1
254+
255+ text: menuData && menuData.label || ""
256+ iconSource: menuData && menuData.icon || ""
257+ enabled: menuData && menuData.sensitive || false
258+ highlightWhenPressed: false
186259
187260 onTriggered: {
188261 menuModel.activate(menuIndex);
189262 }
263+
264+ backColor: Qt.rgba(1,1,1,0.07)
265+
266+ component: menuData.icon ? icon : undefined
267+ Component {
268+ id: icon
269+ Icon {
270+ source: menuData.icon
271+ height: units.gu(3)
272+ width: height
273+ color: Theme.palette.selected.backgroundText
274+ }
275+ }
190276 }
191277 }
192278
@@ -201,6 +287,7 @@ Item {
201287 text: menuData && menuData.label || ""
202288 enabled: menuData && menuData.sensitive || false
203289 checked: menuData && menuData.isToggled || false
290+ highlightWhenPressed: false
204291
205292 onTriggered: {
206293 menuModel.activate(menuIndex);
@@ -208,7 +295,6 @@ Item {
208295 }
209296 }
210297
211−
212298 Component {
213299 id: switchMenu;
214300
@@ -221,6 +307,7 @@ Item {
221307 iconSource: menuData && menuData.icon || ""
222308 enabled: menuData && menuData.sensitive || false
223309 checked: menuData && menuData.isToggled || false
310+ highlightWhenPressed: false
224311
225312 onTriggered: {
226313 menuModel.activate(menuIndex);
@@ -247,6 +334,7 @@ Item {
247334 iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
248335 time: timeFormatter.timeString
249336 enabled: menuData && menuData.sensitive || false
337+ highlightWhenPressed: false
250338
251339 onMenuModelChanged: {
252340 loadAttributes();
@@ -286,6 +374,7 @@ Item {
286374 time: timeFormatter.timeString
287375 eventColor: getExtendedProperty(extendedData, "xCanonicalColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
288376 enabled: menuData && menuData.sensitive || false
377+ highlightWhenPressed: false
289378
290379 onMenuModelChanged: {
291380 loadAttributes();
@@ -351,10 +440,11 @@ Item {
351440
352441 text: menuData && menuData.label || ""
353442 enabled: menuData && menuData.sensitive || false
354− checked: menuData && menuData.isToggled || false
443+ active: menuData && menuData.isToggled || false
355444 secure: getExtendedProperty(extendedData, "xCanonicalWifiApIsSecure", false)
356445 adHoc: getExtendedProperty(extendedData, "xCanonicalWifiApIsAdhoc", false)
357446 signalStrength: strengthAction.valid ? strengthAction.state : 0
447+ highlightWhenPressed: false
358448
359449 onMenuModelChanged: {
360450 loadAttributes();
@@ -383,6 +473,7 @@ Item {
383473 property var menuModel: menuFactory.menuModel
384474 property int menuIndex: -1
385475 property var extendedData: menuData && menuData.ext || undefined
476+ highlightWhenPressed: false
386477
387478 property var statusLabelAction: UnityMenuAction {
388479 model: menuModel
@@ -468,9 +559,10 @@ Item {
468559 property var extendedData: menuData && menuData.ext || undefined
469560
470561 text: menuData && menuData.label || ""
471− iconSource: getExtendedProperty(extendedData, "icon", "qrc:/indicators/artwork/messaging/default_app.svg")
562+ iconSource: getExtendedProperty(extendedData, "icon", "image://theme/message")
472563 count: menuData && menuData.actionState.length > 0 ? menuData.actionState[0] : "0"
473564 enabled: menuData && menuData.sensitive || false
565+ highlightWhenPressed: false
474566 removable: true
475567
476568 onMenuModelChanged: {
@@ -502,17 +594,20 @@ Item {
502594 property var menuModel: menuFactory.menuModel
503595 property int menuIndex: -1
504596 property var actionState: menuData && menuData.actionState || undefined
597+ property bool running: getExtendedProperty(actionState, "running", false)
505598
506− playerIcon: menuData && menuData.icon || ""
507− playerName: menuData && menuData.label || ""
599+ playerIcon: menuData && menuData.icon || "image://theme/stock_music"
600+ playerName: menuData && menuData.label || i18n.tr("Nothing is playing")
508601
509− albumArt: getExtendedProperty(actionState, "art-url", "")
510− song: getExtendedProperty(actionState, "title", "unknown")
511− artist: getExtendedProperty(actionState, "artist", "unknown")
512− album: getExtendedProperty(actionState, "album", "unknown")
513− running: getExtendedProperty(actionState, "running", false)
602+ albumArt: getExtendedProperty(actionState, "art-url", "image://theme/stock_music")
603+ song: getExtendedProperty(actionState, "title", "")
604+ artist: getExtendedProperty(actionState, "artist", "")
605+ album: getExtendedProperty(actionState, "album", "")
606+ showTrack: running && (state == "Playing" || state == "Paused")
514607 state: getExtendedProperty(actionState, "state", "")
515608 enabled: menuData && menuData.sensitive || false
609+ highlightWhenPressed: false
610+ showDivider: false
516611
517612 onTriggered: {
518613 model.activate(modelIndex);
@@ -551,6 +646,7 @@ Item {
551646 canGoNext: nextAction.valid
552647 canGoPrevious: previousAction.valid
553648 enabled: menuData && menuData.sensitive || false
649+ highlightWhenPressed: false
554650
555651 onPlay: {
556652 playAction.activate();
@@ -590,9 +686,10 @@ Item {
590686 property var uid: getExtendedProperty(extendedData, "xCanonicalUid", undefined)
591687
592688 text: menuData && menuData.label || ""
593− iconSource: menuData && menuData.icon || ""
689+ iconSource: menuData && menuData.icon || "image://theme/transfer-none"
594690 maximum: 1.0
595691 enabled: menuData && menuData.sensitive || false
692+ highlightWhenPressed: false
596693 removable: true
597694 confirmRemoval: true
598695
@@ -688,7 +785,7 @@ Item {
688785 Component {
689786 id: buttonSectionMenu;
690787
691− ListItems.Standard {
788+ Menus.StandardMenu {
692789 objectName: "buttonSectionMenu"
693790 property QtObject menuData: null
694791 property var menuModel: menuFactory.menuModel
@@ -697,8 +794,9 @@ Item {
697794
698795 iconSource: menuData && menuData.icon || ""
699796 enabled: menuData && menuData.sensitive || false
797+ highlightWhenPressed: false
700798 text: menuData && menuData.label || ""
701− showDivider: false
799+ foregroundColor: Theme.palette.normal.backgroundText
702800
703801 onMenuModelChanged: {
704802 loadAttributes();
@@ -711,22 +809,35 @@ Item {
711809 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-extra-label': 'string'});
712810 }
713811
714− control: Button {
715− text: getExtendedProperty(extendedData, "xCanonicalExtraLabel", "")
812+ component: Component {
813+ Button {
814+ objectName: "buttonSectionMenuControl"
815+ text: getExtendedProperty(extendedData, "xCanonicalExtraLabel", "")
716816
717− onClicked: {
718− menuModel.activate(menuIndex);
817+ onClicked: {
818+ menuModel.activate(menuIndex);
819+ }
719820 }
720821 }
721822 }
722823 }
723824
724− function load(modelData) {
825+ function load(modelData, context) {
725826 if (modelData.type !== undefined) {
726− var component = _map[modelData.type];
827+ var component = undefined;
828+
829+ var contextComponents = _map[context];
830+ if (contextComponents !== undefined) {
831+ component = contextComponents[modelData.type];
832+ }
833+
834+ if (component === undefined) {
835+ component = _map["default"][modelData.type];
836+ }
727837 if (component !== undefined) {
728838 return component;
729839 }
840+ console.debug("Don't know how to make " + modelData.type + " for " + context);
730841 }
731842 if (modelData.isCheck || modelData.isRadio) {
732843 return checkableMenu;
qml/Panel/Indicators/MessageMenuItemFactory.qml+156−171
@@ -24,8 +24,9 @@ import Ubuntu.Settings.Menus 0.1 as Menus
2424import QMenuModel 0.1 as QMenuModel
2525import Utils 0.1 as Utils
2626
27−Item {
27+Loader {
2828 id: messageFactoryItem
29+ objectName: "messageItem"
2930 property var menuModel: null
3031 property QtObject menuData: null
3132 property int menuIndex: -1
@@ -38,9 +39,8 @@ Item {
3839 property var actionsDescription: getExtendedProperty(extendedData, "xCanonicalMessageActions", undefined)
3940
4041 // TODO - bug #1260728
41− property var timeFormatter: Utils.TimeFormatter {
42− time: getExtendedProperty(extendedData, "xCanonicalTime", 0)
43− format: "hh:mm - MMM dd"
42+ property var timeFormatter: Utils.RelativeTimeFormatter {
43+ time: getExtendedProperty(extendedData, "xCanonicalTime", 0) / 1000000
4444 }
4545
4646 onMenuModelChanged: {
@@ -50,6 +50,32 @@ Item {
5050 loadAttributes();
5151 }
5252
53+ sourceComponent: loadMessage(actionsDescription)
54+
55+ function loadMessage(actions)
56+ {
57+ var parameterType = ""
58+ for (var actIndex in actions) {
59+ var desc = actions[actIndex];
60+ if (desc["parameter-type"] !== undefined) {
61+ parameterType += desc["parameter-type"];
62+ } else {
63+ parameterType += "_";
64+ }
65+ }
66+
67+ if (parameterType === "") {
68+ return simpleMessage;
69+ } else if (parameterType === "s") {
70+ return textMessage;
71+ } else if (parameterType === "_s") {
72+ return snapDecision;
73+ } else {
74+ console.debug("Unknown paramater type: " + parameterType);
75+ }
76+ return undefined;
77+ }
78+
5379 function loadAttributes() {
5480 if (!menuModel || menuIndex == -1) return;
5581 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-time': 'int64',
@@ -66,188 +92,147 @@ Item {
6692 return defaultValue;
6793 }
6894
69− implicitHeight: contents.status == Loader.Ready ? contents.item.implicitHeight : 0
70−
71− Loader {
72− id: contents
73− objectName: "loader"
74− anchors.fill: parent
75−
76− sourceComponent: loadMessage(actionsDescription);
77−
78− Component {
79− id: simpleTextMessage
80−
81− Menus.SimpleTextMessageMenu {
82− id: message
83− objectName: "simpleTextMessage"
84− // text
85− title: menuData && menuData.label || ""
86− time: timeFormatter.timeString
87− message: getExtendedProperty(extendedData, "xCanonicalText", "")
88− // icons
89− avatar: getExtendedProperty(extendedData, "icon", "qrc:/indicators/artwork/messaging/default_contact.png")
90− appIcon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "qrc:/indicators/artwork/messaging/default_app.svg")
91− // actions
92− enabled: menuData && menuData.sensitive || false
93− removable: !selected
94− selected: messageFactoryItem.selected
95−
96− onAppActivated: {
97− menuModel.activate(menuIndex, true);
98− }
99− onDismissed: {
100− menuModel.activate(menuIndex, false);
101− }
102− onClicked: {
103− if (selected) {
104− menuDeselected();
105− } else {
106− menuSelected();
107− }
108− }
109−
110− backgroundIndicator: RemoveBackground {
111− state: message.swipingState
95+ Component {
96+ id: simpleMessage
97+
98+ Menus.SimpleMessageMenu {
99+ id: message
100+ objectName: "simpleTextMessage"
101+ // text
102+ title: menuData && menuData.label || ""
103+ time: timeFormatter.timeString
104+ body: getExtendedProperty(extendedData, "xCanonicalText", "")
105+ // icons
106+ avatar: getExtendedProperty(extendedData, "icon", "image://theme/contact")
107+ icon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "image://theme/message")
108+ // actions
109+ enabled: menuData && menuData.sensitive || false
110+ removable: !selected
111+ confirmRemoval: true
112+ selected: messageFactoryItem.selected
113+
114+ onIconActivated: {
115+ menuModel.activate(menuIndex, true);
116+ }
117+ onDismissed: {
118+ menuModel.activate(menuIndex, false);
119+ }
120+ onTriggered: {
121+ if (selected) {
122+ menuDeselected();
123+ } else {
124+ menuSelected();
112125 }
113126 }
114127 }
128+ }
115129
116− Component {
117− id: textMessage
118−
119− Menus.TextMessageMenu {
120− id: message
121− objectName: "textMessage"
122− property var replyActionDescription: actionsDescription && actionsDescription.length > 0 ? actionsDescription[0] : undefined
130+ Component {
131+ id: textMessage
123132
124− property var replyAction: QMenuModel.UnityMenuAction {
125− model: menuModel
126− index: menuIndex
127− name: getExtendedProperty(replyActionDescription, "name", "")
128− }
133+ Menus.TextMessageMenu {
134+ id: message
135+ objectName: "textMessage"
136+ property var replyActionDescription: actionsDescription && actionsDescription.length > 0 ? actionsDescription[0] : undefined
129137
130− // text
131− title: menuData && menuData.label || ""
132− time: timeFormatter.timeString
133− message: getExtendedProperty(extendedData, "xCanonicalText", "")
134− replyButtonText: getExtendedProperty(replyActionDescription, "label", "Send")
135− // icons
136− avatar: getExtendedProperty(extendedData, "icon", "qrc:/indicators/artwork/messaging/default_contact.png")
137− appIcon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "qrc:/indicators/artwork/messaging/default_app.svg")
138− // actions
139− replyEnabled: replyAction.valid && replyAction.enabled
140− enabled: menuData && menuData.sensitive || false
141− removable: !selected
142− selected: messageFactoryItem.selected
143−
144− onAppActivated: {
145− menuModel.activate(menuIndex, true);
146− }
147− onDismissed: {
148− menuModel.activate(menuIndex, false);
149− }
150− onReplied: {
151− replyAction.activate(value);
152− }
153− onClicked: {
154− if (selected) {
155− menuDeselected();
156− } else {
157− menuSelected();
158− }
159− }
160−
161− backgroundIndicator: RemoveBackground {
162− state: message.swipingState
163− }
138+ property var replyAction: QMenuModel.UnityMenuAction {
139+ model: menuModel
140+ index: menuIndex
141+ name: getExtendedProperty(replyActionDescription, "name", "")
164142 }
165− }
166− Component {
167− id: snapDecision
168−
169− Menus.SnapDecisionMenu {
170− id: message
171− objectName: "snapDecision"
172− property var activateActionDescription: actionsDescription && actionsDescription.length > 0 ? actionsDescription[0] : undefined
173− property var replyActionDescription: actionsDescription && actionsDescription.length > 1 ? actionsDescription[1] : undefined
174−
175− property var activateAction: QMenuModel.UnityMenuAction {
176− model: menuModel
177− index: menuIndex
178− name: getExtendedProperty(activateActionDescription, "name", "")
179− }
180− property var replyAction: QMenuModel.UnityMenuAction {
181− model: menuModel
182− index: menuIndex
183− name: getExtendedProperty(replyActionDescription, "name", "")
184− }
185−
186− // text
187− title: menuData && menuData.label || ""
188− time: timeFormatter.timeString
189− message: getExtendedProperty(extendedData, "xCanonicalText", "")
190− actionButtonText: getExtendedProperty(activateActionDescription, "label", "Call back")
191− replyButtonText: getExtendedProperty(replyActionDescription, "label", "Send")
192− replyMessages: getExtendedProperty(replyActionDescription, "parameter-hint", "")
193− // icons
194− avatar: getExtendedProperty(extendedData, "icon", "qrc:/indicators/artwork/messaging/default_contact.png")
195− appIcon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "qrc:/indicators/artwork/messaging/default_app.svg")
196− // actions
197− activateEnabled: activateAction.valid && activateAction.enabled
198− replyEnabled: replyAction.valid && replyAction.enabled
199− enabled: menuData && menuData.sensitive || false
200− removable: !selected
201− selected: messageFactoryItem.selected
202−
203− onAppActivated: {
204− menuModel.activate(menuIndex, true);
205− }
206− onDismissed: {
207− menuModel.activate(menuIndex, false);
208− }
209− onActivated: {
210− activateAction.activate();
211− }
212− onReplied: {
213− replyAction.activate(value);
214− }
215− onClicked: {
216− if (selected) {
217− menuDeselected();
218− } else {
219− menuSelected();
220− }
221− }
222143
223− backgroundIndicator: RemoveBackground {
224− state: message.swipingState
144+ // text
145+ title: menuData && menuData.label || ""
146+ time: timeFormatter.timeString
147+ body: getExtendedProperty(extendedData, "xCanonicalText", "")
148+ replyButtonText: getExtendedProperty(replyActionDescription, "label", "Send")
149+ // icons
150+ avatar: getExtendedProperty(extendedData, "icon", "image://theme/contact")
151+ icon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "image://theme/message")
152+ // actions
153+ replyEnabled: replyAction.valid && replyAction.enabled
154+ enabled: menuData && menuData.sensitive || false
155+ removable: !selected
156+ confirmRemoval: true
157+ selected: messageFactoryItem.selected
158+ highlightWhenPressed: false
159+
160+ onIconActivated: {
161+ menuModel.activate(menuIndex, true);
162+ }
163+ onDismissed: {
164+ menuModel.activate(menuIndex, false);
165+ }
166+ onReplied: {
167+ replyAction.activate(value);
168+ }
169+ onTriggered: {
170+ if (selected) {
171+ menuDeselected();
172+ } else {
173+ menuSelected();
225174 }
226175 }
227176 }
228177 }
229178
230− function loadMessage(actions)
231− {
232− var parameterType = ""
233− for (var actIndex in actions) {
234− var desc = actions[actIndex];
235− if (desc["parameter-type"] !== undefined) {
236− parameterType += desc["parameter-type"];
237− } else {
238− parameterType += "_";
179+ Component {
180+ id: snapDecision
181+
182+ Menus.SnapDecisionMenu {
183+ id: message
184+ objectName: "snapDecision"
185+ property var activateActionDescription: actionsDescription && actionsDescription.length > 0 ? actionsDescription[0] : undefined
186+ property var replyActionDescription: actionsDescription && actionsDescription.length > 1 ? actionsDescription[1] : undefined
187+
188+ property var activateAction: QMenuModel.UnityMenuAction {
189+ model: menuModel
190+ index: menuIndex
191+ name: getExtendedProperty(activateActionDescription, "name", "")
192+ }
193+ property var replyAction: QMenuModel.UnityMenuAction {
194+ model: menuModel
195+ index: menuIndex
196+ name: getExtendedProperty(replyActionDescription, "name", "")
239197 }
240− }
241198
242− if (parameterType === "") {
243− return simpleTextMessage;
244− } else if (parameterType === "s") {
245− return textMessage;
246− } else if (parameterType === "_s") {
247− return snapDecision;
248− } else {
249− console.debug("Unknown paramater type: " + parameterType);
199+ // text
200+ title: menuData && menuData.label || ""
201+ time: timeFormatter.timeString
202+ body: getExtendedProperty(extendedData, "xCanonicalText", "")
203+ actionButtonText: getExtendedProperty(activateActionDescription, "label", "Call back")
204+ replyButtonText: getExtendedProperty(replyActionDescription, "label", "Send")
205+ // icons
206+ avatar: getExtendedProperty(extendedData, "icon", "image://theme/contact")
207+ icon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "image://theme/missed-call")
208+ // actions
209+ actionEnabled: activateAction.valid && activateAction.enabled
210+ replyEnabled: replyAction.valid && replyAction.enabled
211+ enabled: menuData && menuData.sensitive || false
212+ removable: !selected
213+ confirmRemoval: true
214+ selected: messageFactoryItem.selected
215+ highlightWhenPressed: false
216+
217+ onIconActivated: {
218+ menuModel.activate(menuIndex, true);
219+ }
220+ onDismissed: {
221+ menuModel.activate(menuIndex, false);
222+ }
223+ onActionActivated: {
224+ activateAction.activate();
225+ }
226+ onReplied: {
227+ replyAction.activate(value);
228+ }
229+ onTriggered: {
230+ if (selected) {
231+ menuDeselected();
232+ } else {
233+ menuSelected();
234+ }
235+ }
250236 }
251− return undefined;
252237 }
253238}
qml/Panel/Indicators/RemoveBackground.qmldeleted−55
@@ -1,55 +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 Lesser General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU Lesser General Public License for more details.
12− *
13− * You should have received a copy of the GNU Lesser General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−
20−Rectangle {
21− anchors.fill: parent
22− color: "#333130"
23− Label {
24− id: backgroundText
25−
26− anchors.fill: parent
27− verticalAlignment: Text.AlignVCenter
28− text: "Clear"
29− fontSize: "medium"
30− color: "#e8e1d0"
31− font.bold: true
32− }
33−
34− states: [
35− State {
36− name: "SwipingRight"
37− PropertyChanges {
38− target: backgroundText
39− anchors.rightMargin: units.gu(3)
40− anchors.leftMargin: 0
41− horizontalAlignment: Text.AlignRight
42−
43− }
44− },
45− State {
46− name: "SwipingLeft"
47− PropertyChanges {
48− target: backgroundText
49− anchors.rightMargin: 0
50− anchors.leftMargin: units.gu(3)
51− horizontalAlignment: Text.AlignLeft
52− }
53− }
54− ]
55−}
qml/Panel/Indicators/client/IndicatorsClient.qml+4
@@ -25,6 +25,10 @@ Rectangle {
2525 color: "black"
2626 id: root
2727
28+ Component.onCompleted: {
29+ Theme.name = "Ubuntu.Components.Themes.SuruGradient"
30+ }
31+
2832 PageStack {
2933 id: pages
3034 anchors.fill: parent
qml/Panel/MenuContent.qml+89−113
@@ -15,122 +15,64 @@
1515 */
1616
1717import QtQuick 2.0
18−import Ubuntu.Components 0.1
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 1.1
1920import Unity.Indicators 0.1 as Indicators
2021import Utils 0.1
2122import "../Components"
23+import "Indicators"
2224
23−// FIXME : We dont want to use MainView.
24−// Need a regular Item which can have tabs with local header.
25−// https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1211704
26−MainView {
25+Rectangle {
2726 id: content
2827
2928 property QtObject indicatorsModel: null
30− property bool __contentActive: false
31− readonly property int currentMenuIndex: tabs.selectedTabIndex
32− backgroundColor: "#221e1c" // FIXME not in palette yet
33− property int contentReleaseInterval: 20000
34− property bool activeHeader: false
35− property real headerHeight: tabs.tabBar.height
29+ readonly property alias currentMenuIndex: listViewHeader.currentIndex
30+ color: "#221e1c" // FIXME not in palette yet
31+ property real headerHeight: listViewHeader.height
3632
3733 width: units.gu(40)
3834 height: units.gu(42)
3935
4036 function setCurrentMenuIndex(index, animate) {
41− if (tabs.selectedTabIndex !== index) {
42− if (tabs.selectedTabIndex === -1 || !animate) {
43− tabs.tabBar.animate = false;
44− }
45− tabs.selectedTabIndex = index;
46− tabs.tabBar.animate = true;
47− }
37+ // FIXME - https://bugreports.qt-project.org/browse/QTBUG-41229
38+ listViewHeader.currentIndex = -1;
39+ listViewHeader.currentIndex = index;
4840 }
4941
50− function activateContent() {
51− contentReleaseTimer.stop();
52− __contentActive = true;
53− }
42+ ListView {
43+ id: listViewHeader
44+ objectName: "indicatorsHeaderListView"
45+ model: content.indicatorsModel
46+ clip: true
5447
55− function releaseContent() {
56− if (__contentActive) {
57− contentReleaseTimer.restart();
48+ anchors {
49+ left: parent.left
50+ right: parent.right
5851 }
59− }
60−
61− onActiveHeaderChanged: {
62− tabs.tabBar.selectionMode = activeHeader;
63− tabs.tabBar.alwaysSelectionMode = activeHeader;
64− }
65−
66− Tabs {
67− id: tabs
68− objectName: "tabs"
69− anchors.fill: parent
70−
71− Repeater {
72− id: repeater
73− model: content.indicatorsModel ? content.indicatorsModel : null
74− objectName: "tabsRepeater"
75−
76− // FIXME: This is needed because tabs dont handle repeaters well.
77− // Due to the child ordering happening after child insertion.
78− // QTBUG-32438
79− onItemAdded: {
80− parent.childrenChanged();
81− }
82−
83− Tab {
84− id: tab
85− objectName: model.identifier
86−
87− page: Page {
88− Loader {
89− id: loader
90− clip: true
91− anchors.fill: parent
92− source: pageSource
93− asynchronous: true
94−
95− readonly property bool indexActive: index >= 0 && index < menuActivator.count && menuActivator.content[index].active
96− readonly property bool contentActive: content.__contentActive && indexActive
97−
98− onContentActiveChanged: {
99− if (contentActive && item) {
100− item.start()
101− } else if (!contentActive && item) {
102− item.stop()
103− }
104− }
105−
106− onVisibleChanged: {
107− // Reset the indicator states
108− if (!visible && item && item["reset"]) {
109− item.reset()
110− }
111− }
112−
113− onLoaded: {
114− for(var pName in indicatorProperties) {
115− if (item.hasOwnProperty(pName)) {
116− item[pName] = indicatorProperties[pName]
117− }
118− }
119− if (contentActive && tabs.visible) {
120− item.start()
121− }
122− }
123−
124− Binding {
125− target: tab
126− property: "title"
127− value: loader.item && loader.item.hasOwnProperty("title") && loader.item.title !== "" ? loader.item.title : model.identifier
128− }
129−
130− Binding {
131− target: loader.item
132− property: "objectName"
133− value: identifier + "-page"
52+ height: units.gu(8.5)
53+
54+ highlightFollowsCurrentItem: true
55+ highlightMoveDuration: 0
56+
57+ orientation: ListView.Horizontal
58+ snapMode: ListView.SnapOneItem
59+ highlightRangeMode: ListView.StrictlyEnforceRange
60+ boundsBehavior: Flickable.StopAtBounds
61+ // Load all the indicator menus (a big number)
62+ cacheBuffer: 1073741823
63+
64+ delegate: Header {
65+ width: ListView.view.width
66+ height: implicitHeight
67+
68+ title: indicatorDelegate.title !== "" ? indicatorDelegate.title : identifier
69+
70+ IndicatorDelegate {
71+ id: indicatorDelegate
72+ Component.onCompleted: {
73+ for(var pName in indicatorProperties) {
74+ if (indicatorDelegate.hasOwnProperty(pName)) {
75+ indicatorDelegate[pName] = indicatorProperties[pName];
13476 }
13577 }
13678 }
@@ -138,20 +80,54 @@ MainView {
13880 }
13981 }
14082
141− Timer {
142− id: contentReleaseTimer
143−
144− interval: contentReleaseInterval
145− onTriggered: {
146− content.__contentActive = false;
147− menuActivator.clear();
83+ ListView {
84+ id: listViewContent
85+ objectName: "indicatorsContentListView"
86+ anchors {
87+ left: parent.left
88+ right: parent.right
89+ top: listViewHeader.bottom
90+ bottom: parent.bottom
14891 }
149− }
92+ model: content.indicatorsModel
93+ clip: true
94+
95+ currentIndex: listViewHeader.currentIndex
96+ interactive: false
97+ highlightMoveDuration: 0
98+ orientation: ListView.Horizontal
99+ // Load all the indicator menus (a big number)
100+ cacheBuffer: 1073741823
101+
102+ delegate: Loader {
103+ id: loader
104+ width: ListView.view.width
105+ height: ListView.view.height
106+ objectName: identifier
107+
108+ source: pageSource
109+ asynchronous: true
110+
111+ onVisibleChanged: {
112+ // Reset the indicator states
113+ if (!visible && item && item["reset"]) {
114+ item.reset()
115+ }
116+ }
150117
151− Indicators.MenuContentActivator {
152− id: menuActivator
153− running: content.__contentActive
154− baseIndex: content.currentMenuIndex
155− count: indicatorsModel.count
118+ onLoaded: {
119+ for(var pName in indicatorProperties) {
120+ if (item.hasOwnProperty(pName)) {
121+ item[pName] = indicatorProperties[pName]
122+ }
123+ }
124+ }
125+
126+ Binding {
127+ target: loader.item
128+ property: "objectName"
129+ value: identifier + "-page"
130+ }
131+ }
156132 }
157133}
tests/mocks/Unity/Indicators/FakeMenuPage.qml−10
@@ -19,16 +19,6 @@ import QtQuick 2.0
1919Flickable {
2020 objectName: "fakeMenuPlugin"
2121 // Make it compatible with the PluginItem interface
22− function start() {
23− if (shell != undefined && shell.indicator_status != undefined) {
24− shell.indicator_status[objectName].started = true;
25− }
26− }
27− function stop() {
28− if (shell != undefined && shell.indicator_status != undefined) {
29− shell.indicator_status[objectName].started = false;
30− }
31− }
3222 function reset() {
3323 if (shell != undefined && shell.indicator_status != undefined) {
3424 shell.indicator_status[objectName].reset++;
tests/plugins/Utils/CMakeLists.txt+1−1
@@ -7,7 +7,7 @@ include_directories(
77macro(run_tests)
88 set(_test_list "")
99 foreach(_test ${ARGN})
10− set(testCommand env "LC_ALL=C" ${CMAKE_CURRENT_BINARY_DIR}/${_test}Exec
10+ set(testCommand env "LC_ALL=C.UTF-8" ${CMAKE_CURRENT_BINARY_DIR}/${_test}Exec
1111 -o ${CMAKE_BINARY_DIR}/${_test}.xml,xunitxml
1212 -o -,txt)
1313 add_test(NAME ${_test} COMMAND ${testCommand})
tests/plugins/Utils/timeformattertest.cpp+104−7
@@ -17,6 +17,9 @@
1717 */
1818
1919#include "timeformatter.h"
20+#include "relativetimeformatter.h"
21+
22+#include <locale.h>
2023
2124#include <QtTest>
2225#include <QDebug>
@@ -24,8 +27,24 @@
2427class TimeFormatterTest : public QObject
2528{
2629 Q_OBJECT
27−private Q_SLOTS:
30+private:
31+ void addTestData(QList<int> daysToAdd = QList<int>()) {
32+ QTest::addColumn<QDateTime>("time");
33+
34+ Q_FOREACH(int dayToAdd, daysToAdd) {
35+ QDateTime time = QDateTime::currentDateTime().addDays(dayToAdd);
36+ QDateTime am(time); am.setTime(QTime(1, 1, 1));
37+ QDateTime midday(time); midday.setTime(QTime(12, 0, 0));
38+ QDateTime pm(time); pm.setTime(QTime(17, 59, 59));
39+
40+ QTest::newRow(QString("%1day-am").arg(dayToAdd).toLatin1()) << am;
41+ QTest::newRow(QString("%1day-midday").arg(dayToAdd).toLatin1()) << midday;
42+ QTest::newRow(QString("%1day-pm").arg(dayToAdd).toLatin1()) << pm;
43+ }
2844
45+ }
46+
47+private Q_SLOTS:
2948 void initTestCase()
3049 {
3150 setenv("UNITY_TEST_ENV", "1", 1);
@@ -36,12 +55,12 @@ private Q_SLOTS:
3655 unsetenv("UNITY_TEST_ENV");
3756 }
3857
39− void testFormat()
58+ void testTimeFormatter_Format_data() { addTestData(QList<int>() << 0); }
59+ void testTimeFormatter_Format()
4060 {
61+ QFETCH(QDateTime, time);
4162 const QString format = "hh:mm dd.MM.yy";
4263
43− QDateTime time = QDateTime::currentDateTime();
44−
4564 TimeFormatter formatter;
4665 formatter.setTime(time.toMSecsSinceEpoch() * 1000);
4766 formatter.setFormat(format);
@@ -49,18 +68,96 @@ private Q_SLOTS:
4968 QCOMPARE(formatter.timeString(), time.toString(format));
5069 }
5170
52− void testFormatStrF()
71+ void tesGDatetTimeFormatter_Format_data() { addTestData(QList<int>() << 0); }
72+ void tesGDatetTimeFormatter_Format()
5373 {
74+ QFETCH(QDateTime, time);
5475 const QString format = "%d-%m-%Y %I:%M%p";
5576
56− QDateTime time = QDateTime::currentDateTime();
57−
5877 GDateTimeFormatter formatter;
5978 formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
6079 formatter.setFormat(format);
6180
6281 QCOMPARE(formatter.timeString(), time.toString("dd-MM-yyyy hh:mmAP"));
6382 }
83+
84+ void tesRelativeTimeFormatter_FarBack_data() { addTestData(QList<int>() << -200 << -20 << -7); }
85+ void tesRelativeTimeFormatter_FarBack()
86+ {
87+ QFETCH(QDateTime, time);
88+
89+ RelativeTimeFormatter formatter;
90+ formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
91+
92+ QCOMPARE(formatter.timeString(), time.toString("ddd dd MMM\u2003HH:mm"));
93+ }
94+
95+ void tesRelativeTimeFormatter_WeekBack_data() { addTestData(QList<int>() << -6 << -2); }
96+ void tesRelativeTimeFormatter_WeekBack()
97+ {
98+ QFETCH(QDateTime, time);
99+
100+ RelativeTimeFormatter formatter;
101+ formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
102+
103+ QCOMPARE(formatter.timeString(), time.toString("ddd\u2003HH:mm"));
104+ }
105+
106+ void tesRelativeTimeFormatter_Yesterday_data() { addTestData(QList<int>() << -1); }
107+ void tesRelativeTimeFormatter_Yesterday()
108+ {
109+ QFETCH(QDateTime, time);
110+
111+ RelativeTimeFormatter formatter;
112+ formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
113+
114+ QCOMPARE(formatter.timeString(), QString("Yesterday\u2003%1").arg(time.toString("HH:mm")));
115+ }
116+
117+ void tesRelativeTimeFormatter_Today_data() { addTestData(QList<int>() << 0); }
118+ void tesRelativeTimeFormatter_Today()
119+ {
120+ QFETCH(QDateTime, time);
121+
122+ RelativeTimeFormatter formatter;
123+ formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
124+
125+ QCOMPARE(formatter.timeString(), time.toString("HH:mm"));
126+ }
127+
128+ void tesRelativeTimeFormatter_Tomorrow_data() { addTestData(QList<int>() << 1); }
129+ void tesRelativeTimeFormatter_Tomorrow()
130+ {
131+ QFETCH(QDateTime, time);
132+
133+ RelativeTimeFormatter formatter;
134+ formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
135+
136+ QCOMPARE(formatter.timeString(), QString("Tomorrow\u2003%1").arg(time.toString("HH:mm")));
137+ }
138+
139+
140+ void tesRelativeTimeFormatter_WeekForward_data() { addTestData(QList<int>() << 2 << 6); }
141+ void tesRelativeTimeFormatter_WeekForward()
142+ {
143+ QFETCH(QDateTime, time);
144+
145+ RelativeTimeFormatter formatter;
146+ formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
147+
148+ QCOMPARE(formatter.timeString(), time.toString("ddd\u2003HH:mm"));
149+ }
150+
151+ void tesRelativeTimeFormatter_FarForward_data() { addTestData(QList<int>() << 7 << 20 << 200); }
152+ void tesRelativeTimeFormatter_FarForward()
153+ {
154+ QFETCH(QDateTime, time);
155+
156+ RelativeTimeFormatter formatter;
157+ formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
158+
159+ QCOMPARE(formatter.timeString(), time.toString("ddd dd MMM\u2003HH:mm"));
160+ }
64161};
65162
66163QTEST_GUILESS_MAIN(TimeFormatterTest)
tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+32−16
@@ -368,9 +368,9 @@ Item {
368368 function test_create_alarmMenu_data() {
369369 return [
370370 {label: "testLabel1", enabled: true, icon: "file:///testIcon1", color: Qt.rgba(0, 0, 0, 0),
371− time: new Date(2014, 04, 14).getTime()*1000, timeFormat: "%a %d %b %l:%M %p"},
371+ time: new Date(2014, 04, 14), timeFormat: "%a %d %b %l:%M %p"},
372372 {label: "testLabel2", enabled: false, icon: "file:///testIcon2", color: Qt.rgba(1, 0, 0, 0),
373− time: new Date(2015, 12, 31).getTime()*1000, timeFormat: "%A" },
373+ time: new Date(2015, 11, 31), timeFormat: "%A" },
374374 ];
375375 }
376376
@@ -380,11 +380,11 @@ Item {
380380 menuData.sensitive = data.enabled;
381381 menuData.icon = data.icon;
382382 menuData.ext = {
383− 'xCanonicalTime': data.time,
383+ 'xCanonicalTime': data.time.getTime() / 1000,
384384 'xCanonicalTimeFormat': data.timeFormat
385385 };
386386 timeFormatter.format = data.timeFormat;
387− timeFormatter.time = data.time;
387+ timeFormatter.time = data.time.getTime() / 1000;
388388
389389 loader.data = menuData;
390390 loader.sourceComponent = factory.load(menuData);
@@ -400,9 +400,9 @@ Item {
400400 function test_create_appointmentMenu_data() {
401401 return [
402402 {label: "testLabel1", enabled: true, icon: "file:///testIcon1", color: Qt.rgba(0, 0, 0, 0),
403− time: new Date(2014, 04, 14).getTime()*1000, timeFormat: "%a %d %b %l:%M %p"},
403+ time: new Date(2014, 04, 14), timeFormat: "%a %d %b %l:%M %p"},
404404 {label: "testLabel2", enabled: false, icon: "file:///testIcon2", color: Qt.rgba(1, 0, 0, 0),
405− time: new Date(2015, 12, 31).getTime()*1000, timeFormat: "%A" },
405+ time: new Date(2015, 11, 31), timeFormat: "%A" },
406406 ];
407407 }
408408
@@ -413,11 +413,11 @@ Item {
413413 menuData.icon = data.icon;
414414 menuData.ext = {
415415 'xCanonicalColor': data.colour,
416− 'xCanonicalTime': data.time,
416+ 'xCanonicalTime': data.time.getTime() / 1000,
417417 'xCanonicalTimeFormat': data.timeFormat
418418 };
419419 timeFormatter.format = data.timeFormat;
420− timeFormatter.time = data.time;
420+ timeFormatter.time = data.time.getTime() / 1000;
421421
422422 loader.data = menuData;
423423 loader.sourceComponent = factory.load(menuData);
@@ -488,7 +488,10 @@ Item {
488488 compare(loader.item.text, data.label, "Label does not match data");
489489 compare(loader.item.iconSource, data.icon, "Icon does not match data");
490490 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
491− compare(loader.item.control.text, data.buttonText, "Button text does not match data");
491+
492+ var button = findChild(loader.item, "buttonSectionMenuControl");
493+ verify(button !== null);
494+ compare(button.text, data.buttonText, "Button text does not match data");
492495 }
493496
494497 function test_create_wifiSection_data() {
@@ -514,8 +517,8 @@ Item {
514517
515518 function test_create_accessPoint_data() {
516519 return [
517− {label: "testLabel1", enabled: true, checked: false, secure: true, adHoc: false },
518− {label: "testLabel2", enabled: false, checked: true, secure: false, adHoc: true },
520+ {label: "testLabel1", enabled: true, active: false, secure: true, adHoc: false },
521+ {label: "testLabel2", enabled: false, active: true, secure: false, adHoc: true },
519522 ];
520523 }
521524
@@ -523,7 +526,7 @@ Item {
523526 menuData.type = "unity.widgets.systemsettings.tablet.accesspoint";
524527 menuData.label = data.label;
525528 menuData.sensitive = data.enabled;
526− menuData.isToggled = data.checked;
529+ menuData.isToggled = data.active;
527530 menuData.ext = {
528531 'xCanonicalWifiApStrengthAction': "action::strength",
529532 'xCanonicalWifiApIsSecure': data.secure,
@@ -539,7 +542,7 @@ Item {
539542 compare(loader.item.strengthAction.name, "action::strength", "Strength action incorrect");
540543 compare(loader.item.secure, data.secure, "Secure does not match data");
541544 compare(loader.item.adHoc, data.adHoc, "AdHoc does not match data");
542− compare(loader.item.checked, data.checked, "Checked does not match data");
545+ compare(loader.item.active, data.active, "Checked does not match data");
543546 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
544547 }
545548
@@ -635,7 +638,8 @@ Item {
635638 album: "album1",
636639 running: true,
637640 state: "Playing",
638− enabled: true
641+ enabled: true,
642+ showTrack: true
639643 },{
640644 label: "player2",
641645 icon: "file:://icon2",
@@ -645,7 +649,19 @@ Item {
645649 album: "album2",
646650 running: false,
647651 state: "Paused",
648− enabled: false
652+ enabled: false,
653+ showTrack: false
654+ },{
655+ label: "player3",
656+ icon: "file:://icon3",
657+ albumArt: "file:://art3",
658+ song: "song3",
659+ artist: "artist3",
660+ album: "album3",
661+ running: true,
662+ state: "Stopped",
663+ enabled: true,
664+ showTrack: false
649665 }
650666 ];
651667 }
@@ -675,7 +691,7 @@ Item {
675691 compare(loader.item.song, data.song, "Song does not match data");
676692 compare(loader.item.artist, data.artist, "Artist does not match data");
677693 compare(loader.item.album, data.album, "Album does not match data");
678− compare(loader.item.running, data.running, "Running does not match data");
694+ compare(loader.item.showTrack, data.showTrack, "Show track does not match data");
679695 compare(loader.item.state, data.state, "State does not match data");
680696 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
681697 }
tests/qmltests/Panel/Indicators/tst_MessageMenuItemFactory.qml+43−53
@@ -32,9 +32,8 @@ Item {
3232 menuIndex: 0
3333 }
3434
35− Utils.TimeFormatter {
35+ Utils.RelativeTimeFormatter {
3636 id: timeFormatter
37− format: "hh:mm - MMM dd"
3837 }
3938
4039 UT.UnityTestCase {
@@ -73,8 +72,8 @@ Item {
7372
7473 function test_create_simpleTextmessage_data() {
7574 return [
76− { title: "Title1", time: new Date(2013, 10, 10).getTime()*1000, message: "This is a text message 1", avatar: "file:///avatar1", appIcon: "file:///appIcon1", enabled: true},
77− { title: "Title2", time: new Date(2014, 12, 10).getTime()*1000, message: "This is a text message 2", avatar: "file:///avatar2", appIcon: "file:///appIcon2", enabled: false},
75+ { title: "Title1", time: new Date(2013, 10, 10), body: "This is a text message 1", avatar: "file:///avatar1", icon: "file:///appIcon1", enabled: true},
76+ { title: "Title2", time: new Date(2014, 12, 10), body: "This is a text message 2", avatar: "file:///avatar2", icon: "file:///appIcon2", enabled: false},
7877 ];
7978 }
8079
@@ -83,30 +82,27 @@ Item {
8382 menuData.label = data.title;
8483 menuData.sensitive = data.enabled;
8584 menuData.ext = {
86− 'xCanonicalTime': data.time,
87− 'xCanonicalText': data.message,
85+ 'xCanonicalTime': data.time.getTime()*1000,
86+ 'xCanonicalText': data.body,
8887 'icon': data.avatar,
89− 'xCanonicalAppIcon': data.appIcon,
88+ 'xCanonicalAppIcon': data.icon,
9089 };
9190 factory.menuData = menuData;
92− timeFormatter.time = data.time;
93−
94− var loader = findChild(factory, "loader");
95− verify(loader !== undefined);
96−
97− tryCompare(loader.item, "objectName", "simpleTextMessage");
98− compare(loader.item.title, data.title, "Title does not match data");
99− compare(loader.item.time, timeFormatter.timeString, "Time does not match data");
100− compare(loader.item.message, data.message, "Message does not match data");
101− compare(loader.item.avatar, data.avatar, "Avatar does not match data");
102− compare(loader.item.appIcon, data.appIcon, "App icon does not match data");
103− compare(loader.item.enabled, data.enabled, "Enabled does not match data");
91+ timeFormatter.time = data.time.getTime()/1000;
92+
93+ tryCompare(factory.item, "objectName", "simpleTextMessage");
94+ compare(factory.item.title, data.title, "Title does not match data");
95+ compare(factory.item.time, timeFormatter.timeString, "Time does not match data");
96+ compare(factory.item.body, data.body, "Message does not match data");
97+ compare(factory.item.avatar, data.avatar, "Avatar does not match data");
98+ compare(factory.item.icon, data.icon, "App icon does not match data");
99+ compare(factory.item.enabled, data.enabled, "Enabled does not match data");
104100 }
105101
106102 function test_create_textmessage_data() {
107103 return [
108− { title: "Title1", time: new Date(2013, 10, 10).getTime()*1000, message: "This is a text message 1", avatar: "file:///avatar1", appIcon: "file:///appIcon1", enabled: true},
109− { title: "Title2", time: new Date(2014, 12, 10).getTime()*1000, message: "This is a text message 2", avatar: "file:///avatar2", appIcon: "file:///appIcon2", enabled: false},
104+ { title: "Title1", time: new Date(2013, 10, 10), body: "This is a text message 1", avatar: "file:///avatar1", icon: "file:///appIcon1", enabled: true},
105+ { title: "Title2", time: new Date(2014, 12, 10), body: "This is a text message 2", avatar: "file:///avatar2", icon: "file:///appIcon2", enabled: false},
110106 ];
111107 }
112108
@@ -115,10 +111,10 @@ Item {
115111 menuData.label = data.title;
116112 menuData.sensitive = data.enabled;
117113 menuData.ext = {
118− 'xCanonicalTime': data.time,
119− 'xCanonicalText': data.message,
114+ 'xCanonicalTime': data.time.getTime()*1000,
115+ 'xCanonicalText': data.body,
120116 'icon': data.avatar,
121− 'xCanonicalAppIcon': data.appIcon,
117+ 'xCanonicalAppIcon': data.icon,
122118 'xCanonicalMessageActions': [{
123119 'parameter-type': "s",
124120 'name': "action::reply",
@@ -127,25 +123,22 @@ Item {
127123 ]
128124 };
129125 factory.menuData = menuData;
130− timeFormatter.time = data.time;
131−
132− var loader = findChild(factory, "loader");
133− verify(loader !== undefined);
134−
135− tryCompare(loader.item, "objectName", "textMessage");
136− compare(loader.item.title, data.title, "Title does not match data");
137− compare(loader.item.time, timeFormatter.timeString, "Time does not match data");
138− compare(loader.item.message, data.message, "Message does not match data");
139− compare(loader.item.avatar, data.avatar, "Avatar does not match data");
140− compare(loader.item.appIcon, data.appIcon, "App icon does not match data");
141− compare(loader.item.enabled, data.enabled, "Enabled does not match data");
126+ timeFormatter.time = data.time.getTime()/1000;
127+
128+ tryCompare(factory.item, "objectName", "textMessage");
129+ compare(factory.item.title, data.title, "Title does not match data");
130+ compare(factory.item.time, timeFormatter.timeString, "Time does not match data");
131+ compare(factory.item.body, data.body, "Message does not match data");
132+ compare(factory.item.avatar, data.avatar, "Avatar does not match data");
133+ compare(factory.item.icon, data.icon, "App icon does not match data");
134+ compare(factory.item.enabled, data.enabled, "Enabled does not match data");
142135 }
143136
144137
145138 function test_create_snapDecision_data() {
146139 return [
147− { title: "Title1", time: new Date(2013, 10, 10).getTime()*1000, message: "This is a text message 1", avatar: "file:///avatar1", appIcon: "file:///appIcon1", enabled: true},
148− { title: "Title2", time: new Date(2014, 12, 10).getTime()*1000, message: "This is a text message 2", avatar: "file:///avatar2", appIcon: "file:///appIcon2", enabled: false},
140+ { title: "Title1", time: new Date(2013, 10, 10), body: "This is a text message 1", avatar: "file:///avatar1", icon: "file:///appIcon1", enabled: true},
141+ { title: "Title2", time: new Date(2014, 12, 10), body: "This is a text message 2", avatar: "file:///avatar2", icon: "file:///appIcon2", enabled: false},
149142 ];
150143 }
151144
@@ -154,10 +147,10 @@ Item {
154147 menuData.label = data.title;
155148 menuData.sensitive = data.enabled;
156149 menuData.ext = {
157− 'xCanonicalTime': data.time,
158− 'xCanonicalText': data.message,
150+ 'xCanonicalTime': data.time.getTime()*1000,
151+ 'xCanonicalText': data.body,
159152 'icon': data.avatar,
160− 'xCanonicalAppIcon': data.appIcon,
153+ 'xCanonicalAppIcon': data.icon,
161154 'xCanonicalMessageActions': [{
162155 'name': "action::callback",
163156 'label': "Callback1"
@@ -169,18 +162,15 @@ Item {
169162 ]
170163 };
171164 factory.menuData = menuData;
172− timeFormatter.time = data.time;
173−
174− var loader = findChild(factory, "loader");
175− verify(loader !== undefined);
176−
177− tryCompare(loader.item, "objectName", "snapDecision");
178− compare(loader.item.title, data.title, "Title does not match data");
179− compare(loader.item.time, timeFormatter.timeString, "Time does not match data");
180− compare(loader.item.message, data.message, "Message does not match data");
181− compare(loader.item.avatar, data.avatar, "Avatar does not match data");
182− compare(loader.item.appIcon, data.appIcon, "App icon does not match data");
183− compare(loader.item.enabled, data.enabled, "Enabled does not match data");
165+ timeFormatter.time = data.time.getTime()/1000;
166+
167+ tryCompare(factory.item, "objectName", "snapDecision");
168+ compare(factory.item.title, data.title, "Title does not match data");
169+ compare(factory.item.time, timeFormatter.timeString, "Time does not match data");
170+ compare(factory.item.body, data.body, "Message does not match data");
171+ compare(factory.item.avatar, data.avatar, "Avatar does not match data");
172+ compare(factory.item.icon, data.icon, "App icon does not match data");
173+ compare(factory.item.enabled, data.enabled, "Enabled does not match data");
184174 }
185175
186176 }
tests/qmltests/Panel/tst_Indicators.qml+10−10
@@ -173,7 +173,7 @@ Item {
173173 function test_row_visible_menuContent_visible(data) {
174174 indicators.show();
175175
176− var indicatorTabs = findChild(indicators, "tabs");
176+ var contentListView = findChild(indicators, "indicatorsContentListView");
177177 var indicatorRowItems = findChild(indicators, "indicatorRowItems");
178178
179179 var count = data.visible.length
@@ -194,19 +194,19 @@ Item {
194194 tryCompareFunction(function() { return findChild(indicatorRowItems, widgetName) !== null }, data.visible[i]);
195195
196196 // check for tab
197− tryCompareFunction(function() { return findChild(indicatorTabs, pageName) !== null }, data.visible[i]);
197+ tryCompareFunction(function() { return findChild(contentListView, pageName) !== null }, data.visible[i]);
198198 }
199199 }
200200
201− function test_indicator_visible_correct_tabs_data() { return [
202− {tag: "current-first", currentIndex: 0, visible: [false, true, true, true, true], expectedIndex: 0, expectedTab: "indicator-fake2" },
203− {tag: "current-last", currentIndex: 4, visible: [true, true, true, true, false], expectedIndex: 3, expectedTab: "indicator-fake4" },
204− {tag: "after", currentIndex: 0, visible: [true, false, true, true, true], expectedIndex: 0, expectedTab: "indicator-fake1" },
205− {tag: "before", currentIndex: 1, visible: [false, true, true, true, true], expectedIndex: 1, expectedTab: "indicator-fake2" }];
201+ function test_indicator_visible_correct_menu_data() { return [
202+ {tag: "current-first", currentIndex: 0, visible: [false, true, true, true, true], expectedIndex: 0, expextedMenu: "indicator-fake2" },
203+ {tag: "current-last", currentIndex: 4, visible: [true, true, true, true, false], expectedIndex: 3, expextedMenu: "indicator-fake4" },
204+ {tag: "after", currentIndex: 0, visible: [true, false, true, true, true], expectedIndex: 0, expextedMenu: "indicator-fake1" },
205+ {tag: "before", currentIndex: 1, visible: [false, true, true, true, true], expectedIndex: 1, expextedMenu: "indicator-fake2" }];
206206 }
207207
208− function test_indicator_visible_correct_tabs(data) {
209− var indicatorTabs = findChild(indicators, "tabs");
208+ function test_indicator_visible_correct_menu(data) {
209+ var contentListView = findChild(indicators, "indicatorsContentListView");
210210 var indicatorRow = findChild(indicators, "indicatorRow");
211211
212212 indicators.show();
@@ -223,7 +223,7 @@ Item {
223223 tryCompare(indicatorRow, "currentItemIndex", data.expectedIndex);
224224
225225 // check for current selected tab
226− tryCompareFunction(function() { return findChild(indicatorTabs, data.expectedTab) === indicatorTabs.selectedTab }, true);
226+ tryCompareFunction(function() { return findChild(contentListView, data.expextedMenu) === contentListView.currentItem }, true);
227227
228228 }
229229 }
tests/qmltests/Panel/tst_MenuContent.qml+14−56
@@ -47,7 +47,6 @@ Item {
4747 MenuContent {
4848 id: menuContent
4949 indicatorsModel: indicatorsModel
50− contentReleaseInterval: 50
5150 height: parent.height - 50
5251 }
5352
@@ -91,22 +90,22 @@ Item {
9190 return "indicator-fake" + (index + 1) + "-page";
9291 }
9392
94− property string testTabObjectName : ""
93+ property string testItemObjectName : ""
9594
96− function selected_tab_equals_test_tab() {
97− var currentTab = menu_content_test.findChild(menuContent, "tabs").selectedTab
98− if (currentTab === null) {
99− console.log("selected tab undefined");
95+ function current_item_equals_test_item() {
96+ var currentItem = menu_content_test.findChild(menuContent, "indicatorsContentListView").currentItem
97+ if (currentItem === null) {
98+ console.log("current item undefined");
10099 return false;
101100 }
102101
103− var testTab = menu_content_test.findChild(menuContent, testTabObjectName);
104− if (testTab === null) {
105− console.log("test_tab " + testTabObjectName + " undefined");
102+ var testItem = menu_content_test.findChild(menuContent, testItemObjectName);
103+ if (testItem === null) {
104+ console.log("testItem " + testItemObjectName + " undefined");
106105 return false;
107106 }
108107
109− return testTab == currentTab;
108+ return testItem === currentItem;
110109 }
111110
112111 UT.UnityTestCase {
@@ -114,63 +113,22 @@ Item {
114113 name: "MenuContentTest"
115114 when: windowShown
116115
117− function init() {
118− if (menuContent.__contentActive)
119− menuContent.releaseContent();
120− tryCompare(menuContent, "__contentActive", false);
121− }
122−
123116 // Check that the correct menus are displayed for the requested item.
124117 function test_show_menu() {
125118 var menuCount = indicatorsModel.count;
126119 verify(menuCount > 0, "Menu count should be greater than zero");
127120
128− var tabs = menu_content_test.findChild(menuContent, "tabs")
121+ var listView = menu_content_test.findChild(menuContent, "indicatorsContentListView")
122+ verify(listView !== null)
129123
130124 // Loop over twice to test jump between last and first.
131125 for (var i = 0; i < menuCount*2; i++) {
132−
133126 var menuIndex = i%menuCount;
134127
135128 activate_content(menuIndex);
136− testTabObjectName = indicatorsModel.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
137− compare(tabs.selectedTabIndex, menuIndex, "Current tab index does not match selected tab index");
138− tryCompareFunction(selected_tab_equals_test_tab, true);
139− }
140− }
141−
142− // Calling activateContent should call start on all menus
143− function test_activate_content() {
144− var menuCount = indicatorsModel.count;
145− verify(menuCount > 0, "Menu count should be greater than zero");
146−
147− // Ensure all the menus are stopped first
148− menuContent.__contentActive = false;
149− for (var i = 0; i < menuCount; i++) {
150− tryCompare(indicator_status[get_test_menu_objecName(i)], "started", false);
151− }
152−
153− // activate content the content to call stop.
154− menuContent.activateContent();
155− for (var i = 0; i < menuCount; i++) {
156− tryCompare(indicator_status[get_test_menu_objecName(i)], "started", true);
157− }
158− }
159−
160− // Calling activateContent should call stop on all menus.
161− function test_release_content() {
162− var menuCount = indicatorsModel.count;
163− verify(menuCount > 0, "Menu count should be greater than zero");
164−
165− // Ensure all the menus are started first
166− menuContent.__contentActive = true;
167− for (var i = 0; i < menuCount; i++) {
168− tryCompare(indicator_status[get_test_menu_objecName(i)], "started", true);
169− }
170− // release the content to call stop.
171− menuContent.releaseContent();
172− for (var i = 0; i < menuCount; i++) {
173− tryCompare(indicator_status[get_test_menu_objecName(i)], "started", false);
129+ testItemObjectName = indicatorsModel.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
130+ compare(listView.currentIndex, menuIndex, "Current tab index does not match selected tab index");
131+ tryCompareFunction(current_item_equals_test_item, true);
174132 }
175133 }
176134