on xenial help sign in
Visual changes for indicator RTM polishing Fixes: 1329289, 1349921, 1350308, 1350952, 1354506, 1373319
73484f4Nick Dedekind
debian/control+2−2
@@ -45,7 +45,7 @@ Build-Depends: cmake,
45 qtdeclarative5-dev-tools,
45 qtdeclarative5-dev-tools,
46 qtdeclarative5-private-dev (>= 5.2.1),
46 qtdeclarative5-private-dev (>= 5.2.1),
47 qtdeclarative5-qtmultimedia-plugin,
47 qtdeclarative5-qtmultimedia-plugin,
48− qtdeclarative5-ubuntu-settings-components (>= 0.3),
48+ qtdeclarative5-ubuntu-settings-components (>= 0.4),
49 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.49) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 0.1.49),
49 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.49) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 0.1.49),
50 ttf-ubuntu-font-family,
50 ttf-ubuntu-font-family,
51Standards-Version: 3.9.4
51Standards-Version: 3.9.4
@@ -108,7 +108,7 @@ Description: Unity 8 shell
108Package: unity8-common
108Package: unity8-common
109Architecture: all
109Architecture: all
110Depends: qml-module-qtquick-layouts,
110Depends: qml-module-qtquick-layouts,
111− qtdeclarative5-ubuntu-settings-components (>= 0.3),
111+ qtdeclarative5-ubuntu-settings-components (>= 0.4),
112 qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
112 qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
113 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.49) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 0.1.49),
113 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.49) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 0.1.49),
114 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
114 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
plugins/Unity/Indicators/CMakeLists.txt−9
@@ -30,16 +30,7 @@ set(IndicatorsQML_SOURCES
30)
30)
31add_definitions(-DUNITYINDICATORS_LIBRARY)
31add_definitions(-DUNITYINDICATORS_LIBRARY)
32
32
33−set(IndicatorsQML_RESOURCES
34− resources/indicators.qrc
35−)
36−
37−qt5_add_resources(IndicatorsQML_RESOURCES_RCC
38− ${IndicatorsQML_RESOURCES}
39−)
40−
41add_library(IndicatorsQml SHARED
33add_library(IndicatorsQml SHARED
42− ${IndicatorsQML_RESOURCES_RCC}
43 ${IndicatorsQML_SOURCES}
34 ${IndicatorsQML_SOURCES}
44)
35)
45
36
plugins/Unity/Indicators/plugin.cpp−2
@@ -42,8 +42,6 @@ static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEng
42
42
43void Indicators2Plugin::registerTypes(const char *uri)
43void Indicators2Plugin::registerTypes(const char *uri)
44{
44{
45− Q_INIT_RESOURCE(indicators);
46−
47 qmlRegisterType<IndicatorsManager>(uri, 0, 1, "IndicatorsManager");
45 qmlRegisterType<IndicatorsManager>(uri, 0, 1, "IndicatorsManager");
48 qmlRegisterType<IndicatorsModel>(uri, 0, 1, "IndicatorsModel");
46 qmlRegisterType<IndicatorsModel>(uri, 0, 1, "IndicatorsModel");
49 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
47 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(
13set(QMLPLUGIN_SRC
13set(QMLPLUGIN_SRC
14 qlimitproxymodelqml.cpp
14 qlimitproxymodelqml.cpp
15 qsortfilterproxymodelqml.cpp
15 qsortfilterproxymodelqml.cpp
16+ relativetimeformatter.cpp
16 timeformatter.cpp
17 timeformatter.cpp
17 unitymenumodelpaths.cpp
18 unitymenumodelpaths.cpp
18 windowkeysfilter.cpp
19 windowkeysfilter.cpp
plugins/Utils/plugin.cpp+2
@@ -28,6 +28,7 @@
28// local
28// local
29#include "qlimitproxymodelqml.h"
29#include "qlimitproxymodelqml.h"
30#include "qsortfilterproxymodelqml.h"
30#include "qsortfilterproxymodelqml.h"
31+#include "relativetimeformatter.h"
31#include "timeformatter.h"
32#include "timeformatter.h"
32#include "unitymenumodelpaths.h"
33#include "unitymenumodelpaths.h"
33#include "windowkeysfilter.h"
34#include "windowkeysfilter.h"
@@ -44,6 +45,7 @@ void UtilsPlugin::registerTypes(const char *uri)
44 qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
45 qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
45 qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
46 qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
46 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
47 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
48+ qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
47}
49}
48
50
49void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
51void 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)
121 g_bus_get (G_BUS_TYPE_SYSTEM, priv->cancellable, got_bus, priv);
121 g_bus_get (G_BUS_TYPE_SYSTEM, priv->cancellable, got_bus, priv);
122}
122}
123
123
124+TimeFormatter::TimeFormatter(const QString &initialFormat, QObject *parent): TimeFormatter(parent)
125+{
126+ priv->format = initialFormat;
127+}
128+
124TimeFormatter::~TimeFormatter()
129TimeFormatter::~TimeFormatter()
125{
130{
126 if (priv->system_bus) {
131 if (priv->system_bus) {
@@ -177,7 +182,7 @@ QString TimeFormatter::formatTime() const
177}
182}
178
183
179GDateTimeFormatter::GDateTimeFormatter(QObject* parent)
184GDateTimeFormatter::GDateTimeFormatter(QObject* parent)
180−: TimeFormatter(parent)
185+: TimeFormatter("%d-%m-%Y %I:%M%p", parent)
181{
186{
182}
187}
183
188
plugins/Utils/timeformatter.h+3−1
@@ -33,7 +33,7 @@ public:
33 TimeFormatter(QObject *parent = 0);
33 TimeFormatter(QObject *parent = 0);
34 virtual ~TimeFormatter();
34 virtual ~TimeFormatter();
35
35
36− QString format() const;
36+ virtual QString format() const;
37 QString timeString() const;
37 QString timeString() const;
38 qint64 time() const;
38 qint64 time() const;
39
39
@@ -48,6 +48,8 @@ Q_SIGNALS:
48 void timeChanged(qint64 time);
48 void timeChanged(qint64 time);
49
49
50protected:
50protected:
51+ TimeFormatter(const QString &initialFormat, QObject *parent = 0);
52+
51 virtual QString formatTime() const;
53 virtual QString formatTime() const;
52
54
53private:
55private:
po/unity8.pot+29−25
@@ -8,7 +8,7 @@ msgid ""
8msgstr ""
8msgstr ""
9"Project-Id-Version: unity8\n"
9"Project-Id-Version: unity8\n"
10"Report-Msgid-Bugs-To: \n"
10"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"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -21,12 +21,12 @@ msgstr ""
21msgid "Password: "
21msgid "Password: "
22msgstr ""
22msgstr ""
23
23
24−#: plugins/Unity/Launcher/launcheritem.cpp:44
24+#: plugins/Unity/Launcher/launcheritem.cpp:45
25−#: plugins/Unity/Launcher/launcheritem.cpp:74
25+#: plugins/Unity/Launcher/launcheritem.cpp:91
26msgid "Pin shortcut"
26msgid "Pin shortcut"
27msgstr ""
27msgstr ""
28
28
29−#: plugins/Unity/Launcher/launcheritem.cpp:74
29+#: plugins/Unity/Launcher/launcheritem.cpp:91
30msgid "Unpin shortcut"
30msgid "Unpin shortcut"
31msgstr ""
31msgstr ""
32
32
@@ -157,15 +157,15 @@ msgstr ""
157msgid "Hello"
157msgid "Hello"
158msgstr ""
158msgstr ""
159
159
160−#: qml/Dash/GenericScopeView.qml:364 qml/Dash/GenericScopeView.qml:483
160+#: qml/Dash/GenericScopeView.qml:411 qml/Dash/GenericScopeView.qml:533
161msgid "See less"
161msgid "See less"
162msgstr ""
162msgstr ""
163
163
164−#: qml/Dash/GenericScopeView.qml:364
164+#: qml/Dash/GenericScopeView.qml:411
165msgid "See all"
165msgid "See all"
166msgstr ""
166msgstr ""
167
167
168−#: qml/Dash/GenericScopeView.qml:427 qml/Dash/PageHeader.qml:258
168+#: qml/Dash/GenericScopeView.qml:473 qml/Dash/PageHeader.qml:258
169#: qml/Panel/SearchIndicator.qml:27
169#: qml/Panel/SearchIndicator.qml:27
170msgid "Search"
170msgid "Search"
171msgstr ""
171msgstr ""
@@ -203,7 +203,7 @@ msgid "Send"
203msgstr ""
203msgstr ""
204
204
205#: qml/Dash/ScopesOverview.qml:206
205#: qml/Dash/ScopesOverview.qml:206
206−msgid "Manage Dash"
206+msgid "Manage Scopes"
207msgstr ""
207msgstr ""
208
208
209#: qml/Dash/ScopesOverview.qml:428
209#: qml/Dash/ScopesOverview.qml:428
@@ -266,27 +266,31 @@ msgstr ""
266msgid "Conference"
266msgid "Conference"
267msgstr ""
267msgstr ""
268
268
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
270msgid "In queue…"
274msgid "In queue…"
271msgstr ""
275msgstr ""
272
276
273−#: qml/Panel/Indicators/MenuItemFactory.qml:655
277+#: qml/Panel/Indicators/MenuItemFactory.qml:752
274msgid "Downloading"
278msgid "Downloading"
275msgstr ""
279msgstr ""
276
280
277−#: qml/Panel/Indicators/MenuItemFactory.qml:657
281+#: qml/Panel/Indicators/MenuItemFactory.qml:754
278msgid "Paused, tap to resume"
282msgid "Paused, tap to resume"
279msgstr ""
283msgstr ""
280
284
281−#: qml/Panel/Indicators/MenuItemFactory.qml:659
285+#: qml/Panel/Indicators/MenuItemFactory.qml:756
282msgid "Canceled"
286msgid "Canceled"
283msgstr ""
287msgstr ""
284
288
285−#: qml/Panel/Indicators/MenuItemFactory.qml:661
289+#: qml/Panel/Indicators/MenuItemFactory.qml:758
286msgid "Finished"
290msgid "Finished"
287msgstr ""
291msgstr ""
288
292
289−#: qml/Panel/Indicators/MenuItemFactory.qml:663
293+#: qml/Panel/Indicators/MenuItemFactory.qml:760
290msgid "Failed, tap to retry"
294msgid "Failed, tap to retry"
291msgstr ""
295msgstr ""
292
296
@@ -298,51 +302,51 @@ msgstr ""
298msgid "Roaming"
302msgid "Roaming"
299msgstr ""
303msgstr ""
300
304
301−#: qml/Shell.qml:292
305+#: qml/Shell.qml:320
302msgid "Enter your passphrase"
306msgid "Enter your passphrase"
303msgstr ""
307msgstr ""
304
308
305−#: qml/Shell.qml:293
309+#: qml/Shell.qml:321
306msgid "Sorry, incorrect passphrase"
310msgid "Sorry, incorrect passphrase"
307msgstr ""
311msgstr ""
308
312
309−#: qml/Shell.qml:295
313+#: qml/Shell.qml:323
310msgid "Enter your passcode"
314msgid "Enter your passcode"
311msgstr ""
315msgstr ""
312
316
313−#: qml/Shell.qml:296
317+#: qml/Shell.qml:324
314msgid "Sorry, incorrect passcode"
318msgid "Sorry, incorrect passcode"
315msgstr ""
319msgstr ""
316
320
317−#: qml/Shell.qml:299
321+#: qml/Shell.qml:327
318#, qt-format
322#, qt-format
319msgid "Enter your %1"
323msgid "Enter your %1"
320msgstr ""
324msgstr ""
321
325
322−#: qml/Shell.qml:300
326+#: qml/Shell.qml:328
323#, qt-format
327#, qt-format
324msgid "Sorry, incorrect %1"
328msgid "Sorry, incorrect %1"
325msgstr ""
329msgstr ""
326
330
327−#: qml/Shell.qml:335
331+#: qml/Shell.qml:363
328msgid "Sorry, incorrect passphrase."
332msgid "Sorry, incorrect passphrase."
329msgstr ""
333msgstr ""
330
334
331−#: qml/Shell.qml:336
335+#: qml/Shell.qml:364
332msgid "Sorry, incorrect passcode."
336msgid "Sorry, incorrect passcode."
333msgstr ""
337msgstr ""
334
338
335−#: qml/Shell.qml:337
339+#: qml/Shell.qml:365
336msgid "This will be your last attempt."
340msgid "This will be your last attempt."
337msgstr ""
341msgstr ""
338
342
339−#: qml/Shell.qml:339
343+#: qml/Shell.qml:367
340msgid ""
344msgid ""
341"If passphrase is entered incorrectly, your phone will conduct a factory "
345"If passphrase is entered incorrectly, your phone will conduct a factory "
342"reset and all personal data will be deleted."
346"reset and all personal data will be deleted."
343msgstr ""
347msgstr ""
344
348
345−#: qml/Shell.qml:340
349+#: qml/Shell.qml:368
346msgid ""
350msgid ""
347"If passcode is entered incorrectly, your phone will conduct a factory reset "
351"If passcode is entered incorrectly, your phone will conduct a factory reset "
348"and all personal data will be deleted."
352"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 {
84 indicators.state = "locked";
84 indicators.state = "locked";
85 }
85 }
86 }
86 }
87−
88− if (enableRelease && revealProgress === 0) {
89− menuContent.releaseContent();
90− }
91 }
87 }
92
88
93 function calculateCurrentItem(xValue, useBuffer) {
89 function calculateCurrentItem(xValue, useBuffer) {
@@ -189,8 +185,7 @@ Showable {
189 }
185 }
190 indicatorsModel: visibleIndicators.model
186 indicatorsModel: visibleIndicators.model
191 visible: indicators.partiallyOpened || indicators.fullyOpened
187 visible: indicators.partiallyOpened || indicators.fullyOpened
192− clip: !indicators.fullyOpened
188+ clip: indicators.partiallyOpened
193− activeHeader: indicators.state == "hint" || indicators.state == "reveal"
194 enabled: contentEnabled
189 enabled: contentEnabled
195
190
196 //small shadow gradient at bottom of menu
191 //small shadow gradient at bottom of menu
@@ -212,7 +207,7 @@ Showable {
212 Rectangle {
207 Rectangle {
213 id: handle
208 id: handle
214
209
215− color: menuContent.backgroundColor
210+ color: menuContent.color
216
211
217 anchors {
212 anchors {
218 left: parent.left
213 left: parent.left
@@ -358,12 +353,6 @@ Showable {
358 maxTotalDragDistance: openedHeight - panelHeight
353 maxTotalDragDistance: openedHeight - panelHeight
359 distanceThreshold: panelHeight
354 distanceThreshold: panelHeight
360
355
361− onStatusChanged: {
362− if (status === DirectionalDragArea.Recognized) {
363− menuContent.activateContent();
364− }
365− }
366−
367 onTapped: showTapped(Qt.point(touchSceneX, touchSceneY));
356 onTapped: showTapped(Qt.point(touchSceneX, touchSceneY));
368 }
357 }
369
358
qml/Panel/Indicators/DefaultIndicatorPage.qml+8−49
@@ -26,14 +26,12 @@ IndicatorBase {
26 id: main
26 id: main
27
27
28 //const
28 //const
29− property bool contentActive: false
30 property string title: rootActionState.title
29 property string title: rootActionState.title
31− property alias emptyText: emptyLabel.text
32 property alias highlightFollowsCurrentItem : mainMenu.highlightFollowsCurrentItem
30 property alias highlightFollowsCurrentItem : mainMenu.highlightFollowsCurrentItem
33
31
34 Indicators.UnityMenuModelStack {
32 Indicators.UnityMenuModelStack {
35 id: menuStack
33 id: menuStack
36− head: contentActive ? main.menuModel : null
34+ head: main.menuModel
37
35
38 property var rootMenu: null
36 property var rootMenu: null
39
37
@@ -88,11 +86,13 @@ IndicatorBase {
88 }
86 }
89 }
87 }
90
88
91− // Ensure all delegates are cached in order to improve smoothness of scrolling
89+ // Don't load all the delegates (only max of 3 pages worth -1/0/+1)
92− cacheBuffer: 10000
90+ cacheBuffer: Math.max(height * 3, units.gu(70))
93
91
94 // Only allow flicking if the content doesn't fit on the page
92 // Only allow flicking if the content doesn't fit on the page
95 interactive: contentHeight > height
93 interactive: contentHeight > height
94+ // FIXME - https://bugreports.qt-project.org/browse/QTBUG-41207
95+ boundsBehavior: Flickable.StopAtBounds
96
96
97 property int selectedIndex: -1
97 property int selectedIndex: -1
98 property bool blockCurrentIndexChange: false
98 property bool blockCurrentIndexChange: false
@@ -125,17 +125,12 @@ IndicatorBase {
125 delegate: Loader {
125 delegate: Loader {
126 id: loader
126 id: loader
127 objectName: "menuItem" + index
127 objectName: "menuItem" + index
128− asynchronous: false
128+ asynchronous: true
129+ width: ListView.view.width
129 visible: status == Loader.Ready
130 visible: status == Loader.Ready
130
131
131 property int modelIndex: index
132 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)
139
134
140 onLoaded: {
135 onLoaded: {
141 if (item.hasOwnProperty("selected")) {
136 if (item.hasOwnProperty("selected")) {
@@ -180,44 +175,8 @@ IndicatorBase {
180 menuModel: mainMenu.model ? mainMenu.model : null
175 menuModel: mainMenu.model ? mainMenu.model : null
181 }
176 }
182
177
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−
218 function reset()
178 function reset()
219 {
179 {
220− mainMenu.selectedIndex = -1;
221 mainMenu.positionViewAtBeginning();
180 mainMenu.positionViewAtBeginning();
222 }
181 }
223}
182}
qml/Panel/Indicators/IndicatorDelegate.qml+2−10
@@ -20,14 +20,6 @@
20import QtQuick 2.0
20import QtQuick 2.0
21
21
22IndicatorBase {
22IndicatorBase {
23− enabled: false
24−
25− onRootActionStateChanged: {
26− if (rootActionState == undefined) {
27− enabled = false;
28− return;
29− }
30−
31− enabled = rootActionState.visible;
23+ enabled: rootActionState.visible
32− }
24+ property string title: rootActionState.title
33}
25}
qml/Panel/Indicators/MenuItemFactory.qml+158−47
@@ -19,11 +19,10 @@
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21import Ubuntu.Settings.Menus 0.1 as Menus
21import Ubuntu.Settings.Menus 0.1 as Menus
22−import Ubuntu.Settings.Components 0.1 as SettingsComponents
23import QMenuModel 0.1
22import QMenuModel 0.1
24import Utils 0.1 as Utils
23import Utils 0.1 as Utils
25import Ubuntu.Components.ListItems 0.1 as ListItems
24import Ubuntu.Components.ListItems 0.1 as ListItems
26−import Ubuntu.Components 0.1
25+import Ubuntu.Components 1.1
27
26
28Item {
27Item {
29 id: menuFactory
28 id: menuFactory
@@ -32,32 +31,38 @@ Item {
32 property var menuModel: null
31 property var menuModel: null
33
32
34 property var _map: {
33 property var _map: {
34+ "default": {
35− "unity.widgets.systemsettings.tablet.volumecontrol" : sliderMenu,
35+ "unity.widgets.systemsettings.tablet.volumecontrol" : sliderMenu,
36− "unity.widgets.systemsettings.tablet.switch" : switchMenu,
36+ "unity.widgets.systemsettings.tablet.switch" : switchMenu,
37−
37+
38− "com.canonical.indicator.button" : buttonMenu,
38+ "com.canonical.indicator.button" : buttonMenu,
39− "com.canonical.indicator.div" : separatorMenu,
39+ "com.canonical.indicator.div" : separatorMenu,
40− "com.canonical.indicator.section" : sectionMenu,
40+ "com.canonical.indicator.section" : sectionMenu,
41− "com.canonical.indicator.progress" : progressMenu,
41+ "com.canonical.indicator.progress" : progressMenu,
42− "com.canonical.indicator.slider" : sliderMenu,
42+ "com.canonical.indicator.slider" : sliderMenu,
43− "com.canonical.indicator.switch" : switchMenu,
43+ "com.canonical.indicator.switch" : switchMenu,
44− "com.canonical.indicator.alarm" : alarmMenu,
44+ "com.canonical.indicator.alarm" : alarmMenu,
45− "com.canonical.indicator.appointment" : appointmentMenu,
45+ "com.canonical.indicator.appointment" : appointmentMenu,
46− "com.canonical.indicator.transfer" : transferMenu,
46+ "com.canonical.indicator.transfer" : transferMenu,
47− "com.canonical.indicator.button-section" : buttonSectionMenu,
47+ "com.canonical.indicator.button-section" : buttonSectionMenu,
48−
48+ "com.canonical.indicator.link" : linkMenu,
49+
49− "com.canonical.indicator.messages.messageitem" : messageItem,
50+ "com.canonical.indicator.messages.messageitem" : messageItem,
50− "com.canonical.indicator.messages.sourceitem" : groupedMessage,
51+ "com.canonical.indicator.messages.sourceitem" : groupedMessage,
51−
52+
52− "com.canonical.unity.slider" : sliderMenu,
53+ "com.canonical.unity.slider" : sliderMenu,
53− "com.canonical.unity.switch" : switchMenu,
54+ "com.canonical.unity.switch" : switchMenu,
54−
55+
55− "com.canonical.unity.media-player" : mediaPayerMenu,
56+ "com.canonical.unity.media-player" : mediaPayerMenu,
56− "com.canonical.unity.playback-item" : playbackItemMenu,
57+ "com.canonical.unity.playback-item" : playbackItemMenu,
57−
58+
58− "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
59+ "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
59− "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
60+ "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
60− "com.canonical.indicator.network.modeminfoitem" : modeminfoitem,
61+ "com.canonical.indicator.network.modeminfoitem" : modeminfoitem,
62+ },
63+ "indicator-messages" : {
64+ "com.canonical.indicator.button" : messagesButtonMenu
65+ }
61 }
66 }
62
67
63 function getExtendedProperty(object, propertyName, defaultValue) {
68 function getExtendedProperty(object, propertyName, defaultValue) {
@@ -100,6 +105,7 @@ Item {
100 return maximum;
105 return maximum;
101 }
106 }
102 enabled: menuData && menuData.sensitive || false
107 enabled: menuData && menuData.sensitive || false
108+ highlightWhenPressed: false
103
109
104 onMenuModelChanged: {
110 onMenuModelChanged: {
105 loadAttributes();
111 loadAttributes();
@@ -138,12 +144,43 @@ Item {
138
144
139 buttonText: menuData && menuData.label || ""
145 buttonText: menuData && menuData.label || ""
140 enabled: menuData && menuData.sensitive || false
146 enabled: menuData && menuData.sensitive || false
147+ highlightWhenPressed: false
141
148
142 onTriggered: {
149 onTriggered: {
143 menuModel.activate(menuIndex);
150 menuModel.activate(menuIndex);
144 }
151 }
145 }
152 }
146 }
153 }
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+
147 Component {
184 Component {
148 id: sectionMenu;
185 id: sectionMenu;
149
186
@@ -169,6 +206,7 @@ Item {
169 iconSource: menuData && menuData.icon || ""
206 iconSource: menuData && menuData.icon || ""
170 value : menuData && menuData.actionState || 0.0
207 value : menuData && menuData.actionState || 0.0
171 enabled: menuData && menuData.sensitive || false
208 enabled: menuData && menuData.sensitive || false
209+ highlightWhenPressed: false
172 }
210 }
173 }
211 }
174
212
@@ -183,10 +221,58 @@ Item {
183 text: menuData && menuData.label || ""
221 text: menuData && menuData.label || ""
184 iconSource: menuData && menuData.icon || ""
222 iconSource: menuData && menuData.icon || ""
185 enabled: menuData && menuData.sensitive || false
223 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
186
259
187 onTriggered: {
260 onTriggered: {
188 menuModel.activate(menuIndex);
261 menuModel.activate(menuIndex);
189 }
262 }
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+ }
190 }
276 }
191 }
277 }
192
278
@@ -201,6 +287,7 @@ Item {
201 text: menuData && menuData.label || ""
287 text: menuData && menuData.label || ""
202 enabled: menuData && menuData.sensitive || false
288 enabled: menuData && menuData.sensitive || false
203 checked: menuData && menuData.isToggled || false
289 checked: menuData && menuData.isToggled || false
290+ highlightWhenPressed: false
204
291
205 onTriggered: {
292 onTriggered: {
206 menuModel.activate(menuIndex);
293 menuModel.activate(menuIndex);
@@ -208,7 +295,6 @@ Item {
208 }
295 }
209 }
296 }
210
297
211−
212 Component {
298 Component {
213 id: switchMenu;
299 id: switchMenu;
214
300
@@ -221,6 +307,7 @@ Item {
221 iconSource: menuData && menuData.icon || ""
307 iconSource: menuData && menuData.icon || ""
222 enabled: menuData && menuData.sensitive || false
308 enabled: menuData && menuData.sensitive || false
223 checked: menuData && menuData.isToggled || false
309 checked: menuData && menuData.isToggled || false
310+ highlightWhenPressed: false
224
311
225 onTriggered: {
312 onTriggered: {
226 menuModel.activate(menuIndex);
313 menuModel.activate(menuIndex);
@@ -247,6 +334,7 @@ Item {
247 iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
334 iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
248 time: timeFormatter.timeString
335 time: timeFormatter.timeString
249 enabled: menuData && menuData.sensitive || false
336 enabled: menuData && menuData.sensitive || false
337+ highlightWhenPressed: false
250
338
251 onMenuModelChanged: {
339 onMenuModelChanged: {
252 loadAttributes();
340 loadAttributes();
@@ -286,6 +374,7 @@ Item {
286 time: timeFormatter.timeString
374 time: timeFormatter.timeString
287 eventColor: getExtendedProperty(extendedData, "xCanonicalColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
375 eventColor: getExtendedProperty(extendedData, "xCanonicalColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
288 enabled: menuData && menuData.sensitive || false
376 enabled: menuData && menuData.sensitive || false
377+ highlightWhenPressed: false
289
378
290 onMenuModelChanged: {
379 onMenuModelChanged: {
291 loadAttributes();
380 loadAttributes();
@@ -351,10 +440,11 @@ Item {
351
440
352 text: menuData && menuData.label || ""
441 text: menuData && menuData.label || ""
353 enabled: menuData && menuData.sensitive || false
442 enabled: menuData && menuData.sensitive || false
354− checked: menuData && menuData.isToggled || false
443+ active: menuData && menuData.isToggled || false
355 secure: getExtendedProperty(extendedData, "xCanonicalWifiApIsSecure", false)
444 secure: getExtendedProperty(extendedData, "xCanonicalWifiApIsSecure", false)
356 adHoc: getExtendedProperty(extendedData, "xCanonicalWifiApIsAdhoc", false)
445 adHoc: getExtendedProperty(extendedData, "xCanonicalWifiApIsAdhoc", false)
357 signalStrength: strengthAction.valid ? strengthAction.state : 0
446 signalStrength: strengthAction.valid ? strengthAction.state : 0
447+ highlightWhenPressed: false
358
448
359 onMenuModelChanged: {
449 onMenuModelChanged: {
360 loadAttributes();
450 loadAttributes();
@@ -383,6 +473,7 @@ Item {
383 property var menuModel: menuFactory.menuModel
473 property var menuModel: menuFactory.menuModel
384 property int menuIndex: -1
474 property int menuIndex: -1
385 property var extendedData: menuData && menuData.ext || undefined
475 property var extendedData: menuData && menuData.ext || undefined
476+ highlightWhenPressed: false
386
477
387 property var statusLabelAction: UnityMenuAction {
478 property var statusLabelAction: UnityMenuAction {
388 model: menuModel
479 model: menuModel
@@ -468,9 +559,10 @@ Item {
468 property var extendedData: menuData && menuData.ext || undefined
559 property var extendedData: menuData && menuData.ext || undefined
469
560
470 text: menuData && menuData.label || ""
561 text: menuData && menuData.label || ""
471− iconSource: getExtendedProperty(extendedData, "icon", "qrc:/indicators/artwork/messaging/default_app.svg")
562+ iconSource: getExtendedProperty(extendedData, "icon", "image://theme/message")
472 count: menuData && menuData.actionState.length > 0 ? menuData.actionState[0] : "0"
563 count: menuData && menuData.actionState.length > 0 ? menuData.actionState[0] : "0"
473 enabled: menuData && menuData.sensitive || false
564 enabled: menuData && menuData.sensitive || false
565+ highlightWhenPressed: false
474 removable: true
566 removable: true
475
567
476 onMenuModelChanged: {
568 onMenuModelChanged: {
@@ -502,17 +594,20 @@ Item {
502 property var menuModel: menuFactory.menuModel
594 property var menuModel: menuFactory.menuModel
503 property int menuIndex: -1
595 property int menuIndex: -1
504 property var actionState: menuData && menuData.actionState || undefined
596 property var actionState: menuData && menuData.actionState || undefined
597+ property bool running: getExtendedProperty(actionState, "running", false)
505
598
506− playerIcon: menuData && menuData.icon || ""
599+ playerIcon: menuData && menuData.icon || "image://theme/stock_music"
507− playerName: menuData && menuData.label || ""
600+ playerName: menuData && menuData.label || i18n.tr("Nothing is playing")
508
601
509− albumArt: getExtendedProperty(actionState, "art-url", "")
602+ albumArt: getExtendedProperty(actionState, "art-url", "image://theme/stock_music")
510− song: getExtendedProperty(actionState, "title", "unknown")
603+ song: getExtendedProperty(actionState, "title", "")
511− artist: getExtendedProperty(actionState, "artist", "unknown")
604+ artist: getExtendedProperty(actionState, "artist", "")
512− album: getExtendedProperty(actionState, "album", "unknown")
605+ album: getExtendedProperty(actionState, "album", "")
513− running: getExtendedProperty(actionState, "running", false)
606+ showTrack: running && (state == "Playing" || state == "Paused")
514 state: getExtendedProperty(actionState, "state", "")
607 state: getExtendedProperty(actionState, "state", "")
515 enabled: menuData && menuData.sensitive || false
608 enabled: menuData && menuData.sensitive || false
609+ highlightWhenPressed: false
610+ showDivider: false
516
611
517 onTriggered: {
612 onTriggered: {
518 model.activate(modelIndex);
613 model.activate(modelIndex);
@@ -551,6 +646,7 @@ Item {
551 canGoNext: nextAction.valid
646 canGoNext: nextAction.valid
552 canGoPrevious: previousAction.valid
647 canGoPrevious: previousAction.valid
553 enabled: menuData && menuData.sensitive || false
648 enabled: menuData && menuData.sensitive || false
649+ highlightWhenPressed: false
554
650
555 onPlay: {
651 onPlay: {
556 playAction.activate();
652 playAction.activate();
@@ -590,9 +686,10 @@ Item {
590 property var uid: getExtendedProperty(extendedData, "xCanonicalUid", undefined)
686 property var uid: getExtendedProperty(extendedData, "xCanonicalUid", undefined)
591
687
592 text: menuData && menuData.label || ""
688 text: menuData && menuData.label || ""
593− iconSource: menuData && menuData.icon || ""
689+ iconSource: menuData && menuData.icon || "image://theme/transfer-none"
594 maximum: 1.0
690 maximum: 1.0
595 enabled: menuData && menuData.sensitive || false
691 enabled: menuData && menuData.sensitive || false
692+ highlightWhenPressed: false
596 removable: true
693 removable: true
597 confirmRemoval: true
694 confirmRemoval: true
598
695
@@ -688,7 +785,7 @@ Item {
688 Component {
785 Component {
689 id: buttonSectionMenu;
786 id: buttonSectionMenu;
690
787
691− ListItems.Standard {
788+ Menus.StandardMenu {
692 objectName: "buttonSectionMenu"
789 objectName: "buttonSectionMenu"
693 property QtObject menuData: null
790 property QtObject menuData: null
694 property var menuModel: menuFactory.menuModel
791 property var menuModel: menuFactory.menuModel
@@ -697,8 +794,9 @@ Item {
697
794
698 iconSource: menuData && menuData.icon || ""
795 iconSource: menuData && menuData.icon || ""
699 enabled: menuData && menuData.sensitive || false
796 enabled: menuData && menuData.sensitive || false
797+ highlightWhenPressed: false
700 text: menuData && menuData.label || ""
798 text: menuData && menuData.label || ""
701− showDivider: false
799+ foregroundColor: Theme.palette.normal.backgroundText
702
800
703 onMenuModelChanged: {
801 onMenuModelChanged: {
704 loadAttributes();
802 loadAttributes();
@@ -711,22 +809,35 @@ Item {
711 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-extra-label': 'string'});
809 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-extra-label': 'string'});
712 }
810 }
713
811
714− control: Button {
812+ component: Component {
813+ Button {
814+ objectName: "buttonSectionMenuControl"
715− text: getExtendedProperty(extendedData, "xCanonicalExtraLabel", "")
815+ text: getExtendedProperty(extendedData, "xCanonicalExtraLabel", "")
716
816
717− onClicked: {
817+ onClicked: {
718− menuModel.activate(menuIndex);
818+ menuModel.activate(menuIndex);
819+ }
719 }
820 }
720 }
821 }
721 }
822 }
722 }
823 }
723
824
724− function load(modelData) {
825+ function load(modelData, context) {
725 if (modelData.type !== undefined) {
826 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+ }
727 if (component !== undefined) {
837 if (component !== undefined) {
728 return component;
838 return component;
729 }
839 }
840+ console.debug("Don't know how to make " + modelData.type + " for " + context);
730 }
841 }
731 if (modelData.isCheck || modelData.isRadio) {
842 if (modelData.isCheck || modelData.isRadio) {
732 return checkableMenu;
843 return checkableMenu;
qml/Panel/Indicators/MessageMenuItemFactory.qml+156−171
@@ -24,8 +24,9 @@ import Ubuntu.Settings.Menus 0.1 as Menus
24import QMenuModel 0.1 as QMenuModel
24import QMenuModel 0.1 as QMenuModel
25import Utils 0.1 as Utils
25import Utils 0.1 as Utils
26
26
27−Item {
27+Loader {
28 id: messageFactoryItem
28 id: messageFactoryItem
29+ objectName: "messageItem"
29 property var menuModel: null
30 property var menuModel: null
30 property QtObject menuData: null
31 property QtObject menuData: null
31 property int menuIndex: -1
32 property int menuIndex: -1
@@ -38,9 +39,8 @@ Item {
38 property var actionsDescription: getExtendedProperty(extendedData, "xCanonicalMessageActions", undefined)
39 property var actionsDescription: getExtendedProperty(extendedData, "xCanonicalMessageActions", undefined)
39
40
40 // TODO - bug #1260728
41 // TODO - bug #1260728
41− property var timeFormatter: Utils.TimeFormatter {
42+ property var timeFormatter: Utils.RelativeTimeFormatter {
42− time: getExtendedProperty(extendedData, "xCanonicalTime", 0)
43+ time: getExtendedProperty(extendedData, "xCanonicalTime", 0) / 1000000
43− format: "hh:mm - MMM dd"
44 }
44 }
45
45
46 onMenuModelChanged: {
46 onMenuModelChanged: {
@@ -50,6 +50,32 @@ Item {
50 loadAttributes();
50 loadAttributes();
51 }
51 }
52
52
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+
53 function loadAttributes() {
79 function loadAttributes() {
54 if (!menuModel || menuIndex == -1) return;
80 if (!menuModel || menuIndex == -1) return;
55 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-time': 'int64',
81 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-time': 'int64',
@@ -66,188 +92,147 @@ Item {
66 return defaultValue;
92 return defaultValue;
67 }
93 }
68
94
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 {
95+ Component {
79− id: simpleTextMessage
96+ id: simpleMessage
80−
97+
81− Menus.SimpleTextMessageMenu {
98+ Menus.SimpleMessageMenu {
82− id: message
99+ id: message
83− objectName: "simpleTextMessage"
100+ objectName: "simpleTextMessage"
84− // text
101+ // text
85− title: menuData && menuData.label || ""
102+ title: menuData && menuData.label || ""
86− time: timeFormatter.timeString
103+ time: timeFormatter.timeString
87− message: getExtendedProperty(extendedData, "xCanonicalText", "")
104+ body: getExtendedProperty(extendedData, "xCanonicalText", "")
88− // icons
105+ // icons
89− avatar: getExtendedProperty(extendedData, "icon", "qrc:/indicators/artwork/messaging/default_contact.png")
106+ avatar: getExtendedProperty(extendedData, "icon", "image://theme/contact")
90− appIcon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "qrc:/indicators/artwork/messaging/default_app.svg")
107+ icon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "image://theme/message")
91− // actions
108+ // actions
92− enabled: menuData && menuData.sensitive || false
109+ enabled: menuData && menuData.sensitive || false
93− removable: !selected
110+ removable: !selected
111+ confirmRemoval: true
94− selected: messageFactoryItem.selected
112+ selected: messageFactoryItem.selected
95−
113+
96− onAppActivated: {
114+ onIconActivated: {
97− menuModel.activate(menuIndex, true);
115+ menuModel.activate(menuIndex, true);
98− }
116+ }
99− onDismissed: {
117+ onDismissed: {
100− menuModel.activate(menuIndex, false);
118+ menuModel.activate(menuIndex, false);
101− }
119+ }
102− onClicked: {
120+ onTriggered: {
103− if (selected) {
121+ if (selected) {
104− menuDeselected();
122+ menuDeselected();
105− } else {
123+ } else {
106− menuSelected();
124+ menuSelected();
107− }
108− }
109−
110− backgroundIndicator: RemoveBackground {
111− state: message.swipingState
112 }
125 }
113 }
126 }
114 }
127 }
128+ }
115
129
116− Component {
130+ Component {
117− id: textMessage
118−
119− Menus.TextMessageMenu {
120− id: message
121− objectName: "textMessage"
131+ id: textMessage
122− property var replyActionDescription: actionsDescription && actionsDescription.length > 0 ? actionsDescription[0] : undefined
123
132
124− property var replyAction: QMenuModel.UnityMenuAction {
133+ Menus.TextMessageMenu {
125− model: menuModel
134+ id: message
126− index: menuIndex
135+ objectName: "textMessage"
127− name: getExtendedProperty(replyActionDescription, "name", "")
136+ property var replyActionDescription: actionsDescription && actionsDescription.length > 0 ? actionsDescription[0] : undefined
128− }
129
137
130− // text
138+ property var replyAction: QMenuModel.UnityMenuAction {
131− title: menuData && menuData.label || ""
139+ model: menuModel
132− time: timeFormatter.timeString
140+ index: menuIndex
133− message: getExtendedProperty(extendedData, "xCanonicalText", "")
134− replyButtonText: getExtendedProperty(replyActionDescription, "label", "Send")
141+ name: getExtendedProperty(replyActionDescription, "name", "")
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− }
164 }
142 }
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− }
222
143
223− backgroundIndicator: RemoveBackground {
144+ // text
224− state: message.swipingState
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();
225 }
174 }
226 }
175 }
227 }
176 }
228 }
177 }
229
178
230− function loadMessage(actions)
179+ Component {
231− {
180+ id: snapDecision
232− var parameterType = ""
181+
233− for (var actIndex in actions) {
182+ Menus.SnapDecisionMenu {
234− var desc = actions[actIndex];
183+ id: message
235− if (desc["parameter-type"] !== undefined) {
184+ objectName: "snapDecision"
236− parameterType += desc["parameter-type"];
185+ property var activateActionDescription: actionsDescription && actionsDescription.length > 0 ? actionsDescription[0] : undefined
237− } else {
186+ property var replyActionDescription: actionsDescription && actionsDescription.length > 1 ? actionsDescription[1] : undefined
238− parameterType += "_";
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", "")
239 }
197 }
240− }
241
198
242− if (parameterType === "") {
199+ // text
243− return simpleTextMessage;
200+ title: menuData && menuData.label || ""
244− } else if (parameterType === "s") {
201+ time: timeFormatter.timeString
245− return textMessage;
202+ body: getExtendedProperty(extendedData, "xCanonicalText", "")
246− } else if (parameterType === "_s") {
203+ actionButtonText: getExtendedProperty(activateActionDescription, "label", "Call back")
247− return snapDecision;
204+ replyButtonText: getExtendedProperty(replyActionDescription, "label", "Send")
248− } else {
205+ // icons
249− console.debug("Unknown paramater type: " + parameterType);
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+ }
250 }
236 }
251− return undefined;
252 }
237 }
253}
238}
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 {
25 color: "black"
25 color: "black"
26 id: root
26 id: root
27
27
28+ Component.onCompleted: {
29+ Theme.name = "Ubuntu.Components.Themes.SuruGradient"
30+ }
31+
28 PageStack {
32 PageStack {
29 id: pages
33 id: pages
30 anchors.fill: parent
34 anchors.fill: parent
qml/Panel/MenuContent.qml+89−113
@@ -15,122 +15,64 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18+import QtQuick.Layouts 1.1
18−import Ubuntu.Components 0.1
19+import Ubuntu.Components 1.1
19import Unity.Indicators 0.1 as Indicators
20import Unity.Indicators 0.1 as Indicators
20import Utils 0.1
21import Utils 0.1
21import "../Components"
22import "../Components"
23+import "Indicators"
22
24
23−// FIXME : We dont want to use MainView.
25+Rectangle {
24−// Need a regular Item which can have tabs with local header.
25−// https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1211704
26−MainView {
27 id: content
26 id: content
28
27
29 property QtObject indicatorsModel: null
28 property QtObject indicatorsModel: null
30− property bool __contentActive: false
31− readonly property int currentMenuIndex: tabs.selectedTabIndex
29+ readonly property alias currentMenuIndex: listViewHeader.currentIndex
32− backgroundColor: "#221e1c" // FIXME not in palette yet
30+ color: "#221e1c" // FIXME not in palette yet
33− property int contentReleaseInterval: 20000
34− property bool activeHeader: false
35− property real headerHeight: tabs.tabBar.height
31+ property real headerHeight: listViewHeader.height
36
32
37 width: units.gu(40)
33 width: units.gu(40)
38 height: units.gu(42)
34 height: units.gu(42)
39
35
40 function setCurrentMenuIndex(index, animate) {
36 function setCurrentMenuIndex(index, animate) {
41− if (tabs.selectedTabIndex !== index) {
37+ // FIXME - https://bugreports.qt-project.org/browse/QTBUG-41229
42− if (tabs.selectedTabIndex === -1 || !animate) {
38+ listViewHeader.currentIndex = -1;
43− tabs.tabBar.animate = false;
39+ listViewHeader.currentIndex = index;
44− }
45− tabs.selectedTabIndex = index;
46− tabs.tabBar.animate = true;
47− }
48 }
40 }
49
41
50− function activateContent() {
42+ ListView {
51− contentReleaseTimer.stop();
43+ id: listViewHeader
52− __contentActive = true;
44+ objectName: "indicatorsHeaderListView"
53− }
45+ model: content.indicatorsModel
46+ clip: true
54
47
55− function releaseContent() {
48+ anchors {
56− if (__contentActive) {
49+ left: parent.left
57− contentReleaseTimer.restart();
50+ right: parent.right
58 }
51 }
59− }
52+ height: units.gu(8.5)
60−
53+
61− onActiveHeaderChanged: {
54+ highlightFollowsCurrentItem: true
62− tabs.tabBar.selectionMode = activeHeader;
55+ highlightMoveDuration: 0
63− tabs.tabBar.alwaysSelectionMode = activeHeader;
56+
64− }
57+ orientation: ListView.Horizontal
65−
58+ snapMode: ListView.SnapOneItem
66− Tabs {
59+ highlightRangeMode: ListView.StrictlyEnforceRange
67− id: tabs
60+ boundsBehavior: Flickable.StopAtBounds
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
61+ // Load all the indicator menus (a big number)
108− if (!visible && item && item["reset"]) {
62+ cacheBuffer: 1073741823
109− item.reset()
63+
110− }
64+ delegate: Header {
111− }
65+ width: ListView.view.width
112−
66+ height: implicitHeight
113− onLoaded: {
67+
68+ title: indicatorDelegate.title !== "" ? indicatorDelegate.title : identifier
69+
70+ IndicatorDelegate {
71+ id: indicatorDelegate
72+ Component.onCompleted: {
114− for(var pName in indicatorProperties) {
73+ for(var pName in indicatorProperties) {
115− if (item.hasOwnProperty(pName)) {
74+ if (indicatorDelegate.hasOwnProperty(pName)) {
116− item[pName] = indicatorProperties[pName]
75+ indicatorDelegate[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"
134 }
76 }
135 }
77 }
136 }
78 }
@@ -138,20 +80,54 @@ MainView {
138 }
80 }
139 }
81 }
140
82
141− Timer {
83+ ListView {
142− id: contentReleaseTimer
84+ id: listViewContent
143−
85+ objectName: "indicatorsContentListView"
144− interval: contentReleaseInterval
86+ anchors {
145− onTriggered: {
87+ left: parent.left
146− content.__contentActive = false;
88+ right: parent.right
147− menuActivator.clear();
89+ top: listViewHeader.bottom
90+ bottom: parent.bottom
148 }
91 }
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+ }
149− }
116+ }
150
117
151− Indicators.MenuContentActivator {
118+ onLoaded: {
152− id: menuActivator
119+ for(var pName in indicatorProperties) {
153− running: content.__contentActive
120+ if (item.hasOwnProperty(pName)) {
154− baseIndex: content.currentMenuIndex
121+ item[pName] = indicatorProperties[pName]
155− count: indicatorsModel.count
122+ }
123+ }
124+ }
125+
126+ Binding {
127+ target: loader.item
128+ property: "objectName"
129+ value: identifier + "-page"
130+ }
131+ }
156 }
132 }
157}
133}
tests/mocks/Unity/Indicators/FakeMenuPage.qml−10
@@ -19,16 +19,6 @@ import QtQuick 2.0
19Flickable {
19Flickable {
20 objectName: "fakeMenuPlugin"
20 objectName: "fakeMenuPlugin"
21 // Make it compatible with the PluginItem interface
21 // 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− }
32 function reset() {
22 function reset() {
33 if (shell != undefined && shell.indicator_status != undefined) {
23 if (shell != undefined && shell.indicator_status != undefined) {
34 shell.indicator_status[objectName].reset++;
24 shell.indicator_status[objectName].reset++;
tests/plugins/Utils/CMakeLists.txt+1−1
@@ -7,7 +7,7 @@ include_directories(
7macro(run_tests)
7macro(run_tests)
8 set(_test_list "")
8 set(_test_list "")
9 foreach(_test ${ARGN})
9 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
11 -o ${CMAKE_BINARY_DIR}/${_test}.xml,xunitxml
11 -o ${CMAKE_BINARY_DIR}/${_test}.xml,xunitxml
12 -o -,txt)
12 -o -,txt)
13 add_test(NAME ${_test} COMMAND ${testCommand})
13 add_test(NAME ${_test} COMMAND ${testCommand})
tests/plugins/Utils/timeformattertest.cpp+104−7
@@ -17,6 +17,9 @@
17 */
17 */
18
18
19#include "timeformatter.h"
19#include "timeformatter.h"
20+#include "relativetimeformatter.h"
21+
22+#include <locale.h>
20
23
21#include <QtTest>
24#include <QtTest>
22#include <QDebug>
25#include <QDebug>
@@ -24,8 +27,24 @@
24class TimeFormatterTest : public QObject
27class TimeFormatterTest : public QObject
25{
28{
26 Q_OBJECT
29 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+ }
28
44
45+ }
46+
47+private Q_SLOTS:
29 void initTestCase()
48 void initTestCase()
30 {
49 {
31 setenv("UNITY_TEST_ENV", "1", 1);
50 setenv("UNITY_TEST_ENV", "1", 1);
@@ -36,12 +55,12 @@ private Q_SLOTS:
36 unsetenv("UNITY_TEST_ENV");
55 unsetenv("UNITY_TEST_ENV");
37 }
56 }
38
57
39− void testFormat()
58+ void testTimeFormatter_Format_data() { addTestData(QList<int>() << 0); }
59+ void testTimeFormatter_Format()
40 {
60 {
61+ QFETCH(QDateTime, time);
41 const QString format = "hh:mm dd.MM.yy";
62 const QString format = "hh:mm dd.MM.yy";
42
63
43− QDateTime time = QDateTime::currentDateTime();
44−
45 TimeFormatter formatter;
64 TimeFormatter formatter;
46 formatter.setTime(time.toMSecsSinceEpoch() * 1000);
65 formatter.setTime(time.toMSecsSinceEpoch() * 1000);
47 formatter.setFormat(format);
66 formatter.setFormat(format);
@@ -49,18 +68,96 @@ private Q_SLOTS:
49 QCOMPARE(formatter.timeString(), time.toString(format));
68 QCOMPARE(formatter.timeString(), time.toString(format));
50 }
69 }
51
70
52− void testFormatStrF()
71+ void tesGDatetTimeFormatter_Format_data() { addTestData(QList<int>() << 0); }
72+ void tesGDatetTimeFormatter_Format()
53 {
73 {
74+ QFETCH(QDateTime, time);
54 const QString format = "%d-%m-%Y %I:%M%p";
75 const QString format = "%d-%m-%Y %I:%M%p";
55
76
56− QDateTime time = QDateTime::currentDateTime();
57−
58 GDateTimeFormatter formatter;
77 GDateTimeFormatter formatter;
59 formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
78 formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
60 formatter.setFormat(format);
79 formatter.setFormat(format);
61
80
62 QCOMPARE(formatter.timeString(), time.toString("dd-MM-yyyy hh:mmAP"));
81 QCOMPARE(formatter.timeString(), time.toString("dd-MM-yyyy hh:mmAP"));
63 }
82 }
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+ }
64};
161};
65
162
66QTEST_GUILESS_MAIN(TimeFormatterTest)
163QTEST_GUILESS_MAIN(TimeFormatterTest)
tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+32−16
@@ -368,9 +368,9 @@ Item {
368 function test_create_alarmMenu_data() {
368 function test_create_alarmMenu_data() {
369 return [
369 return [
370 {label: "testLabel1", enabled: true, icon: "file:///testIcon1", color: Qt.rgba(0, 0, 0, 0),
370 {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"},
372 {label: "testLabel2", enabled: false, icon: "file:///testIcon2", color: Qt.rgba(1, 0, 0, 0),
372 {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" },
374 ];
374 ];
375 }
375 }
376
376
@@ -380,11 +380,11 @@ Item {
380 menuData.sensitive = data.enabled;
380 menuData.sensitive = data.enabled;
381 menuData.icon = data.icon;
381 menuData.icon = data.icon;
382 menuData.ext = {
382 menuData.ext = {
383− 'xCanonicalTime': data.time,
383+ 'xCanonicalTime': data.time.getTime() / 1000,
384 'xCanonicalTimeFormat': data.timeFormat
384 'xCanonicalTimeFormat': data.timeFormat
385 };
385 };
386 timeFormatter.format = data.timeFormat;
386 timeFormatter.format = data.timeFormat;
387− timeFormatter.time = data.time;
387+ timeFormatter.time = data.time.getTime() / 1000;
388
388
389 loader.data = menuData;
389 loader.data = menuData;
390 loader.sourceComponent = factory.load(menuData);
390 loader.sourceComponent = factory.load(menuData);
@@ -400,9 +400,9 @@ Item {
400 function test_create_appointmentMenu_data() {
400 function test_create_appointmentMenu_data() {
401 return [
401 return [
402 {label: "testLabel1", enabled: true, icon: "file:///testIcon1", color: Qt.rgba(0, 0, 0, 0),
402 {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"},
404 {label: "testLabel2", enabled: false, icon: "file:///testIcon2", color: Qt.rgba(1, 0, 0, 0),
404 {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" },
406 ];
406 ];
407 }
407 }
408
408
@@ -413,11 +413,11 @@ Item {
413 menuData.icon = data.icon;
413 menuData.icon = data.icon;
414 menuData.ext = {
414 menuData.ext = {
415 'xCanonicalColor': data.colour,
415 'xCanonicalColor': data.colour,
416− 'xCanonicalTime': data.time,
416+ 'xCanonicalTime': data.time.getTime() / 1000,
417 'xCanonicalTimeFormat': data.timeFormat
417 'xCanonicalTimeFormat': data.timeFormat
418 };
418 };
419 timeFormatter.format = data.timeFormat;
419 timeFormatter.format = data.timeFormat;
420− timeFormatter.time = data.time;
420+ timeFormatter.time = data.time.getTime() / 1000;
421
421
422 loader.data = menuData;
422 loader.data = menuData;
423 loader.sourceComponent = factory.load(menuData);
423 loader.sourceComponent = factory.load(menuData);
@@ -488,7 +488,10 @@ Item {
488 compare(loader.item.text, data.label, "Label does not match data");
488 compare(loader.item.text, data.label, "Label does not match data");
489 compare(loader.item.iconSource, data.icon, "Icon does not match data");
489 compare(loader.item.iconSource, data.icon, "Icon does not match data");
490 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
490 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
491+
492+ var button = findChild(loader.item, "buttonSectionMenuControl");
493+ verify(button !== null);
491− compare(loader.item.control.text, data.buttonText, "Button text does not match data");
494+ compare(button.text, data.buttonText, "Button text does not match data");
492 }
495 }
493
496
494 function test_create_wifiSection_data() {
497 function test_create_wifiSection_data() {
@@ -514,8 +517,8 @@ Item {
514
517
515 function test_create_accessPoint_data() {
518 function test_create_accessPoint_data() {
516 return [
519 return [
517− {label: "testLabel1", enabled: true, checked: false, secure: true, adHoc: false },
520+ {label: "testLabel1", enabled: true, active: false, secure: true, adHoc: false },
518− {label: "testLabel2", enabled: false, checked: true, secure: false, adHoc: true },
521+ {label: "testLabel2", enabled: false, active: true, secure: false, adHoc: true },
519 ];
522 ];
520 }
523 }
521
524
@@ -523,7 +526,7 @@ Item {
523 menuData.type = "unity.widgets.systemsettings.tablet.accesspoint";
526 menuData.type = "unity.widgets.systemsettings.tablet.accesspoint";
524 menuData.label = data.label;
527 menuData.label = data.label;
525 menuData.sensitive = data.enabled;
528 menuData.sensitive = data.enabled;
526− menuData.isToggled = data.checked;
529+ menuData.isToggled = data.active;
527 menuData.ext = {
530 menuData.ext = {
528 'xCanonicalWifiApStrengthAction': "action::strength",
531 'xCanonicalWifiApStrengthAction': "action::strength",
529 'xCanonicalWifiApIsSecure': data.secure,
532 'xCanonicalWifiApIsSecure': data.secure,
@@ -539,7 +542,7 @@ Item {
539 compare(loader.item.strengthAction.name, "action::strength", "Strength action incorrect");
542 compare(loader.item.strengthAction.name, "action::strength", "Strength action incorrect");
540 compare(loader.item.secure, data.secure, "Secure does not match data");
543 compare(loader.item.secure, data.secure, "Secure does not match data");
541 compare(loader.item.adHoc, data.adHoc, "AdHoc does not match data");
544 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");
543 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
546 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
544 }
547 }
545
548
@@ -635,7 +638,8 @@ Item {
635 album: "album1",
638 album: "album1",
636 running: true,
639 running: true,
637 state: "Playing",
640 state: "Playing",
638− enabled: true
641+ enabled: true,
642+ showTrack: true
639 },{
643 },{
640 label: "player2",
644 label: "player2",
641 icon: "file:://icon2",
645 icon: "file:://icon2",
@@ -645,7 +649,19 @@ Item {
645 album: "album2",
649 album: "album2",
646 running: false,
650 running: false,
647 state: "Paused",
651 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
649 }
665 }
650 ];
666 ];
651 }
667 }
@@ -675,7 +691,7 @@ Item {
675 compare(loader.item.song, data.song, "Song does not match data");
691 compare(loader.item.song, data.song, "Song does not match data");
676 compare(loader.item.artist, data.artist, "Artist does not match data");
692 compare(loader.item.artist, data.artist, "Artist does not match data");
677 compare(loader.item.album, data.album, "Album does not match data");
693 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");
679 compare(loader.item.state, data.state, "State does not match data");
695 compare(loader.item.state, data.state, "State does not match data");
680 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
696 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
681 }
697 }
tests/qmltests/Panel/Indicators/tst_MessageMenuItemFactory.qml+43−53
@@ -32,9 +32,8 @@ Item {
32 menuIndex: 0
32 menuIndex: 0
33 }
33 }
34
34
35− Utils.TimeFormatter {
35+ Utils.RelativeTimeFormatter {
36 id: timeFormatter
36 id: timeFormatter
37− format: "hh:mm - MMM dd"
38 }
37 }
39
38
40 UT.UnityTestCase {
39 UT.UnityTestCase {
@@ -73,8 +72,8 @@ Item {
73
72
74 function test_create_simpleTextmessage_data() {
73 function test_create_simpleTextmessage_data() {
75 return [
74 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},
75+ { title: "Title1", time: new Date(2013, 10, 10), body: "This is a text message 1", avatar: "file:///avatar1", icon: "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},
76+ { title: "Title2", time: new Date(2014, 12, 10), body: "This is a text message 2", avatar: "file:///avatar2", icon: "file:///appIcon2", enabled: false},
78 ];
77 ];
79 }
78 }
80
79
@@ -83,30 +82,27 @@ Item {
83 menuData.label = data.title;
82 menuData.label = data.title;
84 menuData.sensitive = data.enabled;
83 menuData.sensitive = data.enabled;
85 menuData.ext = {
84 menuData.ext = {
86− 'xCanonicalTime': data.time,
85+ 'xCanonicalTime': data.time.getTime()*1000,
87− 'xCanonicalText': data.message,
86+ 'xCanonicalText': data.body,
88 'icon': data.avatar,
87 'icon': data.avatar,
89− 'xCanonicalAppIcon': data.appIcon,
88+ 'xCanonicalAppIcon': data.icon,
90 };
89 };
91 factory.menuData = menuData;
90 factory.menuData = menuData;
92− timeFormatter.time = data.time;
91+ timeFormatter.time = data.time.getTime()/1000;
93−
92+
94− var loader = findChild(factory, "loader");
95− verify(loader !== undefined);
96−
97− tryCompare(loader.item, "objectName", "simpleTextMessage");
93+ tryCompare(factory.item, "objectName", "simpleTextMessage");
98− compare(loader.item.title, data.title, "Title does not match data");
94+ compare(factory.item.title, data.title, "Title does not match data");
99− compare(loader.item.time, timeFormatter.timeString, "Time does not match data");
95+ compare(factory.item.time, timeFormatter.timeString, "Time does not match data");
100− compare(loader.item.message, data.message, "Message does not match data");
96+ compare(factory.item.body, data.body, "Message does not match data");
101− compare(loader.item.avatar, data.avatar, "Avatar does not match data");
97+ compare(factory.item.avatar, data.avatar, "Avatar does not match data");
102− compare(loader.item.appIcon, data.appIcon, "App icon does not match data");
98+ compare(factory.item.icon, data.icon, "App icon does not match data");
103− compare(loader.item.enabled, data.enabled, "Enabled does not match data");
99+ compare(factory.item.enabled, data.enabled, "Enabled does not match data");
104 }
100 }
105
101
106 function test_create_textmessage_data() {
102 function test_create_textmessage_data() {
107 return [
103 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},
104+ { title: "Title1", time: new Date(2013, 10, 10), body: "This is a text message 1", avatar: "file:///avatar1", icon: "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},
105+ { title: "Title2", time: new Date(2014, 12, 10), body: "This is a text message 2", avatar: "file:///avatar2", icon: "file:///appIcon2", enabled: false},
110 ];
106 ];
111 }
107 }
112
108
@@ -115,10 +111,10 @@ Item {
115 menuData.label = data.title;
111 menuData.label = data.title;
116 menuData.sensitive = data.enabled;
112 menuData.sensitive = data.enabled;
117 menuData.ext = {
113 menuData.ext = {
118− 'xCanonicalTime': data.time,
114+ 'xCanonicalTime': data.time.getTime()*1000,
119− 'xCanonicalText': data.message,
115+ 'xCanonicalText': data.body,
120 'icon': data.avatar,
116 'icon': data.avatar,
121− 'xCanonicalAppIcon': data.appIcon,
117+ 'xCanonicalAppIcon': data.icon,
122 'xCanonicalMessageActions': [{
118 'xCanonicalMessageActions': [{
123 'parameter-type': "s",
119 'parameter-type': "s",
124 'name': "action::reply",
120 'name': "action::reply",
@@ -127,25 +123,22 @@ Item {
127 ]
123 ]
128 };
124 };
129 factory.menuData = menuData;
125 factory.menuData = menuData;
130− timeFormatter.time = data.time;
126+ timeFormatter.time = data.time.getTime()/1000;
131−
127+
132− var loader = findChild(factory, "loader");
133− verify(loader !== undefined);
134−
135− tryCompare(loader.item, "objectName", "textMessage");
128+ tryCompare(factory.item, "objectName", "textMessage");
136− compare(loader.item.title, data.title, "Title does not match data");
129+ compare(factory.item.title, data.title, "Title does not match data");
137− compare(loader.item.time, timeFormatter.timeString, "Time does not match data");
130+ compare(factory.item.time, timeFormatter.timeString, "Time does not match data");
138− compare(loader.item.message, data.message, "Message does not match data");
131+ compare(factory.item.body, data.body, "Message does not match data");
139− compare(loader.item.avatar, data.avatar, "Avatar does not match data");
132+ compare(factory.item.avatar, data.avatar, "Avatar does not match data");
140− compare(loader.item.appIcon, data.appIcon, "App icon does not match data");
133+ compare(factory.item.icon, data.icon, "App icon does not match data");
141− compare(loader.item.enabled, data.enabled, "Enabled does not match data");
134+ compare(factory.item.enabled, data.enabled, "Enabled does not match data");
142 }
135 }
143
136
144
137
145 function test_create_snapDecision_data() {
138 function test_create_snapDecision_data() {
146 return [
139 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},
140+ { title: "Title1", time: new Date(2013, 10, 10), body: "This is a text message 1", avatar: "file:///avatar1", icon: "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},
141+ { title: "Title2", time: new Date(2014, 12, 10), body: "This is a text message 2", avatar: "file:///avatar2", icon: "file:///appIcon2", enabled: false},
149 ];
142 ];
150 }
143 }
151
144
@@ -154,10 +147,10 @@ Item {
154 menuData.label = data.title;
147 menuData.label = data.title;
155 menuData.sensitive = data.enabled;
148 menuData.sensitive = data.enabled;
156 menuData.ext = {
149 menuData.ext = {
157− 'xCanonicalTime': data.time,
150+ 'xCanonicalTime': data.time.getTime()*1000,
158− 'xCanonicalText': data.message,
151+ 'xCanonicalText': data.body,
159 'icon': data.avatar,
152 'icon': data.avatar,
160− 'xCanonicalAppIcon': data.appIcon,
153+ 'xCanonicalAppIcon': data.icon,
161 'xCanonicalMessageActions': [{
154 'xCanonicalMessageActions': [{
162 'name': "action::callback",
155 'name': "action::callback",
163 'label': "Callback1"
156 'label': "Callback1"
@@ -169,18 +162,15 @@ Item {
169 ]
162 ]
170 };
163 };
171 factory.menuData = menuData;
164 factory.menuData = menuData;
172− timeFormatter.time = data.time;
165+ timeFormatter.time = data.time.getTime()/1000;
173−
166+
174− var loader = findChild(factory, "loader");
175− verify(loader !== undefined);
176−
177− tryCompare(loader.item, "objectName", "snapDecision");
167+ tryCompare(factory.item, "objectName", "snapDecision");
178− compare(loader.item.title, data.title, "Title does not match data");
168+ compare(factory.item.title, data.title, "Title does not match data");
179− compare(loader.item.time, timeFormatter.timeString, "Time does not match data");
169+ compare(factory.item.time, timeFormatter.timeString, "Time does not match data");
180− compare(loader.item.message, data.message, "Message does not match data");
170+ compare(factory.item.body, data.body, "Message does not match data");
181− compare(loader.item.avatar, data.avatar, "Avatar does not match data");
171+ compare(factory.item.avatar, data.avatar, "Avatar does not match data");
182− compare(loader.item.appIcon, data.appIcon, "App icon does not match data");
172+ compare(factory.item.icon, data.icon, "App icon does not match data");
183− compare(loader.item.enabled, data.enabled, "Enabled does not match data");
173+ compare(factory.item.enabled, data.enabled, "Enabled does not match data");
184 }
174 }
185
175
186 }
176 }
tests/qmltests/Panel/tst_Indicators.qml+10−10
@@ -173,7 +173,7 @@ Item {
173 function test_row_visible_menuContent_visible(data) {
173 function test_row_visible_menuContent_visible(data) {
174 indicators.show();
174 indicators.show();
175
175
176− var indicatorTabs = findChild(indicators, "tabs");
176+ var contentListView = findChild(indicators, "indicatorsContentListView");
177 var indicatorRowItems = findChild(indicators, "indicatorRowItems");
177 var indicatorRowItems = findChild(indicators, "indicatorRowItems");
178
178
179 var count = data.visible.length
179 var count = data.visible.length
@@ -194,19 +194,19 @@ Item {
194 tryCompareFunction(function() { return findChild(indicatorRowItems, widgetName) !== null }, data.visible[i]);
194 tryCompareFunction(function() { return findChild(indicatorRowItems, widgetName) !== null }, data.visible[i]);
195
195
196 // check for tab
196 // 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]);
198 }
198 }
199 }
199 }
200
200
201− function test_indicator_visible_correct_tabs_data() { return [
201+ function test_indicator_visible_correct_menu_data() { return [
202− {tag: "current-first", currentIndex: 0, visible: [false, true, true, true, true], expectedIndex: 0, expectedTab: "indicator-fake2" },
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, expectedTab: "indicator-fake4" },
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, expectedTab: "indicator-fake1" },
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, expectedTab: "indicator-fake2" }];
205+ {tag: "before", currentIndex: 1, visible: [false, true, true, true, true], expectedIndex: 1, expextedMenu: "indicator-fake2" }];
206 }
206 }
207
207
208− function test_indicator_visible_correct_tabs(data) {
208+ function test_indicator_visible_correct_menu(data) {
209− var indicatorTabs = findChild(indicators, "tabs");
209+ var contentListView = findChild(indicators, "indicatorsContentListView");
210 var indicatorRow = findChild(indicators, "indicatorRow");
210 var indicatorRow = findChild(indicators, "indicatorRow");
211
211
212 indicators.show();
212 indicators.show();
@@ -223,7 +223,7 @@ Item {
223 tryCompare(indicatorRow, "currentItemIndex", data.expectedIndex);
223 tryCompare(indicatorRow, "currentItemIndex", data.expectedIndex);
224
224
225 // check for current selected tab
225 // 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);
227
227
228 }
228 }
229 }
229 }
tests/qmltests/Panel/tst_MenuContent.qml+14−56
@@ -47,7 +47,6 @@ Item {
47 MenuContent {
47 MenuContent {
48 id: menuContent
48 id: menuContent
49 indicatorsModel: indicatorsModel
49 indicatorsModel: indicatorsModel
50− contentReleaseInterval: 50
51 height: parent.height - 50
50 height: parent.height - 50
52 }
51 }
53
52
@@ -91,22 +90,22 @@ Item {
91 return "indicator-fake" + (index + 1) + "-page";
90 return "indicator-fake" + (index + 1) + "-page";
92 }
91 }
93
92
94− property string testTabObjectName : ""
93+ property string testItemObjectName : ""
95
94
96− function selected_tab_equals_test_tab() {
95+ function current_item_equals_test_item() {
97− var currentTab = menu_content_test.findChild(menuContent, "tabs").selectedTab
96+ var currentItem = menu_content_test.findChild(menuContent, "indicatorsContentListView").currentItem
98− if (currentTab === null) {
97+ if (currentItem === null) {
99− console.log("selected tab undefined");
98+ console.log("current item undefined");
100 return false;
99 return false;
101 }
100 }
102
101
103− var testTab = menu_content_test.findChild(menuContent, testTabObjectName);
102+ var testItem = menu_content_test.findChild(menuContent, testItemObjectName);
104− if (testTab === null) {
103+ if (testItem === null) {
105− console.log("test_tab " + testTabObjectName + " undefined");
104+ console.log("testItem " + testItemObjectName + " undefined");
106 return false;
105 return false;
107 }
106 }
108
107
109− return testTab == currentTab;
108+ return testItem === currentItem;
110 }
109 }
111
110
112 UT.UnityTestCase {
111 UT.UnityTestCase {
@@ -114,63 +113,22 @@ Item {
114 name: "MenuContentTest"
113 name: "MenuContentTest"
115 when: windowShown
114 when: windowShown
116
115
117− function init() {
118− if (menuContent.__contentActive)
119− menuContent.releaseContent();
120− tryCompare(menuContent, "__contentActive", false);
121− }
122−
123 // Check that the correct menus are displayed for the requested item.
116 // Check that the correct menus are displayed for the requested item.
124 function test_show_menu() {
117 function test_show_menu() {
125 var menuCount = indicatorsModel.count;
118 var menuCount = indicatorsModel.count;
126 verify(menuCount > 0, "Menu count should be greater than zero");
119 verify(menuCount > 0, "Menu count should be greater than zero");
127
120
128− var tabs = menu_content_test.findChild(menuContent, "tabs")
121+ var listView = menu_content_test.findChild(menuContent, "indicatorsContentListView")
122+ verify(listView !== null)
129
123
130 // Loop over twice to test jump between last and first.
124 // Loop over twice to test jump between last and first.
131 for (var i = 0; i < menuCount*2; i++) {
125 for (var i = 0; i < menuCount*2; i++) {
132−
133 var menuIndex = i%menuCount;
126 var menuIndex = i%menuCount;
134
127
135 activate_content(menuIndex);
128 activate_content(menuIndex);
136− testTabObjectName = indicatorsModel.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
129+ testItemObjectName = indicatorsModel.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
137− compare(tabs.selectedTabIndex, menuIndex, "Current tab index does not match selected tab index");
130+ compare(listView.currentIndex, menuIndex, "Current tab index does not match selected tab index");
138− tryCompareFunction(selected_tab_equals_test_tab, true);
131+ tryCompareFunction(current_item_equals_test_item, 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);
174 }
132 }
175 }
133 }
176
134