Merge lp:~renatofilho/address-book-app/new-bottom-edge into lp:address-book-app

Proposed by Renato Araujo Oliveira Filho
Status: Merged
Approved by: Bill Filler
Approved revision: 596
Merged at revision: 528
Proposed branch: lp:~renatofilho/address-book-app/new-bottom-edge
Merge into: lp:address-book-app
Prerequisite: lp:~renatofilho/address-book-app/keyboard-navigation
Diff against target: 2821 lines (+809/-955)
37 files modified
CMakeLists.txt (+11/-0)
config.h.in (+1/-0)
debian/control (+1/-0)
src/app/addressbookapp.cpp (+5/-39)
src/app/addressbookapp.h (+0/-6)
src/imports/ABContactEditorPage.qml (+21/-18)
src/imports/ABContactListPage.qml (+251/-356)
src/imports/ABContactViewPage.qml (+57/-28)
src/imports/ABEmptyState.qml (+61/-0)
src/imports/ABMultiColumnEmptyState.qml (+75/-0)
src/imports/ABNewContactBottomEdge.qml (+89/-0)
src/imports/BottomEdge.qml (+0/-338)
src/imports/BottomEdgeShadow.qml (+0/-31)
src/imports/CMakeLists.txt (+3/-2)
src/imports/MainWindow.qml (+42/-21)
src/imports/Settings/SettingsPage.qml (+3/-3)
src/imports/Ubuntu/AddressBook/Base/ContactDetailGroupBase.qml (+4/-5)
src/imports/Ubuntu/AddressBook/Base/KeyboardRectangle.qml (+4/-1)
src/imports/Ubuntu/AddressBook/ContactEditor/ComboButtonAddField.qml (+2/-2)
src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailAvatarEditor.qml (+0/-1)
src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailGroupWithTypeEditor.qml (+0/-1)
src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailWithTypeEditor.qml (+1/-1)
src/imports/Ubuntu/AddressBook/ContactEditor/ContactEditorPage.qml (+74/-18)
src/imports/Ubuntu/AddressBook/ContactEditor/TextInputDetail.qml (+6/-15)
src/imports/Ubuntu/AddressBook/ContactShare/ContactSharePage.qml (+10/-2)
src/imports/Ubuntu/AddressBook/ContactView/ContactViewPage.qml (+13/-2)
src/imports/Ubuntu/Contacts/ContactListView.qml (+15/-18)
src/imports/Ubuntu/Contacts/ContactSimpleListView.qml (+3/-3)
src/imports/Ubuntu/Contacts/ListItemWithActions.qml (+19/-0)
tests/autopilot/address_book_app/__init__.py (+28/-10)
tests/autopilot/address_book_app/pages/_ab_contact_list_page.py (+1/-19)
tests/autopilot/address_book_app/tests/test_add_contact.py (+1/-3)
tests/autopilot/address_book_app/tests/test_create_new_from_uri.py (+0/-2)
tests/autopilot/address_book_app/tests/test_edit_contact.py (+2/-4)
tests/autopilot/address_book_app/tests/test_import_vcard.py (+1/-1)
tests/autopilot/address_book_app/tests/test_single_pick_mode.py (+1/-0)
tests/qml/tst_ContactPreviewPage.qml (+4/-5)
To merge this branch: bzr merge lp:~renatofilho/address-book-app/new-bottom-edge
Reviewer Review Type Date Requested Status
Bill Filler (community) Approve
PS Jenkins bot continuous-integration Needs Fixing
Review via email: mp+279613@code.launchpad.net

This proposal supersedes a proposal from 2015-12-04.

Commit message

Implement the new bottom edge using the SDK component.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
548. By Renato Araujo Oliveira Filho

Update BottomEdge editor page with the correct contact model.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
549. By Renato Araujo Oliveira Filho

Removed support for deprecated application arguments.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
550. By Renato Araujo Oliveira Filho

Make use of the new SDK PageHearder API.

551. By Renato Araujo Oliveira Filho

WORKAROUND: Set 'QuickUtils.mouseAttached = true' to avoid problems with bottom edge on desktop;

552. By Renato Araujo Oliveira Filho

Make sure that a contact is selected on application startup.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
553. By Renato Araujo Oliveira Filho

Push contact view page component instead of object to avoid problems with page stack.

554. By Renato Araujo Oliveira Filho

Make bottom edge component on contact view available only when in dual column layout.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
555. By Renato Araujo Oliveira Filho

Does not use navigationActions.

556. By Renato Araujo Oliveira Filho

Trunk merged.

557. By Renato Araujo Oliveira Filho

Fix problems with previous merge.

558. By Renato Araujo Oliveira Filho

Remove debug message.
Fix focus on contact list after close contact editor page.
Fix ambigous 'esc' short cut when editor page is opened.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
559. By Renato Araujo Oliveira Filho

Create a empty state page for multi column layout.

560. By Renato Araujo Oliveira Filho

Move focus back to contaclist after close ContactEditor page.
Add 'esc' shortcut to close BottomEdge.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
561. By Renato Araujo Oliveira Filho

Change bottom edge visual if a mouse is present.

562. By Renato Araujo Oliveira Filho

Update test to use new header API.

563. By Renato Araujo Oliveira Filho

Trunk merged.

564. By Renato Araujo Oliveira Filho

Create a leading actions property on Contact editor page to avoid problems with SDK replacing it.
Check if the mouse event is a real mouse event and not a touch event.

565. By Renato Araujo Oliveira Filho

Only enable keyboard visuals after pres 'down' key. To avoid enabling it while using the virtual keyboard.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
566. By Renato Araujo Oliveira Filho

Sync load bottom edge page.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
567. By Renato Araujo Oliveira Filho

Load BottomEdge component async to avoid delays on app startup.
Fix bottom edge contents size.

568. By Renato Araujo Oliveira Filho

Does not move contact list when vkb appears on bottom edge page.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
569. By Renato Araujo Oliveira Filho

Trunk merged.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Bill Filler (bfiller) wrote :

Silo 55:
tesing on krillin

- try to search, type in search string, open resultant contact, click back, now you are stuck on search page with back button disabled
See this in log
file:///usr/share/address-book-app/imports/ABContactListPage.qml:615: TypeError: Cannot read property 'status' of null
file:///usr/share/address-book-app/imports/ABContactListPage.qml:441: TypeError: Cannot read property 'status' of null

- opening a contact, sometimes get a page with no back button, it's happend 3 or 4 times but can't reproduce reliably

- the order of the icons changed on the toolbar, settings and search positions are swapped

- Very often bottom edge swipe fails and it does not get properly activated (doesn't get displayed), when this happens I see multiple failure messages:
file:///usr/lib/arm-linux-gnueabihf/qt5/qml/Ubuntu/Components/1.0/Icon.qml:37:5: QML QQuickImage: Failed to get image from provider: image://theme/clear-search

- There is a very big delay in the animation when swiping up from bottom, the page does not follow my finger as it used to. We had this problem in the past and needed to have the page created up front as it takes too long to create it dynamically. This shouldn't be done at start up but after the app is displayed then it should be created and cached.

- Open bottom edge, then close it, get these errors in log
file:///usr/lib/arm-linux-gnueabihf/qt5/qml/Ubuntu/Components/1.0/Icon.qml:37:5: QML QQuickImage: Failed to get image from provider: image://theme/clear-search
file:///usr/lib/arm-linux-gnueabihf/qt5/qml/Ubuntu/Components/1.0/Icon.qml:37:5: QML QQuickImage: Failed to get image from provider: image://theme/clear-search
file:///usr/lib/arm-linux-gnueabihf/qt5/qml/Ubuntu/Components/1.0/Icon.qml:37:5: QML QQuickImage: Failed to get image from provider: image://theme/clear-search
file:///usr/lib/arm-linux-gnueabihf/qt5/qml/Ubuntu/Components/1.0/Icon.qml:37:5: QML QQuickImage: Failed to get image from provider: image://theme/clear-search

- pressing edit on a contact is slow again

- I see multiple of these errors everytime trying to edit a contact
file:///usr/lib/arm-linux-gnueabihf/qt5/qml/Ubuntu/Components/1.0/Icon.qml:37:5: QML QQuickImage: Failed to get image from provider: image://theme/clear-search

review: Needs Fixing
570. By Renato Araujo Oliveira Filho

Fixed 'back' action state while searching.

571. By Renato Araujo Oliveira Filho

Compile ContactEditorPage before push it on stack.

572. By Renato Araujo Oliveira Filho

Avoid stole contact list focus while creating bottom edge component.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :
Download full text (3.2 KiB)

> Silo 55:
> tesing on krillin
>
> - try to search, type in search string, open resultant contact, click back,
> now you are stuck on search page with back button disabled
> See this in log
> file:///usr/share/address-book-app/imports/ABContactListPage.qml:615:
> TypeError: Cannot read property 'status' of null
> file:///usr/share/address-book-app/imports/ABContactListPage.qml:441:
> TypeError: Cannot read property 'status' of null
Fixed rev. 50

>
> - opening a contact, sometimes get a page with no back button, it's happend 3
> or 4 times but can't reproduce reliably
>
> - the order of the icons changed on the toolbar, settings and search positions
> are swapped
This is a regression caused by the new SDK, we need to discuss if it was intentional and we need to change the code or it was a real bug.

>
> - Very often bottom edge swipe fails and it does not get properly activated
> (doesn't get displayed), when this happens I see multiple failure messages:
> file:///usr/lib/arm-linux-
> gnueabihf/qt5/qml/Ubuntu/Components/1.0/Icon.qml:37:5: QML QQuickImage: Failed
> to get image from provider: image://theme/clear-search
This warning message is a bug caused by the new SDK, you can notice that now the input fields does not have the clear button anymore. You should have the same problems in all apps.

>
> - There is a very big delay in the animation when swiping up from bottom, the
> page does not follow my finger as it used to. We had this problem in the past
> and needed to have the page created up front as it takes too long to create it
> dynamically. This shouldn't be done at start up but after the app is displayed
> then it should be created and cached.
Everything on bottom edge is controlled by the SDK now, we (the application) doe not have control over the animation or how to load the page. During the tests I noticed that the page does not follow the finger speed but I think it is intentional because the animation has a controlled speed. (we can discuss that with the SDK team).

>
> - Open bottom edge, then close it, get these errors in log
> file:///usr/lib/arm-linux-
> gnueabihf/qt5/qml/Ubuntu/Components/1.0/Icon.qml:37:5: QML QQuickImage: Failed
> to get image from provider: image://theme/clear-search
> file:///usr/lib/arm-linux-
> gnueabihf/qt5/qml/Ubuntu/Components/1.0/Icon.qml:37:5: QML QQuickImage: Failed
> to get image from provider: image://theme/clear-search
> file:///usr/lib/arm-linux-
> gnueabihf/qt5/qml/Ubuntu/Components/1.0/Icon.qml:37:5: QML QQuickImage: Failed
> to get image from provider: image://theme/clear-search
> file:///usr/lib/arm-linux-
> gnueabihf/qt5/qml/Ubuntu/Components/1.0/Icon.qml:37:5: QML QQuickImage: Failed
> to get image from provider: image://theme/clear-search
This warning message is a bug caused by the new SDK, you can notice that now the input fields does not have the clear button anymore. You should have the same problems in all apps.

>
> - pressing edit on a contact is slow again
I have optimized it a lit bit on rev 573. But we can not create the page sync as we use to do because of this bug #1528017.
AdaptativeLayout always create the page async and for some reason it is way more slower t...

Read more...

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
573. By Renato Araujo Oliveira Filho

Disable edit button after first click on it. (avoid push the page more than once)

574. By Renato Araujo Oliveira Filho

highlight contact on click.

575. By Renato Araujo Oliveira Filho

Quick focus on contact name after open bottom edge page.

576. By Renato Araujo Oliveira Filho

Does not highlight settings item if not using keyboard.

577. By Renato Araujo Oliveira Filho

Update autopilot tests to work with the new bottom edge component.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
578. By Renato Araujo Oliveira Filho

Removed debug message.

579. By Renato Araujo Oliveira Filho

Disable bottom edge while searching on multi colum layout.

580. By Renato Araujo Oliveira Filho

Cancel search after open bottom edge.

581. By Renato Araujo Oliveira Filho

Update contact view title on contact name change.

582. By Renato Araujo Oliveira Filho

Removed debug message.

583. By Renato Araujo Oliveira Filho

Fetch selected contact on application expansion.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
584. By Renato Araujo Oliveira Filho

Use unity 8 private api to detect mouse and keyboard.

585. By Renato Araujo Oliveira Filho

Fetch first contact if no contact is selected after close bottom edge.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
586. By Renato Araujo Oliveira Filho

Fixed field focus while adding new fields on contact editor.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
587. By Renato Araujo Oliveira Filho

Disable bottom edge shortcut on contact list while it is not active.

588. By Renato Araujo Oliveira Filho

Update autopilot tests to work with the new bottom edge components.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
589. By Renato Araujo Oliveira Filho

Update contact delegate click animation.

590. By Renato Araujo Oliveira Filho

Remove unused variables.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
591. By Renato Araujo Oliveira Filho

Fixed focus on wrong field after add a new contact detail

592. By Renato Araujo Oliveira Filho

Focus contact list after close bottom edge.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
593. By Renato Araujo Oliveira Filho

set minimum width and height for the app.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
594. By Renato Araujo Oliveira Filho

Fixed focus on contact deletion.

595. By Renato Araujo Oliveira Filho

Fixed share component page to work with pagestack

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
596. By Renato Araujo Oliveira Filho

Reduce the application width necessary to change into multi column layout.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Bill Filler (bfiller) wrote :

All outstanding issues resolved

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2015-09-03 17:15:32 +0000
3+++ CMakeLists.txt 2016-01-07 13:19:12 +0000
4@@ -83,6 +83,17 @@
5 set(ADDRESS_BOOK_APP_DESKTOP_FILE address-book-app.desktop)
6 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
7
8+#find unity8 qml libraries
9+find_path(LIB_UNITY_QML_EXISTS NAMES libUnity-qml.so
10+ HINTS "/usr/lib/x86_64-linux-gnu/unity8/qml/Unity/"
11+ NO_CMAKE_PATH
12+ NO_CMAKE_ENVIRONMENT_PATH
13+ NO_SYSTEM_ENVIRONMENT_PATH
14+)
15+if(!LIB_UNITY_QML_EXISTS)
16+ MESSAGE(FATAL_ERROR "unity8 private package not-found")
17+endif()
18+
19 add_subdirectory(data)
20 add_subdirectory(src)
21 add_subdirectory(po)
22
23=== modified file 'config.h.in'
24--- config.h.in 2015-09-29 19:03:44 +0000
25+++ config.h.in 2016-01-07 13:19:12 +0000
26@@ -10,6 +10,7 @@
27 #define QT_EXTRA_IMPORTS_DIR "@QT_EXTRA_IMPORTS_DIR@"
28 #define ADDRESS_BOOK_APP_CLICK_PACKAGE "@CLICK_MODE@"
29 #define I18N_DIRECTORY "@CMAKE_INSTALL_PREFIX@/share/locale"
30+#define UNITY8_QML_PATH "/usr/lib/@CMAKE_C_LIBRARY_ARCHITECTURE@/unity8/qml/"
31
32 #define SETTINGS_ORGANIZATION_NAME "com.ubuntu.address-book"
33 #define SETTINGS_ORGANIZATION_DOMAIN "canonical.com"
34
35=== modified file 'debian/control'
36--- debian/control 2015-10-16 13:37:25 +0000
37+++ debian/control 2016-01-07 13:19:12 +0000
38@@ -52,6 +52,7 @@
39 qtdeclarative5-ubuntu-history0.1,
40 qtdeclarative5-ubuntu-keyboard-extensions0.1,
41 qtdeclarative5-ubuntu-telephony-phonenumber0.1 (>= 0.1+14.10.20140715.1),
42+ unity8-private,
43 ${misc:Depends},
44 ${shlibs:Depends},
45 Description: Address Book application
46
47=== modified file 'src/app/addressbookapp.cpp'
48--- src/app/addressbookapp.cpp 2015-12-18 13:48:47 +0000
49+++ src/app/addressbookapp.cpp 2016-01-07 13:19:12 +0000
50@@ -44,8 +44,6 @@
51 {
52 qDebug() << "usage:"
53 << arguments.at(0).toUtf8().constData()
54- << "[addressbook:///addphone?id=<contact-id>&phone=<phone-number>]"
55- << "[addressbook:///addnewphone?phone=<phone-number>]"
56 << "[addressbook:///contact?id=<contact-id>]"
57 << "[addressbook:///create?phone=<phone-number>]"
58 << "[addressbook:///pick?single=<true/false>]"
59@@ -104,8 +102,7 @@
60 m_netManager(new QNetworkConfigurationManager),
61 m_pickingMode(false),
62 m_testMode(false),
63- m_withArgs(false),
64- m_withKeyboard(false)
65+ m_withArgs(false)
66 {
67 s_elapsed.start();
68 setOrganizationName(SETTINGS_ORGANIZATION_NAME);
69@@ -194,10 +191,13 @@
70 this, SLOT(onViewStatusChanged(QQuickView::Status)));
71 QObject::connect(m_view->engine(), SIGNAL(quit()), SLOT(quit()));
72
73+ m_view->setMinimumWidth(300);
74+ m_view->setMinimumHeight(500);
75 m_view->setResizeMode(QQuickView::SizeRootObjectToView);
76 m_view->setTitle("AddressBook");
77 qDebug() << "New import path:" << QCoreApplication::applicationDirPath() + "/" + importPath("");
78 m_view->engine()->addImportPath(QCoreApplication::applicationDirPath() + "/" + importPath(""));
79+ m_view->engine()->addImportPath(UNITY8_QML_PATH);
80 m_view->rootContext()->setContextProperty("QTCONTACTS_MANAGER_OVERRIDE", defaultManager);
81 m_view->rootContext()->setContextProperty("application", this);
82 m_view->rootContext()->setContextProperty("contactKey", contactKey);
83@@ -318,17 +318,12 @@
84
85 if (methodsMetaData.isEmpty()) {
86 QStringList args;
87- //edit
88- args << "id" << "phone";
89- methodsMetaData.insert("addphone", args);
90- args.clear();
91-
92 //view
93 args << "id";
94 methodsMetaData.insert("contact", args);
95 args.clear();
96
97- //add
98+ //create
99 args << "phone";
100 methodsMetaData.insert("create", args);
101 args.clear();
102@@ -342,11 +337,6 @@
103 args << "url";
104 methodsMetaData.insert("importvcard", args);
105 args.clear();
106-
107- //addnewphone
108- args << "phone";
109- methodsMetaData.insert("addnewphone", args);
110- args.clear();
111 }
112
113 QUrlQuery query(url);
114@@ -448,26 +438,6 @@
115 qDebug() << "ELAPSED:" << s_elapsed.elapsed() / 1000.0;
116 }
117
118-bool AddressBookApp::notify(QObject *obj, QEvent *event)
119-{
120- switch(event->type())
121- {
122- case QEvent::KeyPress:
123- // we have no way to detect when a physical keyboard is connected, so we
124- // assume there is one when the down key is pressed
125- if (!m_withKeyboard && (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Down)) {
126- m_withKeyboard = true;
127- Q_EMIT usingKeyboardChanged();
128- return false;
129- }
130- break;
131- default:
132- break;
133- }
134-
135- return QGuiApplication::notify(obj, event);
136-}
137-
138 QString AddressBookApp::callbackApplication() const
139 {
140 return m_callbackApplication;
141@@ -497,7 +467,3 @@
142 return !m_updateWatcher.isNull();
143 }
144
145-bool AddressBookApp::usingKeyboard() const
146-{
147- return m_withKeyboard;
148-}
149
150=== modified file 'src/app/addressbookapp.h'
151--- src/app/addressbookapp.h 2015-12-16 18:35:33 +0000
152+++ src/app/addressbookapp.h 2016-01-07 13:19:12 +0000
153@@ -32,7 +32,6 @@
154 Q_PROPERTY(bool isOnline READ isOnline NOTIFY isOnlineChanged)
155 Q_PROPERTY(bool serverSafeMode READ serverSafeMode NOTIFY serverSafeModeChanged)
156 Q_PROPERTY(bool updating READ updating NOTIFY updatingChanged)
157- Q_PROPERTY(bool usingKeyboard READ usingKeyboard NOTIFY usingKeyboardChanged)
158
159 public:
160 AddressBookApp(int &argc, char **argv);
161@@ -46,14 +45,12 @@
162 bool isOnline() const;
163 bool serverSafeMode() const;
164 bool updating() const;
165- bool usingKeyboard() const;
166
167 Q_SIGNALS:
168 void callbackApplicationChanged();
169 void isOnlineChanged();
170 void serverSafeModeChanged();
171 void updatingChanged();
172- void usingKeyboardChanged();
173 void sourcesChanged();
174
175 public Q_SLOTS:
176@@ -69,8 +66,6 @@
177 // debug
178 void elapsed() const;
179
180-protected:
181- bool notify(QObject *obj, QEvent *event);
182
183 private Q_SLOTS:
184 void onUpdateCallFinished(QDBusPendingCallWatcher *watcher);
185@@ -90,7 +85,6 @@
186 bool m_pickingMode;
187 bool m_testMode;
188 bool m_withArgs;
189- bool m_withKeyboard;
190 };
191
192 #endif
193
194=== modified file 'src/imports/ABContactEditorPage.qml'
195--- src/imports/ABContactEditorPage.qml 2015-12-16 18:35:33 +0000
196+++ src/imports/ABContactEditorPage.qml 2016-01-07 13:19:12 +0000
197@@ -26,31 +26,33 @@
198 objectName: "contactEditorPage"
199
200 property alias backIconName: backAction.iconName
201-
202 // Property used on unit tests
203 readonly property alias saveActionEnabled: saveAction.enabled
204
205- head.backAction: Action {
206- id: backAction
207-
208- objectName: "cancel"
209- name: "cancel"
210-
211- text: i18n.tr("Cancel")
212- iconName: "back"
213- // WORKAROUND: SDK does not unregister shortcut on object destruction
214- // we need to do it manually. (bug #1518420)
215- enabled: root.active && root.enabled
216- shortcut: enabled ? "Esc" : undefined
217- onTriggered: root.cancel()
218- }
219-
220- head.actions: [
221+ leadingActions: [
222+ Action {
223+ id: backAction
224+
225+ objectName: "cancel"
226+ name: "cancel"
227+ text: i18n.tr("Cancel")
228+ iconName: "down"
229+ enabled: root.active && root.enabled
230+ shortcut: "Esc"
231+ onTriggered: {
232+ root.cancel()
233+ if (root.pageStack.contactListPage)
234+ root.pageStack.contactListPage.forceActiveFocus()
235+ }
236+ }
237+ ]
238+
239+ headerActions: [
240 Action {
241 id: saveAction
242+
243 objectName: "save"
244 name: "save"
245-
246 text: i18n.tr("Save")
247 iconName: "ok"
248 enabled: root.isContactValid && root.active && root.enabled
249@@ -62,6 +64,7 @@
250 onContactSaved: {
251 if (pageStack.contactListPage) {
252 pageStack.contactListPage.moveListToContact(contact)
253+ pageStack.contactListPage.forceActiveFocus()
254 }
255 }
256 }
257
258=== modified file 'src/imports/ABContactListPage.qml'
259--- src/imports/ABContactListPage.qml 2015-12-14 18:29:23 +0000
260+++ src/imports/ABContactListPage.qml 2016-01-07 13:19:12 +0000
261@@ -1,4 +1,4 @@
262-/*
263+/*
264 * Copyright (C) 2012-2015 Canonical, Ltd.
265 *
266 * This program is free software; you can redistribute it and/or modify
267@@ -26,71 +26,73 @@
268 import Ubuntu.AddressBook.Base 0.1
269 import Ubuntu.AddressBook.ContactShare 0.1
270
271-import "." as AB
272
273 Page {
274 id: mainPage
275 objectName: "contactListPage"
276
277+ property var viewPage: null
278 property bool pickMode: false
279 property alias contentHubTransfer: contactExporter.activeTransfer
280 property bool pickMultipleContacts: false
281 property QtObject contactIndex: null
282- property string newPhoneToAdd: ""
283 property alias contactManager: contactList.manager
284- property alias contactViewPage: contactViewPageConnections.target
285- property alias contactEditorPage: contactEditorPageConnections.target
286+
287 property var _busyDialog: null
288 property bool _importingTestData: false
289 property bool _creatingContact: false
290
291- readonly property bool bottomEdgePageOpened: bottomEdge.opened && bottomEdge.fullLoaded
292+ readonly property string currentViewContactId: viewPage && viewPage.contact ? viewPage.contact.contactId : ""
293 readonly property bool isEmpty: (contactList.count === 0)
294 readonly property bool allowToQuit: (application.callbackApplication.length > 0)
295 readonly property var contactModel: contactList.listModel ? contactList.listModel : null
296- readonly property bool searching: (state === "searching" || state === "newphoneSearching")
297+ readonly property bool searching: state === "searching"
298+ readonly property string headerTitle: pageHeader.title
299
300 // this function is used to reset the contact list page to the default state if it was called
301 // from the uri. For example when called to add a new contact
302 function returnToNormalState()
303 {
304- // these two states are the only state that need to be reset
305- if (state == "newphoneSearching" || state == "newphone") {
306- state = "default"
307- }
308 application.callbackApplication = ""
309 }
310
311 function createContactWithPhoneNumber(phoneNumber)
312 {
313 var newContact = ContactsJS.createEmptyContact(phoneNumber, mainPage);
314- openEditPage({model: contactList.listModel,
315- contact: newContact,
316- initialFocusSection: "name"},
317- mainPage);
318- }
319-
320- function openEditPage(editPageProperties, sourcePage) {
321- var component = Qt.createComponent(Qt.resolvedUrl("ABContactEditorPage.qml"))
322- if (component.status === Component.Ready) {
323- mainPage.contactEditorPage = component.createObject(mainPage, editPageProperties)
324- pageStack.addPageToNextColumn(sourcePage, mainPage.contactEditorPage)
325+ if (bottomEdgeLoader.status == Loader.Ready) {
326+ bottomEdgeLoader.editContact(newContact)
327+ } else {
328+ contactList.currentIndex = -1
329+ var incubator = pageStack.addPageToNextColumn(mainPage,
330+ Qt.resolvedUrl("ABContactEditorPage.qml"),
331+ { model: mainPage.contactModel,
332+ contact: newContact,
333+ backIconName: 'back',
334+ enabled: false
335+ })
336 }
337 }
338
339 function openViewPage(viewPageProperties) {
340 var component = Qt.createComponent(Qt.resolvedUrl("ABContactViewPage.qml"))
341- if (component.status === Component.Ready) {
342- mainPage.contactViewPage = component.createObject(mainPage, viewPageProperties)
343- pageStack.addPageToNextColumn(mainPage, mainPage.contactViewPage)
344+ var incubator = pageStack.addPageToNextColumn(mainPage, component, viewPageProperties)
345+ if (incubator && (incubator.status === Component.Loading)) {
346+ incubator.onStatusChanged = function(status) {
347+ if (status === Component.Ready)
348+ mainPage.viewPage = incubator.object
349+ }
350+ } else if (incubator && incubator.status ===- Component.Ready) {
351+ mainPage.viewPage = incubator.object
352+ } else {
353+ mainPage.viewPage = null
354 }
355 }
356
357 function showContact(contact)
358 {
359 var currentContact = contactList.listModel.contacts[contactList.currentIndex]
360- if (currentContact && contactViewPage && contactViewPage.contact && (contactViewPage.contact.contactId === currentContact.contactId)) {
361- console.debug("Skip show contact")
362+ if (currentContact && (mainPage.currentViewContactId === currentContact.contactId)) {
363+ // contact view already opened with this contact
364 return
365 }
366
367@@ -109,13 +111,6 @@
368 contactId: contactId});
369 }
370
371- function addPhoneToContact(contactId, phoneNumber)
372- {
373- openViewPage({model: contactList.listModel,
374- contactId: contactId,
375- addPhoneToContact: phoneNumber});
376- }
377-
378 function importContact(urls)
379 {
380 mainPage._busyDialog = PopupUtils.open(busyDialogComponent, mainPage)
381@@ -157,20 +152,6 @@
382 }
383 }
384
385- function addNewPhone(phoneNumber)
386- {
387- newPhoneToAdd = phoneNumber
388- state = "newphone"
389- contactList.reset()
390- }
391-
392- function showContactEditorPage(editorPage) {
393- contactList.currentIndex = -1;
394- mainPage.contactEditorPage = editorPage;
395- pageStack.addPageToNextColumn(mainPage, editorPage);
396- editorPage.contactSaved.connect(onNewContactSaved);
397- editorPage.enabled = true
398- }
399
400 function onNewContactSaved(contact) {
401 _creatingContact = true
402@@ -190,13 +171,57 @@
403 {
404 if ((contactList.currentIndex >= 0) && (pageStack.columns > 1)) {
405 var currentContact = contactList.listModel.contacts[contactList.currentIndex]
406- if (contactViewPage && contactViewPage.contact && (contactViewPage.contact.contactId === currentContact.contactId))
407+ if (!currentContact) {
408+ var component = Qt.createComponent(Qt.resolvedUrl("ABMultiColumnEmptyState.qml"))
409+ var searching = contactList.filterTerm !== ""
410+ pageStack.addPageToNextColumn(mainPage, component,
411+ { headerTitle: searching ? i18n.tr("No contact found") : i18n.tr("No contacts") })
412+ return
413+ }
414+ if (currentContact && (mainPage.currentViewContactId === currentContact.contactId))
415 return
416
417 contactList.view._fetchContact(contactList.currentIndex, currentContact)
418 }
419 }
420
421+ header: PageHeader {
422+ id: pageHeader
423+
424+ property alias leadingActions: leadingBar.actions
425+ property alias trailingActions: trailingBar.actions
426+ property alias sectionsModel: sections.model
427+
428+ title: i18n.tr("Contacts")
429+ //flickable: contactList.view
430+ trailingActionBar {
431+ id: trailingBar
432+ }
433+ leadingActionBar {
434+ id: leadingBar
435+ }
436+ extension: Sections {
437+ id: sections
438+ anchors {
439+ left: parent.left
440+ leftMargin: units.gu(2)
441+ bottom: parent.bottom
442+ }
443+ onSelectedIndexChanged: {
444+ switch (selectedIndex) {
445+ case 0:
446+ contactList.showAllContacts()
447+ break;
448+ case 1:
449+ contactList.showFavoritesContacts()
450+ break;
451+ default:
452+ break;
453+ }
454+ }
455+ }
456+ }
457+
458 // This timer is to avoid fetch unecessary contact if the user select the contacts too fast
459 // while navigating on contact list with keyboard
460 Timer {
461@@ -215,23 +240,23 @@
462 objectName: "contactListView"
463
464 focus: true
465- showImportOptions: !mainPage.pickMode &&
466- mainPage.newPhoneToAdd === "" &&
467- (!mainPage.contactEditorPage || !mainPage.contactEditorPage.active)
468+ showImportOptions: !mainPage.pickMode &&
469+ pageStack.bottomEdge &&
470+ (pageStack.bottomEdge.status === BottomEdge.Hidden)
471 anchors {
472 top: parent.top
473+ topMargin: pageHeader.height
474 left: parent.left
475 bottom: keyboard.top
476 right: parent.right
477 }
478- currentIndex: 0
479+ currentIndex: -1
480 filterTerm: searchField.text
481 multiSelectionEnabled: true
482 multipleSelection: (mainPage.pickMode && mainPage.pickMultipleContacts) || !mainPage.pickMode
483- highlightSelected: application.usingKeyboard && !mainPage._creatingContact
484+ showNewContact: (pageStack.columns > 1) && pageStack.bottomEdge && (pageStack.bottomEdge.status === BottomEdge.Committed)
485+ highlightSelected: pageStack.hasKeyboard && !mainPage._creatingContact
486 onAddContactClicked: mainPage.createContactWithPhoneNumber(label)
487- onAddNewContactClicked: mainPage.createContactWithPhoneNumber(mainPage.newPhoneToAdd)
488-
489 onContactClicked: mainPage.showContact(contact)
490 onIsInSelectionModeChanged: mainPage.state = isInSelectionMode ? "selection" : "default"
491 onSelectionCanceled: {
492@@ -252,14 +277,17 @@
493 contactList.currentIndex = 0
494 }
495 }
496+
497 onCountChanged: {
498 if (mainPage.active &&
499 (pageStack.columns > 1) &&
500- (contactList.currentIndex === -1)) {
501+ (contactList.currentIndex === -1) &&
502+ (pageStack.bottomEdge.status === BottomEdge.Hidden)) {
503 contactList.currentIndex = 0
504 }
505 mainPage.delayFetchContact()
506 }
507+
508 onCurrentIndexChanged: {
509 if (!mainPage.contactIndex)
510 mainPage.delayFetchContact()
511@@ -267,7 +295,7 @@
512
513 Keys.onReturnPressed: {
514 var currentContact = contactList.listModel.contacts[contactList.currentIndex]
515- if (contactViewPage && contactViewPage.contact && (contactViewPage.contact.contactId === currentContact.contactId))
516+ if (mainPage.currentViewContactId === currentContact.contactId)
517 return
518
519 contactList.view._fetchContact(contactList.currentIndex, currentContact)
520@@ -277,7 +305,7 @@
521 //because of that we need this
522 Keys.onRightPressed: {
523 // only move focus away when in edit mode
524- if (mainPage.contactEditorPage) {
525+ if (pageStack.bottomEdge.status === BottomEdge.Committed) {
526 var next = pageStack._nextItemInFocusChain(view, true)
527 if (next === searchField) {
528 pageStack._nextItemInFocusChain(next, true)
529@@ -292,8 +320,6 @@
530 }
531 }
532
533-
534-
535 TextField {
536 id: searchField
537
538@@ -302,58 +328,57 @@
539 readonly property bool _allowFocus: true
540
541 anchors {
542+ top: parent ? parent.top : undefined
543 left: parent ? parent.left : undefined
544 right: parent ? parent.right : undefined
545+ margins: units.gu(1)
546 rightMargin: units.gu(2)
547 }
548
549 visible: false
550 inputMethodHints: Qt.ImhNoPredictiveText
551 placeholderText: i18n.tr("Search...")
552+ onVisibleChanged: {
553+ if (visible) {
554+ if (activeFocus) {
555+ Qt.inputMethod.show()
556+ } else {
557+ searchField.forceActiveFocus()
558+ }
559+ }
560+ }
561+
562 Keys.onTabPressed: contactList.forceActiveFocus()
563 Keys.onDownPressed: contactList.forceActiveFocus()
564 }
565
566- Connections {
567- target: mainPage.head.sections
568- onSelectedIndexChanged: {
569- switch (mainPage.head.sections.selectedIndex) {
570- case 0:
571- contactList.showAllContacts()
572- break;
573- case 1:
574- contactList.showFavoritesContacts()
575- break;
576- default:
577- break;
578- }
579- }
580- }
581-
582 state: "default"
583 states: [
584- PageHeadState {
585+ State {
586 id: defaultState
587-
588 name: "default"
589- backAction: Action {
590- visible: mainPage.allowToQuit
591- iconName: "back"
592- text: i18n.tr("Quit")
593- onTriggered: {
594- application.goBackToSourceApp()
595- mainPage.returnToNormalState()
596+
597+ property list<QtObject> leadingActions: [
598+ Action {
599+ visible: mainPage.allowToQuit
600+ iconName: "back"
601+ text: i18n.tr("Quit")
602+ onTriggered: {
603+ application.goBackToSourceApp()
604+ mainPage.returnToNormalState()
605+ }
606 }
607- }
608- actions: [
609+ ]
610+
611+ property list<QtObject> trailingActions: [
612 Action {
613 text: i18n.tr("Search")
614 iconName: "search"
615 visible: !mainPage.isEmpty
616- enabled: mainPage.state === "default"
617+ enabled: visible && (mainPage.state === "default")
618 shortcut: "Ctrl+F"
619 onTriggered: {
620- mainPage.state = (mainPage.state === "newphone" ? "newphoneSearching" : "searching")
621+ mainPage.state = "searching"
622 contactList.showAllContacts()
623 searchField.forceActiveFocus()
624 }
625@@ -391,73 +416,85 @@
626 }
627 }
628 ]
629+
630 PropertyChanges {
631- target: mainPage.head
632- backAction: defaultState.backAction
633- actions: defaultState.actions
634+ target: pageHeader
635+
636 // TRANSLATORS: this refers to all contacts
637- sections.model: [i18n.tr("All"), i18n.tr("Favorites")]
638+ sectionsModel: [i18n.tr("All"), i18n.tr("Favorites")]
639+ leadingActions: defaultState.leadingActions
640+ trailingActions: defaultState.trailingActions
641 }
642 PropertyChanges {
643 target: searchField
644 text: ""
645 }
646 PropertyChanges {
647- target: bottomEdge
648+ target: bottomEdgeLoader
649 enabled: true
650 }
651 },
652- PageHeadState {
653+ State {
654 id: searchingState
655-
656 name: "searching"
657- backAction: Action {
658- iconName: "back"
659- text: i18n.tr("Cancel")
660- enabled: mainPage.state === "searching" && !mainPage.contactEditorPage && mainPage.active
661- shortcut: "Esc"
662- onTriggered: {
663- mainPage.head.sections.selectedIndex = 0
664- mainPage.state = (mainPage.state === "newphoneSearching" ? "newphone" : "default")
665- contactList.forceActiveFocus()
666+
667+ property list<QtObject> leadingActions: [
668+ Action {
669+ iconName: "back"
670+ text: i18n.tr("Cancel")
671+ enabled: (mainPage.state === "searching") &&
672+ mainPage.active &&
673+ (!pageStack.bottomEdge ||
674+ (pageStack.bottomEdge && (pageStack.bottomEdge.status === BottomEdge.Hidden))) &&
675+ ((pageStack.columns === 1) ||
676+ (mainPage.viewPage && mainPage.viewPage.active))
677+ shortcut:"Esc"
678+ onTriggered: {
679+ mainPage.head.sections.selectedIndex = 0
680+ mainPage.state = "default"
681+ contactList.forceActiveFocus()
682+ }
683 }
684- }
685-
686- PropertyChanges {
687- target: bottomEdge
688- enabled: false
689- }
690-
691- PropertyChanges {
692- target: mainPage.head
693- backAction: searchingState.backAction
694+ ]
695+
696+ PropertyChanges {
697+ target: pageHeader
698+
699 contents: searchField
700+ leadingActions: searchingState.leadingActions
701+
702 }
703
704 PropertyChanges {
705- target: searchField
706- text: ""
707+ target: bottomEdgeLoader
708+ enabled: false
709 }
710
711 PropertyChanges {
712 target: searchField
713 visible: true
714 focus: true
715+ text: ""
716 }
717 },
718- PageHeadState {
719+ State {
720 id: selectionState
721-
722 name: "selection"
723- backAction: Action {
724- text: i18n.tr("Cancel selection")
725- iconName: "back"
726- enabled: mainPage.state === "selection"
727- shortcut: "Esc"
728- onTriggered: contactList.cancelSelection()
729- }
730- actions: [
731+
732+ property list<QtObject> leadingActions: [
733 Action {
734+ objectName: "cancel"
735+ name: "cancel"
736+ text: i18n.tr("Cancel selection")
737+ iconName: "back"
738+ enabled: mainPage.state === "selection"
739+ onTriggered: contactList.cancelSelection()
740+ shortcut: "Esc"
741+ }
742+ ]
743+
744+ property list<QtObject> trailingActions: [
745+ Action {
746 text: (contactList.selectedItems.count === contactList.count) ? i18n.tr("Unselect All") : i18n.tr("Select All")
747 iconName: "select"
748 onTriggered: {
749@@ -506,76 +543,48 @@
750 }
751 }
752 ]
753- PropertyChanges {
754- target: mainPage.head
755- backAction: selectionState.backAction
756- actions: selectionState.actions
757- }
758- PropertyChanges {
759- target: bottomEdge
760- enabled: false
761- }
762- },
763- PageHeadState {
764- name: "newphone"
765- extend: "default"
766- head: mainPage.head
767- PropertyChanges {
768- target: contactList
769- showAddNewButton: true
770- }
771- PropertyChanges {
772- target: mainPage
773- title: i18n.tr("Add contact")
774- }
775- PropertyChanges {
776- target: bottomEdge
777- enabled: false
778- }
779- PropertyChanges {
780- target: contactList
781- detailToPick: -1
782- }
783- },
784- PageHeadState {
785- name: "newphoneSearching"
786- extend: "searching"
787- head: mainPage.head
788- PropertyChanges {
789- target: contactList
790- detailToPick: -1
791- showAddNewButton: true
792- }
793- PropertyChanges {
794- target: bottomEdge
795- enabled: false
796- }
797- },
798- PageHeadState {
799+
800+ PropertyChanges {
801+ target: pageHeader
802+
803+ leadingActions: selectionState.leadingActions
804+ trailingActions: selectionState.trailingActions
805+ }
806+
807+ PropertyChanges {
808+ target: bottomEdgeLoader
809+ enabled: false
810+ }
811+ },
812+ State {
813 id: vcardImportedState
814-
815 name: "vcardImported"
816- backAction: Action {
817- iconName: "back"
818- text: i18n.tr("Back")
819- onTriggered: {
820- contactList.forceActiveFocus()
821- mainPage.state = "default"
822- importedIdsFilter.ids = []
823+
824+ property list<QtObject> leadingActions: [
825+ Action {
826+ objectName: "cancel"
827+ name: "cancel"
828+ iconName: "back"
829+ text: i18n.tr("Back")
830+ onTriggered: {
831+ contactList.forceActiveFocus()
832+ mainPage.state = "default"
833+ importedIdsFilter.ids = []
834+ }
835 }
836- }
837- PropertyChanges {
838- target: mainPage.head
839- backAction: vcardImportedState.backAction
840- }
841- PropertyChanges {
842- target: bottomEdge
843- enabled: false
844- }
845- PropertyChanges {
846- target: mainPage
847+ ]
848+
849+ PropertyChanges {
850+ target: pageHeader
851+
852+ leadingActions: vcardImportedState.leadingActions
853 title: i18n.tr("Imported contacts")
854 }
855+
856+ PropertyChanges {
857+ target: bottomEdgeLoader
858+ enabled: false
859+ }
860 }
861 ]
862
863@@ -614,54 +623,30 @@
864
865 KeyboardRectangle {
866 id: keyboard
867+ active: mainPage.active &&
868+ (pageStack.bottomEdge && (pageStack.bottomEdge.status === BottomEdge.Hidden))
869 }
870
871- Column {
872+ ABEmptyState {
873 id: emptyStateScreen
874
875- anchors.centerIn: parent
876+ anchors {
877+ verticalCenter: parent.verticalCenter
878+ left: parent.left
879+ right: parent.right
880+ leftMargin: units.gu(6)
881+ rightMargin: units.gu(6)
882+ }
883+
884 height: childrenRect.height
885- width: childrenRect.width
886- spacing: units.gu(2)
887- visible: (!contactList.busy &&
888+ visible: ((pageStack.columns === 1) &&
889+ !contactList.busy &&
890 !contactList.favouritesIsSelected &&
891 mainPage.isEmpty &&
892- (mainPage.newPhoneToAdd === "") &&
893- !(contactList.filterTerm && contactList.filterTerm !== "")) &&
894- bottomEdge.visible
895-
896- Behavior on visible {
897- SequentialAnimation {
898- PauseAnimation {
899- duration: !emptyStateScreen.visible ? 500 : 0
900- }
901- PropertyAction {
902- target: emptyStateScreen
903- property: "visible"
904- }
905- }
906- }
907-
908- Icon {
909- id: emptyStateIcon
910- anchors.horizontalCenter: emptyStateLabel.horizontalCenter
911- height: units.gu(5)
912- width: units.gu(5)
913- opacity: 0.3
914- name: "contact"
915- }
916- Label {
917- id: emptyStateLabel
918- width: mainPage.width - units.gu(12)
919- height: paintedHeight
920- text: mainPage.pickMode ?
921- i18n.tr("You have no contacts.") :
922- i18n.tr("Create a new contact by swiping up from the bottom of the screen.")
923- color: "#5d5d5d"
924- fontSize: "x-large"
925- wrapMode: Text.WordWrap
926- horizontalAlignment: Text.AlignHCenter
927- }
928+ !(contactList.filterTerm && contactList.filterTerm !== ""))
929+ text: mainPage.pickMode ?
930+ i18n.tr("You have no contacts.") :
931+ i18n.tr("Create a new contact by swiping up from the bottom of the screen.")
932 }
933
934 ContactExporter {
935@@ -753,103 +738,26 @@
936 }
937 }
938
939- Component {
940- id: editorPageBottomEdge
941- ABContactEditorPage {
942- backIconName: "down"
943- implicitWidth: mainPage.width
944- implicitHeight: mainPage.height
945- model: contactList.listModel
946- contact: ContactsJS.createEmptyContact("", mainPage)
947- initialFocusSection: "name"
948- enabled: false
949- }
950- }
951-
952- Component {
953- id: emptyContact
954- ContactsUI.ContactDelegate {
955- property Contact contact: Contact {
956- Name {
957- firstName: i18n.tr("New contact")
958- }
959- Avatar {
960- imageUrl: "image://theme/contact"
961- }
962- }
963- width: mainPage.width
964- }
965- }
966-
967- AB.BottomEdge {
968- id: bottomEdge
969- objectName: "bottomEdge"
970-
971- property var incubator
972-
973- // FIXME: this is a workaround for the lack of fully asynchronous loading
974- // of Pages in AdaptativePageLayout
975- function createObjectAsynchronously(url, properties, callback) {
976- var component = Qt.createComponent(url, Component.Asynchronous);
977- if (component.status == Component.Ready) {
978- incubateObject(component, properties, callback);
979- } else {
980- component.onStatusChanged.connect(function(status) {
981- if (status == Component.Ready) {
982- incubateObject(component, properties, callback);
983- }
984- });
985- }
986- }
987-
988- function incubateObject(component, properties, callback) {
989- if (component.status == Component.Ready) {
990- incubator = component.incubateObject(null,
991- properties,
992- Qt.Asynchronous);
993- incubator.onStatusChanged = function(status) {
994- if (status == Component.Ready) {
995- callback(incubator.object);
996- incubator = null;
997- }
998- }
999- }
1000- }
1001-
1002- function loadEditorPage() {
1003- var newContact = ContactsJS.createEmptyContact("", mainPage);
1004- createObjectAsynchronously(Qt.resolvedUrl("ABContactEditorPage.qml"),
1005- {model: contactList.listModel,
1006- enabled: false,
1007- contact: newContact,
1008- initialFocusSection: "name"},
1009- showContactEditorPage);
1010- }
1011-
1012- anchors.fill: parent
1013- contentComponent: pageStack.columns === 1 ? editorPageBottomEdge : emptyContact
1014- flickable: contactList
1015- iconName: "contact-new"
1016- backGroundEffectEnabled: pageStack.columns === 1
1017-
1018- onBottomEdgeLoaded: contactList.forceActiveFocus()
1019- onOpenBegin: {
1020- contactList.prepareNewContact = true;
1021- contactList.positionViewAtBeginning();
1022- if (pageStack.columns > 1) {
1023- loadEditorPage();
1024- }
1025- }
1026- onOpenEnd: {
1027- contactList.showNewContact = true;
1028- if (pageStack.columns <= 1) {
1029- showContactEditorPage(bottomEdge.content);
1030- }
1031- }
1032-
1033- onClicked: {
1034- bottomEdge.open();
1035- }
1036+ Loader {
1037+ id: bottomEdgeLoader
1038+
1039+ enabled: false
1040+ active: (pageStack.columns === 1) && bottomEdgeLoader.enabled
1041+ asynchronous: true
1042+ sourceComponent: ABNewContactBottomEdge {
1043+ parent: mainPage
1044+ modelToEdit: mainPage.contactModel
1045+ hint.flickable: contactList.view
1046+ pageStack: mainPage.pageStack
1047+ enabled: mainPage.active
1048+ }
1049+ }
1050+
1051+ Binding {
1052+ target: pageStack
1053+ property: 'bottomEdge'
1054+ value: bottomEdgeLoader.item
1055+ when: bottomEdgeLoader.status == Loader.Ready
1056 }
1057
1058 Connections {
1059@@ -861,6 +769,7 @@
1060 mainPage.contactIndex = null
1061 // at this point the operation has finished already
1062 mainPage._creatingContact = false
1063+ fetchNewContactTimer.restart()
1064 }
1065 }
1066 onImportCompleted: {
1067@@ -890,34 +799,20 @@
1068 }
1069
1070 Connections {
1071- id: contactViewPageConnections
1072-
1073- ignoreUnknownSignals: true
1074- onEditContact: openEditPage(editPageProperties, mainPage.contactViewPage);
1075- onActiveChanged: {
1076- if (mainPage.contactViewPage &&
1077- !mainPage.contactViewPage.active &&
1078- (mainPage.contactEditorPage == null)) { // not editing
1079- mainPage.contactViewPage = null
1080+ target: pageStack.bottomEdge
1081+ onCommitCompleted: {
1082+ if (mainPage.state !== "default") {
1083+ mainPage.head.sections.selectedIndex = 0
1084+ mainPage.state = "default"
1085 }
1086 }
1087- }
1088-
1089- Connections {
1090- id: contactEditorPageConnections
1091-
1092- ignoreUnknownSignals: true
1093- onActiveChanged: {
1094- if (mainPage.contactEditorPage && !mainPage.contactEditorPage.active) {
1095- contactList.prepareNewContact = false;
1096- contactList.showNewContact = false;
1097- bottomEdge.close();
1098- mainPage.contactEditorPage = null
1099- contactList.forceActiveFocus()
1100- bottomEdge.enabled = true
1101- } else if (mainPage.contactEditorPage && !mainPage.contactEditorPage.active) {
1102- bottomEdge.enabled = false
1103+ onCollapseCompleted: {
1104+ if (!mainPage._creatingContact) {
1105+ if (contactList.currentIndex === -1)
1106+ contactList.currentIndex = 0
1107+ mainPage.delayFetchContact()
1108 }
1109+ contactList.forceActiveFocus()
1110 }
1111 }
1112 }
1113
1114=== modified file 'src/imports/ABContactViewPage.qml'
1115--- src/imports/ABContactViewPage.qml 2015-12-14 18:29:23 +0000
1116+++ src/imports/ABContactViewPage.qml 2016-01-07 13:19:12 +0000
1117@@ -1,4 +1,4 @@
1118-/*
1119+/*
1120 * Copyright (C) 2012-2015 Canonical, Ltd.
1121 *
1122 * This program is free software; you can redistribute it and/or modify
1123@@ -27,10 +27,35 @@
1124 id: root
1125 objectName: "contactViewPage"
1126
1127- property string addPhoneToContact: ""
1128- signal editContact(var editPageProperties)
1129-
1130- // Override Action buttom to add shortcut to it
1131+ property bool editing: false
1132+ // used by autopilot test
1133+ readonly property string headerTitle: header.title
1134+
1135+ function editContact(contact)
1136+ {
1137+ if (editing)
1138+ return
1139+ editing = true
1140+ var component = Qt.createComponent(Qt.resolvedUrl("ABContactEditorPage.qml"))
1141+ var incubator = pageStack.addPageToCurrentColumn(root,
1142+ component,
1143+ { model: root.model,
1144+ contact: contact,
1145+ backIconName: 'back'})
1146+ if (incubator && (incubator.status === Component.Loading)) {
1147+ incubator.onStatusChanged = function(status) {
1148+ if (status === Component.Ready) {
1149+ incubator.object.Component.destruction.connect(function() {
1150+ root.editing = false;
1151+ });
1152+ }
1153+ }
1154+ } else {
1155+ editing = false
1156+ }
1157+ }
1158+
1159+ // Shortcut in case of single column
1160 Action {
1161 id: backAction
1162
1163@@ -40,7 +65,8 @@
1164 onTriggered: pageStack.removePages(root)
1165 }
1166
1167- head.actions: [
1168+
1169+ headerActions: [
1170 Action {
1171 objectName: "share"
1172 name: "share"
1173@@ -60,12 +86,9 @@
1174
1175 text: i18n.tr("Edit")
1176 iconName: "edit"
1177- enabled: root.active
1178+ enabled: root.active && !editing
1179 shortcut: "Ctrl+e"
1180- onTriggered: {
1181- editContact({model: root.model,
1182- contact: root.contact});
1183- }
1184+ onTriggered: root.editContact(root.contact)
1185 }
1186 ]
1187
1188@@ -80,23 +103,6 @@
1189 height: implicitHeight
1190 }
1191
1192- // This will load the contact information when the app was launched with
1193- // the URI: addressbook:///contact?id=<id>
1194- onContactFetched: {
1195- if (root.addPhoneToContact != "") {
1196- var detailSourceTemplate = "import QtContacts 5.0; PhoneNumber{ number: \"" + root.addPhoneToContact.trim() + "\" }"
1197- var newDetail = Qt.createQmlObject(detailSourceTemplate, contact)
1198- if (newDetail) {
1199- contact.addDetail(newDetail)
1200- editContact({ model: root.model,
1201- contact: contact,
1202- initialFocusSection: "phones",
1203- newDetails: [newDetail] })
1204- root.addPhoneToContact = ""
1205- }
1206- }
1207- }
1208-
1209 onActionTrigerred: {
1210 // "default" action is used inside of the apps (dialer, messaging) to trigger
1211 // actions based on context.
1212@@ -112,4 +118,27 @@
1213 id: contactShareComponent
1214 ContactSharePage {}
1215 }
1216+
1217+ Loader {
1218+ id: bottomEdgeLoader
1219+
1220+ active: (pageStack.columns > 1)
1221+ asynchronous: true
1222+ sourceComponent: ABNewContactBottomEdge {
1223+ id: bottomEdge
1224+
1225+ parent: root
1226+ height: root.height
1227+ modelToEdit: root.model
1228+ hint.flickable: root.flickable
1229+ pageStack: root.pageStack
1230+ }
1231+ }
1232+
1233+ Binding {
1234+ target: pageStack
1235+ property: 'bottomEdge'
1236+ value: bottomEdgeLoader.item
1237+ when: bottomEdgeLoader.status === Loader.Ready
1238+ }
1239 }
1240
1241=== added file 'src/imports/ABEmptyState.qml'
1242--- src/imports/ABEmptyState.qml 1970-01-01 00:00:00 +0000
1243+++ src/imports/ABEmptyState.qml 2016-01-07 13:19:12 +0000
1244@@ -0,0 +1,61 @@
1245+/*
1246+ * Copyright (C) 2012-2016 Canonical, Ltd.
1247+ *
1248+ * This program is free software; you can redistribute it and/or modify
1249+ * it under the terms of the GNU General Public License as published by
1250+ * the Free Software Foundation; version 3.
1251+ *
1252+ * This program is distributed in the hope that it will be useful,
1253+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1254+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1255+ * GNU General Public License for more details.
1256+ *
1257+ * You should have received a copy of the GNU General Public License
1258+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1259+ */
1260+
1261+import QtQuick 2.4
1262+import Ubuntu.Components 1.3
1263+
1264+Column {
1265+ id: root
1266+
1267+ property alias text: emptyStateLabel.text
1268+
1269+ spacing: units.gu(2)
1270+ //implicitHeight: childrenRect.height
1271+
1272+ Behavior on visible {
1273+ SequentialAnimation {
1274+ PauseAnimation {
1275+ duration: !root.visible ? 500 : 0
1276+ }
1277+ PropertyAction {
1278+ target: root
1279+ property: "visible"
1280+ }
1281+ }
1282+ }
1283+
1284+ Icon {
1285+ id: emptyStateIcon
1286+ anchors.horizontalCenter: emptyStateLabel.horizontalCenter
1287+ height: units.gu(5)
1288+ width: units.gu(5)
1289+ opacity: 0.3
1290+ name: "contact"
1291+ }
1292+ Label {
1293+ id: emptyStateLabel
1294+ anchors {
1295+ left: parent.left
1296+ right: parent.right
1297+ }
1298+ height: paintedHeight
1299+ text: i18n.tr("Create a new contact by swiping up from the bottom of the screen.")
1300+ color: "#5d5d5d"
1301+ fontSize: "x-large"
1302+ wrapMode: Text.WordWrap
1303+ horizontalAlignment: Text.AlignHCenter
1304+ }
1305+}
1306
1307=== added file 'src/imports/ABMultiColumnEmptyState.qml'
1308--- src/imports/ABMultiColumnEmptyState.qml 1970-01-01 00:00:00 +0000
1309+++ src/imports/ABMultiColumnEmptyState.qml 2016-01-07 13:19:12 +0000
1310@@ -0,0 +1,75 @@
1311+/*
1312+ * Copyright (C) 2012-2016 Canonical, Ltd.
1313+ *
1314+ * This program is free software; you can redistribute it and/or modify
1315+ * it under the terms of the GNU General Public License as published by
1316+ * the Free Software Foundation; version 3.
1317+ *
1318+ * This program is distributed in the hope that it will be useful,
1319+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1320+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1321+ * GNU General Public License for more details.
1322+ *
1323+ * You should have received a copy of the GNU General Public License
1324+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1325+ */
1326+
1327+import QtQuick 2.4
1328+import Ubuntu.Components 1.3
1329+
1330+
1331+Page {
1332+ id: root
1333+
1334+ property string headerTitle: i18n.tr("No contacts")
1335+
1336+ header: PageHeader {
1337+ title: root.headerTitle
1338+ }
1339+
1340+ ABEmptyState {
1341+ id: emptyStateScreen
1342+
1343+ anchors {
1344+ verticalCenter: parent.verticalCenter
1345+ left: parent.left
1346+ right: parent.right
1347+ leftMargin: units.gu(6)
1348+ rightMargin: units.gu(6)
1349+ }
1350+ height: childrenRect.height
1351+ text: i18n.tr("Create a new contact by swiping up from the bottom of the screen.")
1352+ }
1353+
1354+ Loader {
1355+ id: bottomEdgeLoader
1356+
1357+ active: (pageStack.columns > 1)
1358+ asynchronous: true
1359+ sourceComponent: ABNewContactBottomEdge {
1360+ id: bottomEdge
1361+
1362+ parent: root
1363+ height: root.height
1364+ modelToEdit: root.pageStack.contactListPage.contactModel
1365+ hint.flickable: root.flickable
1366+ pageStack: root.pageStack
1367+ }
1368+ }
1369+
1370+ Binding {
1371+ target: pageStack
1372+ property: 'bottomEdge'
1373+ value: bottomEdgeLoader.item
1374+ when: bottomEdgeLoader.status === Loader.Ready
1375+ }
1376+
1377+ Connections {
1378+ target: pageStack
1379+ onColumnsChanged: {
1380+ if (pageStack.columns === 1) {
1381+ pageStack.removePages(root)
1382+ }
1383+ }
1384+ }
1385+}
1386
1387=== added file 'src/imports/ABNewContactBottomEdge.qml'
1388--- src/imports/ABNewContactBottomEdge.qml 1970-01-01 00:00:00 +0000
1389+++ src/imports/ABNewContactBottomEdge.qml 2016-01-07 13:19:12 +0000
1390@@ -0,0 +1,89 @@
1391+/*
1392+ * Copyright (C) 2012-2015 Canonical, Ltd.
1393+ *
1394+ * This program is free software; you can redistribute it and/or modify
1395+ * it under the terms of the GNU General Public License as published by
1396+ * the Free Software Foundation; version 3.
1397+ *
1398+ * This program is distributed in the hope that it will be useful,
1399+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1400+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1401+ * GNU General Public License for more details.
1402+ *
1403+ * You should have received a copy of the GNU General Public License
1404+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1405+ */
1406+
1407+import QtQuick 2.4
1408+import Ubuntu.Components 1.3
1409+import Ubuntu.Contacts 0.1 as ContactsUI
1410+
1411+BottomEdge {
1412+ id: bottomEdge
1413+ objectName: "bottomEdge"
1414+
1415+ property var modelToEdit: null
1416+ property var pageStack: null
1417+ property var _contactToEdit: null
1418+ // WORKAROUND: BottomEdge component loads the page async while draging it
1419+ // this cause a very bad visual.
1420+ // To avoid that we create it as soon as the component is ready and keep
1421+ // it invisible until the user start to drag it.
1422+ property var _realPage: null
1423+
1424+ function editContact(contact)
1425+ {
1426+ _contactToEdit = contact
1427+ commit()
1428+ }
1429+
1430+ hint {
1431+ action: Action {
1432+ iconName: "contact-new"
1433+ shortcut: "ctrl+n"
1434+ enabled: bottomEdge.enabled
1435+
1436+ onTriggered: bottomEdge.commit()
1437+ }
1438+ }
1439+
1440+ contentComponent: Item {
1441+ id: pageContent
1442+
1443+ implicitWidth: bottomEdge.width
1444+ implicitHeight: bottomEdge.height
1445+ children: bottomEdge._realPage
1446+ }
1447+
1448+
1449+ onCommitCompleted: {
1450+ if (bottomEdge._contactToEdit)
1451+ editorPage.contact = bottomEdge._contactToEdit
1452+ bottomEdge._contactToEdit = null
1453+ }
1454+
1455+ onCollapseCompleted: {
1456+ _realPage = editorPageBottomEdge.createObject(null)
1457+ }
1458+
1459+ Component.onCompleted: {
1460+ _realPage = editorPageBottomEdge.createObject(null)
1461+ }
1462+
1463+ Component {
1464+ id: editorPageBottomEdge
1465+
1466+ ABContactEditorPage {
1467+ implicitWidth: bottomEdge.width
1468+ implicitHeight: bottomEdge.height
1469+ contact: ContactsUI.ContactsJS.createEmptyContact("", bottomEdge)
1470+ model: bottomEdge.modelToEdit
1471+ enabled: bottomEdge.status === BottomEdge.Committed
1472+ active: bottomEdge.status === BottomEdge.Committed
1473+ visible: bottomEdge.status !== BottomEdge.Hidden
1474+ onCanceled: bottomEdge.collapse()
1475+ onContactSaved: bottomEdge.collapse()
1476+ pageStack: bottomEdge.pageStack
1477+ }
1478+ }
1479+}
1480
1481=== removed file 'src/imports/BottomEdge.qml'
1482--- src/imports/BottomEdge.qml 2015-12-14 15:31:50 +0000
1483+++ src/imports/BottomEdge.qml 1970-01-01 00:00:00 +0000
1484@@ -1,338 +0,0 @@
1485-/*
1486- * Copyright (C) 2015 Canonical, Ltd.
1487- *
1488- * This program is free software; you can redistribute it and/or modify
1489- * it under the terms of the GNU General Public License as published by
1490- * the Free Software Foundation; version 3.
1491- *
1492- * This program is distributed in the hope that it will be useful,
1493- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1494- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1495- * GNU General Public License for more details.
1496- *
1497- * You should have received a copy of the GNU General Public License
1498- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1499- */
1500-
1501-import QtQuick 2.4
1502-import Ubuntu.Components 1.3
1503-
1504-Item {
1505- id: bottomEdge
1506-
1507- readonly property alias content: bottomEdgeLoader.item
1508- readonly property bool fullLoaded: bottomEdgeLoader.status == Loader.Ready
1509-
1510- property bool opened: false
1511- property Component contentComponent
1512- property string iconName
1513- property Item flickable
1514- property alias backGroundEffectEnabled: darkBg.visible
1515-
1516- signal openBegin
1517- signal openEnd
1518- signal clicked
1519- signal bottomEdgeLoaded
1520-
1521- function open() {
1522- bottomEdge.state = "expanded";
1523- }
1524-
1525- function close() {
1526- bottomEdge.state = "collapsed";
1527- }
1528-
1529- Action {
1530- text: i18n.tr("New contact")
1531- enabled: bottomEdge.visible
1532- shortcut: "Ctrl+N"
1533- onTriggered: bottomEdge.clicked()
1534- }
1535-
1536- Rectangle {
1537- id: darkBg
1538-
1539- anchors.fill: parent
1540- color: "black"
1541- opacity: 0.0
1542- }
1543-
1544- Item {
1545- id: bottomEdgeBody
1546- anchors {
1547- left: parent.left
1548- right: parent.right
1549- }
1550- height: bottomEdgeContent.height
1551-
1552- Item {
1553- id: bottomEdgeContent
1554- anchors {
1555- left: parent.left
1556- right: parent.right
1557- }
1558- height: bottomEdgeLoader.height
1559-
1560- Item {
1561- id: bottomEdgeShadows
1562- anchors.fill: bottomEdgeContent
1563-
1564- BottomEdgeShadow {
1565- anchors.bottom: parent.top
1566- }
1567-
1568- BottomEdgeShadow {
1569- anchors.top: parent.bottom
1570- rotation: 180
1571- }
1572- }
1573-
1574- Rectangle {
1575- anchors.fill: parent
1576- color: Theme.palette.normal.background
1577- }
1578-
1579- Loader {
1580- id: bottomEdgeLoader
1581- sourceComponent: bottomEdge.contentComponent
1582- asynchronous: true
1583- active: bottomEdge.enabled
1584- onStatusChanged: {
1585- if (status === Loader.Ready)
1586- bottomEdge.bottomEdgeLoaded()
1587- }
1588- }
1589- }
1590-
1591- BottomEdgeHint {
1592- id: bottomEdgeHint
1593-
1594- anchors.bottom: bottomEdgeBody.top
1595- iconName: bottomEdge.iconName
1596- onClicked: bottomEdge.clicked()
1597-
1598- Connections {
1599- target: bottomEdgeDragArea
1600- onClosedChanged: {
1601- if (!bottomEdgeDragArea.closed) {
1602- bottomEdgeHint.state = "Visible";
1603- }
1604- }
1605- }
1606-
1607- Connections {
1608- target: flickable
1609- onVerticalVelocityChanged: {
1610- if (!bottomEdgeDragArea.closed) {
1611- return;
1612- }
1613-
1614- if (flickable.verticalVelocity > 0) {
1615- bottomEdgeHint.state = "Hidden";
1616- } else if (flickable.verticalVelocity < 0) {
1617- bottomEdgeHint.state = "Visible";
1618- }
1619- }
1620- }
1621- }
1622- }
1623-
1624-
1625- state: "collapsed"
1626- states: [
1627- State {
1628- name: "collapsed"
1629- ParentChange {
1630- target: bottomEdgeContent
1631- parent: bottomEdgeBody
1632- x: 0
1633- y: 0
1634- }
1635- PropertyChanges {
1636- target: bottomEdgeBody
1637- y: bottomEdgeDragArea.drag.maximumY
1638- }
1639- PropertyChanges {
1640- target: bottomEdgeContent
1641- opacity: 0.0
1642- }
1643- PropertyChanges {
1644- target: darkBg
1645- opacity: 0.0
1646- }
1647- },
1648- State {
1649- name: "expanded"
1650- ParentChange {
1651- target: bottomEdgeContent
1652- parent: bottomEdge
1653- x: 0
1654- y: 0
1655- }
1656- PropertyChanges {
1657- target: bottomEdgeContent
1658- opacity: 1.0
1659- }
1660- PropertyChanges {
1661- target: bottomEdgeBody
1662- y: 0
1663- }
1664- PropertyChanges {
1665- target: bottomEdgeShadows
1666- opacity: 0.0
1667- visible: true
1668- }
1669- PropertyChanges {
1670- target: darkBg
1671- opacity: 0.8
1672- }
1673- },
1674- State {
1675- name: "floating"
1676- when: bottomEdgeDragArea.drag.active
1677- PropertyChanges {
1678- target: bottomEdgeContent
1679- opacity: 1.0
1680- }
1681- PropertyChanges {
1682- target: darkBg
1683- opacity: bottomEdgeBody.y > 0 ? 0.8 - (bottomEdgeBody.y / bottomEdgeDragArea.drag.maximumY) : 0.8
1684- }
1685- }
1686- ]
1687-
1688- transitions: [
1689- Transition {
1690- to: "collapsed"
1691- SequentialAnimation {
1692- alwaysRunToEnd: true
1693- ParallelAnimation {
1694- ParentAnimation {
1695- UbuntuNumberAnimation {
1696- properties: "x,y"
1697- duration: UbuntuAnimation.SlowDuration
1698- target: bottomEdgeContent
1699- }
1700- }
1701- UbuntuNumberAnimation {
1702- target: bottomEdgeBody
1703- property: "y"
1704- duration: UbuntuAnimation.SlowDuration
1705- }
1706- UbuntuNumberAnimation {
1707- target: darkBg
1708- property: "opacity"
1709- duration: UbuntuAnimation.SlowDuration
1710- }
1711- }
1712- PropertyAction {
1713- target: bottomEdgeContent
1714- property: "opacity"
1715- }
1716- ScriptAction {
1717- script: {
1718- bottomEdgeLoader.active = false
1719- bottomEdgeLoader.active = true
1720- bottomEdge.opened = false
1721- }
1722- }
1723- }
1724- },
1725- Transition {
1726- to: "expanded"
1727- SequentialAnimation {
1728- alwaysRunToEnd: true
1729- ParallelAnimation {
1730- ScriptAction {
1731- script: bottomEdge.openBegin()
1732- }
1733- ParentAnimation {
1734- UbuntuNumberAnimation {
1735- properties: "x,y"
1736- duration: UbuntuAnimation.SlowDuration
1737- target: bottomEdgeContent
1738- }
1739- }
1740- UbuntuNumberAnimation {
1741- target: bottomEdgeShadows
1742- property: "opacity"
1743- duration: UbuntuAnimation.SlowDuration
1744- }
1745- UbuntuNumberAnimation {
1746- target: darkBg
1747- property: "opacity"
1748- duration: UbuntuAnimation.SlowDuration
1749- }
1750- }
1751- UbuntuNumberAnimation {
1752- target: bottomEdgeContent
1753- property: "opacity"
1754- duration: UbuntuAnimation.FastDuration
1755- }
1756- ScriptAction {
1757- script: {
1758- bottomEdge.opened = true
1759- bottomEdge.openEnd()
1760- }
1761-
1762- }
1763- }
1764- }
1765- ]
1766-
1767- MouseArea {
1768- id: bottomEdgeDragArea
1769- objectName: "bottomEdgeDragArea"
1770-
1771- property real previousY: -1
1772- property string dragDirection: "None"
1773- property bool closed: drag.target.y == bottomEdgeDragArea.drag.maximumY
1774- && !bottomEdgeDragArea.pressed
1775-
1776- preventStealing: true
1777- propagateComposedEvents: true
1778- drag {
1779- axis: Drag.YAxis
1780- target: bottomEdgeBody
1781- minimumY: 0
1782- maximumY: bottomEdge.height
1783- }
1784-
1785- anchors {
1786- left: parent.left
1787- right: parent.right
1788- bottom: parent.bottom
1789- }
1790- height: bottomEdgeHint.height
1791-
1792- onPressed: {
1793- previousY = mouse.y;
1794- }
1795-
1796- onReleased: {
1797- if (dragDirection === "BottomToTop") {
1798- bottomEdge.state = "expanded";
1799- } else {
1800- bottomEdge.state = "collapsed";
1801- }
1802- previousY = -1;
1803- dragDirection = "None";
1804- }
1805-
1806- onMouseYChanged: {
1807- var yOffset = previousY - mouseY;
1808- // skip if was a small move
1809- if (Math.abs(yOffset) <= units.gu(2)) {
1810- return;
1811- }
1812- previousY = mouseY;
1813- dragDirection = yOffset > 0 ? "BottomToTop" : "TopToBottom";
1814- }
1815- }
1816-
1817- Binding {
1818- target: bottomEdge
1819- property: 'visible'
1820- value: bottomEdge.enabled && !bottomEdge.opened
1821- }
1822-}
1823
1824=== removed file 'src/imports/BottomEdgeShadow.qml'
1825--- src/imports/BottomEdgeShadow.qml 2015-09-10 14:47:38 +0000
1826+++ src/imports/BottomEdgeShadow.qml 1970-01-01 00:00:00 +0000
1827@@ -1,31 +0,0 @@
1828-/*
1829- * Copyright (C) 2015 Canonical, Ltd.
1830- *
1831- * This program is free software; you can redistribute it and/or modify
1832- * it under the terms of the GNU General Public License as published by
1833- * the Free Software Foundation; version 3.
1834- *
1835- * This program is distributed in the hope that it will be useful,
1836- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1837- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1838- * GNU General Public License for more details.
1839- *
1840- * You should have received a copy of the GNU General Public License
1841- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1842- */
1843-
1844-import QtQuick 2.4
1845-import Ubuntu.Components 1.3
1846-
1847-Rectangle {
1848- id: bottomEdgeShadow
1849- anchors {
1850- left: parent.left
1851- right: parent.right
1852- }
1853- height: units.gu(1)
1854- gradient: Gradient {
1855- GradientStop { position: 0.0; color: Qt.rgba(0.0, 0.0, 0.0, 0.0) }
1856- GradientStop { position: 1.0; color: Qt.rgba(0.0, 0.0, 0.0, 0.3) }
1857- }
1858-}
1859
1860=== modified file 'src/imports/CMakeLists.txt'
1861--- src/imports/CMakeLists.txt 2015-11-19 14:41:18 +0000
1862+++ src/imports/CMakeLists.txt 2016-01-07 13:19:12 +0000
1863@@ -4,10 +4,11 @@
1864 ABContactListPage.qml
1865 ABContactEditorPage.qml
1866 ABContactViewPage.qml
1867+ ABEmptyState.qml
1868+ ABNewContactBottomEdge.qml
1869+ ABMultiColumnEmptyState.qml
1870 ContentHubProxy.qml
1871 MainWindow.qml
1872- BottomEdgeShadow.qml
1873- BottomEdge.qml
1874 )
1875
1876 install(FILES ${ADDRESS_BOOK_APP_QMLS}
1877
1878=== modified file 'src/imports/MainWindow.qml'
1879--- src/imports/MainWindow.qml 2015-12-10 19:13:20 +0000
1880+++ src/imports/MainWindow.qml 2016-01-07 13:19:12 +0000
1881@@ -18,6 +18,8 @@
1882 import Ubuntu.Components 1.3
1883 import Ubuntu.Components.Popups 1.3 as Popups
1884
1885+import Unity.InputInfo 0.1
1886+
1887 MainView {
1888 id: mainWindow
1889 objectName: "addressBookAppMainWindow"
1890@@ -47,16 +49,6 @@
1891 }
1892 }
1893
1894- function addphone(contactId, phoneNumber)
1895- {
1896- mainStack.resetStack()
1897- if (mainStack.contactListPage) {
1898- mainStack.contactListPage.addPhoneToContact(contactId, phoneNumber)
1899- } else {
1900- console.error("Add phone to contact requested but ContactListPage not loaded")
1901- }
1902- }
1903-
1904 function pick(single)
1905 {
1906 console.debug("Pick mode:" + single)
1907@@ -88,24 +80,35 @@
1908 }
1909 }
1910
1911- function addnewphone(phoneNumer)
1912- {
1913- mainStack.resetStack()
1914- if (mainStack.contactListPage) {
1915- mainStack.contactListPage.addNewPhone(phoneNumer)
1916- } else {
1917- console.error("Add new phone requested but ContactListPage not loaded")
1918- }
1919- }
1920-
1921 width: units.gu(90)
1922 height: units.gu(71)
1923 anchorToKeyboard: false
1924
1925+ InputDeviceModel {
1926+ id: miceModel
1927+ deviceFilter: InputInfo.Mouse
1928+ }
1929+
1930+ InputDeviceModel {
1931+ id: touchPadModel
1932+ deviceFilter: InputInfo.TouchPad
1933+ }
1934+
1935+ InputDeviceModel {
1936+ id: keyboardsModel
1937+ deviceFilter: InputInfo.Keyboard
1938+ }
1939+
1940+
1941 AdaptivePageLayout {
1942 id: mainStack
1943+ objectName: "mainStack"
1944
1945 property var contactListPage: null
1946+ property var bottomEdge: null
1947+ readonly property bool bottomEdgeOpened: (bottomEdge && bottomEdge.status === BottomEdge.Committed)
1948+ readonly property bool hasMouse: ((miceModel.count > 0) || (touchPadModel.count > 0))
1949+ readonly property bool hasKeyboard: (keyboardsModel.count > 0)
1950
1951 function resetStack()
1952 {
1953@@ -145,7 +148,7 @@
1954 anchors.fill: parent
1955 layouts: [
1956 PageColumnsLayout {
1957- when: mainStack.width >= units.gu(80)
1958+ when: mainStack.width >= units.gu(70)
1959 PageColumn {
1960 maximumWidth: units.gu(50)
1961 minimumWidth: units.gu(40)
1962@@ -163,6 +166,14 @@
1963 }
1964 ]
1965
1966+ onColumnsChanged: {
1967+ if (mainStack.columns > 1) {
1968+ if (mainStack.contactListPage)
1969+ mainStack.contactListPage.fetchContact()
1970+ else
1971+ mainStack.addPageToNextColumn(contactPage, Qt.resolvedUrl("./ABMultiColumnEmptyState.qml"))
1972+ }
1973+ }
1974 }
1975
1976 ABContactListPage {
1977@@ -177,6 +188,16 @@
1978 mainWindow.applicationReady()
1979 }
1980
1981+ // WORKAROUND: Due the missing feature on SDK, they can not detect if
1982+ // there is a mouse attached to device or not. And this will cause the
1983+ // bootom edge component to not work correct on desktop.
1984+ // We will consider that a mouse is always attached until it get implement on SDK.
1985+ Binding {
1986+ target: QuickUtils
1987+ property: "mouseAttached"
1988+ value: mainStack.hasMouse
1989+ }
1990+
1991 Component {
1992 id: errorDialog
1993
1994
1995=== modified file 'src/imports/Settings/SettingsPage.qml'
1996--- src/imports/Settings/SettingsPage.qml 2015-12-16 18:35:33 +0000
1997+++ src/imports/Settings/SettingsPage.qml 2016-01-07 13:19:12 +0000
1998@@ -87,12 +87,12 @@
1999 // FIXME: Using a private property here. This uses the old list item and the only way to change the text
2000 // color is with this property.
2001 // We should remove it when update the app to the new ListItem.
2002- __foregroundColor: (activeFocus && (pageStack.columns > 1)) ? Theme.palette.normal.foregroundText :
2003- Theme.palette.normal.foreground
2004+ __foregroundColor: (activeFocus && pageStack.hasKeyboard) ? Theme.palette.normal.foregroundText :
2005+ Theme.palette.normal.foreground
2006 Rectangle {
2007 color: UbuntuColors.orange
2008 anchors.fill: parent
2009- visible:addGoogleAccountItem.activeFocus
2010+ visible:addGoogleAccountItem.activeFocus && pageStack.hasKeyboard
2011 z: -1
2012 }
2013 }
2014
2015=== modified file 'src/imports/Ubuntu/AddressBook/Base/ContactDetailGroupBase.qml'
2016--- src/imports/Ubuntu/AddressBook/Base/ContactDetailGroupBase.qml 2015-11-19 16:38:36 +0000
2017+++ src/imports/Ubuntu/AddressBook/Base/ContactDetailGroupBase.qml 2016-01-07 13:19:12 +0000
2018@@ -33,9 +33,8 @@
2019 property int minimumHeight: 0
2020 property bool loaded: false
2021 property bool showEmpty: true
2022- property bool forceFocusOnFieldCreation: false
2023
2024- signal newFieldAdded(var index)
2025+ signal newFieldAdded(int fieldIndex, QtObject field)
2026
2027 function reloadDetails(clearFields)
2028 {
2029@@ -145,10 +144,10 @@
2030 if (status === Loader.Ready) {
2031 var newFields = root.inputFields
2032 newFields.push(detailItem.item)
2033- root.newFieldAdded(detailItem.item)
2034+
2035 root.inputFields = newFields
2036- if (root.loaded && root.forceFocusOnFieldCreation) {
2037- item.forceActiveFocus()
2038+ if (root.loaded) {
2039+ root.newFieldAdded(detailItem.item, item)
2040 }
2041 }
2042 }
2043
2044=== modified file 'src/imports/Ubuntu/AddressBook/Base/KeyboardRectangle.qml'
2045--- src/imports/Ubuntu/AddressBook/Base/KeyboardRectangle.qml 2015-10-26 13:18:11 +0000
2046+++ src/imports/Ubuntu/AddressBook/Base/KeyboardRectangle.qml 2016-01-07 13:19:12 +0000
2047@@ -18,10 +18,13 @@
2048
2049 Item {
2050 id: keyboardRect
2051+
2052+ property bool active: true
2053+
2054 anchors.left: parent.left
2055 anchors.right: parent.right
2056 anchors.bottom: parent.bottom
2057- height: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
2058+ height: active && Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
2059
2060 states: [
2061 State {
2062
2063=== modified file 'src/imports/Ubuntu/AddressBook/ContactEditor/ComboButtonAddField.qml'
2064--- src/imports/Ubuntu/AddressBook/ContactEditor/ComboButtonAddField.qml 2015-10-26 13:18:11 +0000
2065+++ src/imports/Ubuntu/AddressBook/ContactEditor/ComboButtonAddField.qml 2016-01-07 13:19:12 +0000
2066@@ -106,8 +106,8 @@
2067 // make sure that the signal will be fired after the item collapse
2068 onHeightChanged: {
2069 if (!expanded && (selectedDetail !== -1) && (height === collapsedHeight)) {
2070- fieldSelected(root.nameFromEnum(selectedDetail), root.qmlTypeFromEnum(selectedDetail))
2071- selectedDetail = -1
2072+ root.fieldSelected(root.nameFromEnum(root.selectedDetail), root.qmlTypeFromEnum(root.selectedDetail))
2073+ root.selectedDetail = -1
2074 }
2075 }
2076
2077
2078=== modified file 'src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailAvatarEditor.qml'
2079--- src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailAvatarEditor.qml 2015-11-24 12:18:17 +0000
2080+++ src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailAvatarEditor.qml 2016-01-07 13:19:12 +0000
2081@@ -144,7 +144,6 @@
2082 }
2083
2084 Component.onDestruction: {
2085- console.debug("Delete temporary avatar image:" + root.temporaryAvatar)
2086 Contacts.removeFile("file:///" + root.temporaryAvatar)
2087 root.temporaryAvatar = ""
2088 }
2089
2090=== modified file 'src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailGroupWithTypeEditor.qml'
2091--- src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailGroupWithTypeEditor.qml 2015-11-19 16:38:36 +0000
2092+++ src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailGroupWithTypeEditor.qml 2016-01-07 13:19:12 +0000
2093@@ -98,7 +98,6 @@
2094 return changed
2095 }
2096
2097- forceFocusOnFieldCreation: true
2098 headerDelegate: Label {
2099 id: header
2100
2101
2102=== modified file 'src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailWithTypeEditor.qml'
2103--- src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailWithTypeEditor.qml 2015-11-10 19:03:36 +0000
2104+++ src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailWithTypeEditor.qml 2016-01-07 13:19:12 +0000
2105@@ -33,6 +33,7 @@
2106 property variant placeholderTexts: []
2107 property int inputMethodHints: Qt.ImhNone
2108 property bool usePhoneFormat: false
2109+ readonly property alias repeater: fieldRepeater
2110
2111 function selectType(type) {
2112 detailTypeSelector.selectItem(type)
2113@@ -96,7 +97,6 @@
2114
2115 detail: root.detail
2116 field: modelData
2117- focus: true
2118 placeholderText: root.placeholderTexts[index]
2119 inputMethodHints: root.inputMethodHints
2120 autoFormat: root.usePhoneFormat
2121
2122=== modified file 'src/imports/Ubuntu/AddressBook/ContactEditor/ContactEditorPage.qml'
2123--- src/imports/Ubuntu/AddressBook/ContactEditor/ContactEditorPage.qml 2015-12-18 13:16:55 +0000
2124+++ src/imports/Ubuntu/AddressBook/ContactEditor/ContactEditorPage.qml 2016-01-07 13:19:12 +0000
2125@@ -33,11 +33,15 @@
2126
2127 property string initialFocusSection: ""
2128 property var newDetails: []
2129+ property list<QtObject> leadingActions
2130+ property alias headerActions: trailingBar.actions
2131
2132 readonly property bool isNewContact: contact && (contact.contactId === "qtcontacts:::")
2133 readonly property bool isContactValid: !avatarEditor.busy && (!nameEditor.isEmpty() || !phonesEditor.isEmpty())
2134+ readonly property alias editorFlickable: scrollArea
2135
2136 signal contactSaved(var contact);
2137+ signal canceled()
2138
2139 function cancel() {
2140 for (var i = 0; i < contactEditor.newDetails.length; ++i) {
2141@@ -51,11 +55,12 @@
2142 field.cancel()
2143 }
2144 }
2145- if (pageStack.removePages) {
2146+ if (pageStack && pageStack.removePages) {
2147 pageStack.removePages(contactEditor)
2148- } else {
2149+ } else if (pageStack) {
2150 pageStack.pop()
2151 }
2152+ contactEditor.canceled()
2153 }
2154
2155 function save() {
2156@@ -100,18 +105,25 @@
2157 }
2158 }
2159
2160- function makeMeVisible(item) {
2161+ function idleMakeMeVisible(item) {
2162 if (!enabled || !item) {
2163 return
2164 }
2165
2166 activeItem = item
2167- var position = scrollArea.contentItem.mapFromItem(item, 0, activeItem.y);
2168-
2169+ timerMakemakeMeVisible.restart()
2170+ }
2171+
2172+ function makeMeVisible(item) {
2173+ if (!item)
2174+ return
2175+
2176+ var position = activeItem.mapToItem(editEditor, item.x, item.y);
2177 // check if the item is already visible
2178 var bottomY = scrollArea.contentY + scrollArea.height
2179 var itemBottom = position.y + (item.height * 3) // extra margin
2180 if (position.y >= scrollArea.contentY && itemBottom <= bottomY) {
2181+ Qt.inputMethod.show()
2182 return;
2183 }
2184
2185@@ -123,7 +135,9 @@
2186 // if it is hidden at the top, also show it
2187 scrollArea.contentY = position.y;
2188 }
2189+
2190 scrollArea.returnToBounds()
2191+ Qt.inputMethod.show()
2192 }
2193
2194 function ready()
2195@@ -135,6 +149,7 @@
2196 contactEditor.focusToLastPhoneField()
2197 break;
2198 case "name":
2199+ default:
2200 nameEditor.fieldDelegates[0].forceActiveFocus()
2201 break;
2202 }
2203@@ -146,9 +161,42 @@
2204 lastPhoneField.forceActiveFocus()
2205 }
2206
2207- title: isNewContact ? i18n.dtr("address-book-app", "New contact") : i18n.dtr("address-book-app", "Edit")
2208+ function focusToFirstEntry(field)
2209+ {
2210+ var itemToFocus = field
2211+ if (field.repeater)
2212+ itemToFocus = field.repeater.itemAt(0)
2213+
2214+ if (itemToFocus) {
2215+ root.idleMakeMeVisible(itemToFocus)
2216+ itemToFocus.forceActiveFocus()
2217+ }
2218+ }
2219+
2220+ Timer {
2221+ id: timerMakemakeMeVisible
2222+
2223+ interval: 100
2224+ repeat: false
2225+ running: false
2226+ onTriggered: root.makeMeVisible(root.activeItem)
2227+ }
2228+
2229+ header: PageHeader {
2230+ id: pageHeader
2231+
2232+ title: isNewContact ? i18n.dtr("address-book-app", "New contact") : i18n.dtr("address-book-app", "Edit")
2233+ trailingActionBar {
2234+ id: trailingBar
2235+ }
2236+ leadingActionBar {
2237+ id: leadingBar
2238+ actions: contactEditor.leadingActions
2239+ }
2240+ }
2241+
2242 enabled: false
2243-
2244+ flickable: null
2245 Timer {
2246 id: focusTimer
2247
2248@@ -158,7 +206,6 @@
2249 onTriggered: contactEditor.ready()
2250 }
2251
2252- flickable: null
2253 Flickable {
2254 id: scrollArea
2255 objectName: "scrollArea"
2256@@ -169,17 +216,13 @@
2257 anchors{
2258 left: parent.left
2259 top: parent.top
2260+ topMargin: pageHeader.height
2261 right: parent.right
2262 bottom: keyboardRectangle.top
2263 }
2264 contentHeight: contents.height + units.gu(2)
2265 contentWidth: parent.width
2266
2267- //after add a new field we need to wait for the contentHeight to change to scroll to the correct position
2268- onContentHeightChanged: {
2269- contactEditor.makeMeVisible(contactEditor.activeItem)
2270- }
2271-
2272 Column {
2273 id: contents
2274
2275@@ -242,6 +285,7 @@
2276 right: parent.right
2277 }
2278 height: implicitHeight
2279+ onNewFieldAdded: root.focusToFirstEntry(field)
2280 }
2281
2282 ContactDetailEmailsEditor {
2283@@ -254,6 +298,7 @@
2284 right: parent.right
2285 }
2286 height: implicitHeight
2287+ onNewFieldAdded: root.focusToFirstEntry(field)
2288 }
2289
2290 ContactDetailOnlineAccountsEditor {
2291@@ -266,6 +311,7 @@
2292 right: parent.right
2293 }
2294 height: implicitHeight
2295+ onNewFieldAdded: root.focusToFirstEntry(field)
2296 }
2297
2298 ContactDetailAddressesEditor {
2299@@ -278,6 +324,7 @@
2300 right: parent.right
2301 }
2302 height: implicitHeight
2303+ onNewFieldAdded: root.focusToFirstEntry(field)
2304 }
2305
2306 ContactDetailOrganizationsEditor {
2307@@ -290,6 +337,7 @@
2308 right: parent.right
2309 }
2310 height: implicitHeight
2311+ onNewFieldAdded: root.focusToFirstEntry(field)
2312 }
2313
2314 ContactDetailSyncTargetEditor {
2315@@ -332,6 +380,7 @@
2316 margins: units.gu(2)
2317 }
2318 height: implicitHeight
2319+ activeFocusOnPress: false
2320 onHeightChanged: {
2321 if (expanded && (height === expandedHeight) && !scrollArea.atYEnd) {
2322 moveToBottom.start()
2323@@ -343,9 +392,11 @@
2324
2325 target: scrollArea
2326 property: "contentY"
2327- from: scrollArea.contentY
2328- to: Math.min(scrollArea.contentHeight - scrollArea.height,
2329- scrollArea.contentY + (addNewFieldButton.height - addNewFieldButton.collapsedHeight - units.gu(3)))
2330+ to: scrollArea.contentHeight - scrollArea.height
2331+ onStopped: {
2332+ scrollArea.returnToBounds()
2333+ addNewFieldButton.forceActiveFocus()
2334+ }
2335 }
2336
2337 onFieldSelected: {
2338@@ -406,8 +457,11 @@
2339 id: keyboardRectangle
2340
2341 onHeightChanged: {
2342- if (activeItem) {
2343- makeMeVisible(activeItem)
2344+ if (addNewFieldButton.expanded) {
2345+ scrollArea.contentY = scrollArea.contentHeight - scrollArea.height
2346+ scrollArea.returnToBounds()
2347+ } else if (activeItem) {
2348+ idleMakeMeVisible(activeItem)
2349 }
2350 }
2351 }
2352@@ -493,6 +547,8 @@
2353 contactEditor.pageStack.pop() // view page
2354 }
2355 }
2356+ if (contactEditor.pageStack.primaryPage)
2357+ contactEditor.pageStack.primaryPage.forceActiveFocus()
2358 }
2359 }
2360 }
2361
2362=== modified file 'src/imports/Ubuntu/AddressBook/ContactEditor/TextInputDetail.qml'
2363--- src/imports/Ubuntu/AddressBook/ContactEditor/TextInputDetail.qml 2015-11-25 17:31:33 +0000
2364+++ src/imports/Ubuntu/AddressBook/ContactEditor/TextInputDetail.qml 2016-01-07 13:19:12 +0000
2365@@ -47,27 +47,18 @@
2366
2367 //FIXME: Move this property to TextField as soon as the SDK get ported to QtQuick 2.2
2368 activeFocusOnTab: true
2369-
2370- // WORKAROUND: For some reason TextField.focus property get reset to false
2371- // we need do a deep investigation on that
2372- Binding {
2373- target: field
2374- property: "focus"
2375- value: visible
2376- }
2377-
2378- onActiveFocusChanged: {
2379- if (activeFocus && field.visible) {
2380- field.forceActiveFocus()
2381- }
2382- }
2383-
2384 onOriginalValueChanged: {
2385 if (originalValue && (originalValue !== "")) {
2386 field.text = originalValue
2387 }
2388 }
2389
2390+ // propage focus to text field
2391+ onActiveFocusChanged: {
2392+ if (activeFocus)
2393+ field.forceActiveFocus()
2394+ }
2395+
2396 PhoneNumberField {
2397 id: field
2398
2399
2400=== modified file 'src/imports/Ubuntu/AddressBook/ContactShare/ContactSharePage.qml'
2401--- src/imports/Ubuntu/AddressBook/ContactShare/ContactSharePage.qml 2015-10-26 13:18:11 +0000
2402+++ src/imports/Ubuntu/AddressBook/ContactShare/ContactSharePage.qml 2016-01-07 13:19:12 +0000
2403@@ -50,13 +50,21 @@
2404 if (exporter.activeTransfer) {
2405 exporter.activeTransfer.state = ContentHub.ContentTransfer.Aborted
2406 }
2407- pageStack.removePages(root)
2408+ if (root.pageStack.removePages)
2409+ root.pageStack.removePages(root)
2410+ else
2411+ root.pageStack.pop()
2412 }
2413 }
2414
2415 ContactExporter {
2416 id: exporter
2417
2418- onDone: pageStack.removePages(root)
2419+ onDone: {
2420+ if (root.pageStack.removePages)
2421+ root.pageStack.removePages(root)
2422+ else
2423+ root.pageStack.pop()
2424+ }
2425 }
2426 }
2427
2428=== modified file 'src/imports/Ubuntu/AddressBook/ContactView/ContactViewPage.qml'
2429--- src/imports/Ubuntu/AddressBook/ContactView/ContactViewPage.qml 2015-12-10 19:13:20 +0000
2430+++ src/imports/Ubuntu/AddressBook/ContactView/ContactViewPage.qml 2016-01-07 13:19:12 +0000
2431@@ -29,6 +29,7 @@
2432 property alias extensions: extensionsContents.children
2433 property alias model: contactFetch.model
2434 property alias editable: contactDetailAvatar.editable
2435+ property alias headerActions: trailingBar.actions
2436
2437 signal contactFetched(QtObject contact)
2438 signal contactRemoved()
2439@@ -41,12 +42,20 @@
2440 }
2441 }
2442
2443- title: contact ? ContactsJS.formatToDisplay(contact, i18n.dtr("address-book-app", "No name")) : ""
2444+ header: PageHeader {
2445+ id: pageHeader
2446+
2447+ flickable: flickable
2448+ title: contact ? ContactsJS.formatToDisplay(contact, i18n.dtr("address-book-app", "No name")) : ""
2449+ trailingActionBar {
2450+ id: trailingBar
2451+ }
2452+ }
2453
2454 Connections {
2455 target: contact
2456 onContactChanged: {
2457- root.title = ContactsJS.formatToDisplay(contact, i18n.dtr("address-book-app", "No name"))
2458+ pageHeader.title = ContactsJS.formatToDisplay(contact, i18n.dtr("address-book-app", "No name"))
2459 }
2460 }
2461
2462@@ -88,11 +97,13 @@
2463 }
2464 }
2465
2466+
2467 Flickable {
2468 id: flickable
2469
2470 flickableDirection: Flickable.VerticalFlick
2471 anchors.fill: parent
2472+ clip: true
2473 //WORKAROUND: There is a bug on SDK page that causes the page to appear flicked with small contents
2474 // see bug #1223050
2475 contentHeight: Math.max(contents.height, parent.height) + units.gu(2)
2476
2477=== modified file 'src/imports/Ubuntu/Contacts/ContactListView.qml'
2478--- src/imports/Ubuntu/Contacts/ContactListView.qml 2015-12-02 17:10:53 +0000
2479+++ src/imports/Ubuntu/Contacts/ContactListView.qml 2016-01-07 13:19:12 +0000
2480@@ -202,12 +202,6 @@
2481 */
2482 property bool showAddNewButton: false
2483 /*!
2484- \qmlproperty bool prepareNewContact
2485-
2486- This property holds if space for a draft new contact should be made available or not
2487- */
2488- property bool prepareNewContact: false
2489- /*!
2490 \qmlproperty bool showNewContact
2491
2492 This property holds if a draft new contact should be visible or not
2493@@ -407,6 +401,7 @@
2494 }
2495 anchors.fill: parent
2496
2497+
2498 // WORKAROUND: The SDK header causes the contactY to move to a wrong postion
2499 // calling the positionViewAtBeginning after the list created fix that
2500 Timer {
2501@@ -424,15 +419,18 @@
2502 right: parent.right
2503 }
2504
2505- Connections {
2506- target: root
2507- onPrepareNewContactChanged: {
2508- if (root.prepareNewContact) {
2509- view.contentY = Qt.binding(function() {return -view.headerItem.height});
2510- } else {
2511- view.contentY = view.contentY;
2512- }
2513- }
2514+ Binding {
2515+ target: view
2516+ property: 'contentY'
2517+ value: -view.headerItem.height
2518+ when: root.showNewContact
2519+ }
2520+
2521+ Binding {
2522+ target: view
2523+ property: 'currentIndex'
2524+ value: -1
2525+ when: root.showNewContact
2526 }
2527
2528 // AddNewButton
2529@@ -461,10 +459,9 @@
2530 }
2531 }
2532 selected: true
2533- visible: root.prepareNewContact
2534- height: root.prepareNewContact ? defaultHeight : 0
2535+ visible: root.showNewContact
2536+ height: root.showNewContact ? defaultHeight : 0
2537 Behavior on height {UbuntuNumberAnimation {}}
2538- opacity: root.showNewContact ? 1.0 : 0.0
2539 }
2540
2541 Column {
2542
2543=== modified file 'src/imports/Ubuntu/Contacts/ContactSimpleListView.qml'
2544--- src/imports/Ubuntu/Contacts/ContactSimpleListView.qml 2015-12-10 19:13:29 +0000
2545+++ src/imports/Ubuntu/Contacts/ContactSimpleListView.qml 2016-01-07 13:19:12 +0000
2546@@ -270,9 +270,9 @@
2547 flicking: contactListView.flicking
2548 width: parent.width
2549 selected: (contactListView.multiSelectionEnabled && contactListView.isSelected(contactDelegate))
2550- || (contactListView.highlightSelected && (contactListView.currentIndex == index))
2551- selectedColor: contactListView.parent.activeFocus && !contactListView.isInSelectionMode ? UbuntuColors.orange :
2552- Theme.palette.selected.background
2553+ || (!contactListView.isInSelectionMode && contactListView.highlightSelected && (contactListView.currentIndex == index))
2554+ selectedColor: contactListView.parent.activeFocus && !contactListView.isInSelectionMode && contactListView.highlightSelected ?
2555+ UbuntuColors.orange : Theme.palette.selected.background
2556 selectionMode: contactListView.isInSelectionMode
2557 defaultAvatarUrl: contactListView.defaultAvatarImageUrl
2558 isCurrentItem: ListView.isCurrentItem
2559
2560=== modified file 'src/imports/Ubuntu/Contacts/ListItemWithActions.qml'
2561--- src/imports/Ubuntu/Contacts/ListItemWithActions.qml 2015-11-10 19:51:19 +0000
2562+++ src/imports/Ubuntu/Contacts/ListItemWithActions.qml 2016-01-07 13:19:12 +0000
2563@@ -314,6 +314,23 @@
2564 }
2565
2566 SequentialAnimation {
2567+ id: clickAnimation
2568+
2569+ running: false
2570+ alwaysRunToEnd: true
2571+ PropertyAnimation {
2572+ target: main
2573+ property: "color"
2574+ to: root.selectedColor
2575+ }
2576+ PropertyAction {
2577+ target: main
2578+ property: "color"
2579+ value: root.selected ? root.selectedColor : root.color
2580+ }
2581+ }
2582+
2583+ SequentialAnimation {
2584 id: triggerAction
2585
2586 property var currentItem: root.activeItem ? root.activeItem.image : null
2587@@ -424,6 +441,8 @@
2588 onClicked: {
2589 if (main.x === 0) {
2590 root.itemClicked(mouse)
2591+ if (!root.selected)
2592+ clickAnimation.start()
2593 } else if (main.x > 0) {
2594 var action = getActionAt(Qt.point(mouse.x, mouse.y))
2595 if (action && action !== -1) {
2596
2597=== modified file 'tests/autopilot/address_book_app/__init__.py'
2598--- tests/autopilot/address_book_app/__init__.py 2015-10-28 13:27:33 +0000
2599+++ tests/autopilot/address_book_app/__init__.py 2016-01-07 13:19:12 +0000
2600@@ -125,14 +125,13 @@
2601 return None
2602
2603 def click_action_button(self, action_name):
2604- actionbars = self.select_many('ActionBar', objectName='headerActionBar')
2605- for actionbar in actionbars:
2606- try:
2607- actionbar.click_action_button(action_name)
2608- return
2609- except ubuntuuitoolkit.ToolkitException:
2610- continue
2611- raise exceptions.StateNotFoundError('Action %s not found.' % action_name)
2612+ actions = self.select_many(objectName='%s_button'%action_name)
2613+ for action in actions:
2614+ if action.enabled:
2615+ self.pointing_device.click_object(action)
2616+ return
2617+
2618+ raise exceptions.StateNotFoundError(action_name)
2619
2620 def open_header(self):
2621 header = self.get_header()
2622@@ -155,6 +154,25 @@
2623
2624 return header
2625
2626+ def wait_bottom_edge(self, opened):
2627+ # wait bottom edge to fully appear
2628+ mainStack = self.wait_select_single(objectName='mainStack')
2629+ mainStack.bottomEdgeOpened.wait_for(opened)
2630+
2631+ def reveal_bottom_edge_page(self):
2632+ flickable = self.get_contact_list_view()
2633+
2634+ globalRect = flickable.globalRect
2635+ start_x = globalRect.x + (globalRect.width * 0.5)
2636+ start_y = globalRect.y + (flickable.height * 0.98)
2637+ stop_y = globalRect.y + (flickable.height * 0.4)
2638+
2639+ self.pointing_device.drag(
2640+ start_x, start_y, start_x, stop_y, rate=5)
2641+
2642+ # wait bottom edge to fully appear
2643+ self.wait_bottom_edge(True)
2644+
2645 def cancel(self):
2646 """
2647 Press the 'Cancel' button
2648@@ -165,6 +183,7 @@
2649 self.pointing_device.click_object(button)
2650 return
2651
2652+ self.click_action_button('cancel')
2653 #self.click_action_button("customBackButton")
2654
2655 def save(self):
2656@@ -200,7 +219,6 @@
2657 """
2658 Press the 'Add' button and return the contact editor page
2659 """
2660- bottom_swipe_page = self.get_contact_list_page()
2661- bottom_swipe_page.reveal_bottom_edge_page()
2662+ self.reveal_bottom_edge_page()
2663
2664 return self.get_contact_edit_page()
2665
2666=== modified file 'tests/autopilot/address_book_app/pages/_ab_contact_list_page.py'
2667--- tests/autopilot/address_book_app/pages/_ab_contact_list_page.py 2015-10-20 03:38:07 +0000
2668+++ tests/autopilot/address_book_app/pages/_ab_contact_list_page.py 2016-01-07 13:19:12 +0000
2669@@ -112,7 +112,7 @@
2670 @log_action_info
2671 def delete_selected_contacts(self, main_window):
2672 main_window.delete()
2673- self.bottomEdgePageOpened.wait_for(False)
2674+ main_window.wait_bottom_edge(False)
2675 dialog = self.get_root_instance().wait_select_single(
2676 address_book.RemoveContactsDialog, objectName='removeContactsDialog')
2677 dialog.confirm_removal()
2678@@ -137,21 +137,3 @@
2679 'ContactListButtonDelegate',
2680 objectName='contactListView.importFromSimCardButton')
2681 return import_from_sim_button.visible
2682-
2683- def reveal_bottom_edge_page(self):
2684- """Bring the bottom edge page to the screen"""
2685- self.bottomEdgePageOpened.wait_for(False)
2686- try:
2687- action_item = self.wait_select_single(objectName='bottomEdgeDragArea')
2688- action_item.enabled.wait_for(True)
2689- start_x = (action_item.globalRect.x +
2690- (action_item.globalRect.width * 0.5))
2691- start_y = action_item.globalRect.y + (action_item.height * 0.2)
2692- stop_y = start_y - (self.height * 0.7)
2693- self.pointing_device.drag(
2694- start_x, start_y, start_x, stop_y, rate=2)
2695- #self pointer became invalid at this point
2696- #self.bottomEdgePageOpened.wait_for(True)
2697- except dbus.StateNotFoundError:
2698- logger.error('ButtomEdge element not found.')
2699- raise
2700
2701=== modified file 'tests/autopilot/address_book_app/tests/test_add_contact.py'
2702--- tests/autopilot/address_book_app/tests/test_add_contact.py 2015-10-20 03:38:07 +0000
2703+++ tests/autopilot/address_book_app/tests/test_add_contact.py 2016-01-07 13:19:12 +0000
2704@@ -36,8 +36,6 @@
2705 contact_editor = self.app.main_window.go_to_add_contact()
2706
2707 # Check if the contact list disapear and contact editor appears
2708- #FIXME: list_page became an invalid pointer after push a new page
2709- #self.assertThat(list_page.bottomEdgePageOpened, Eventually(Equals(True)))
2710 self.assertThat(contact_editor.visible, Eventually(Equals(True)))
2711 self.assertThat(contact_editor.active, Eventually(Equals(True)))
2712
2713@@ -46,7 +44,7 @@
2714
2715 # Check if the contact list is visible again
2716 self.assertThat(list_page.visible, Eventually(Equals(True)))
2717- self.assertThat(list_page.bottomEdgePageOpened, Eventually(Equals(False)))
2718+ self.app.main_window.wait_bottom_edge(False)
2719
2720 # Check if the contact list still empty
2721 list_view = self.app.main_window.get_contact_list_view()
2722
2723=== modified file 'tests/autopilot/address_book_app/tests/test_create_new_from_uri.py'
2724--- tests/autopilot/address_book_app/tests/test_create_new_from_uri.py 2015-10-20 03:38:07 +0000
2725+++ tests/autopilot/address_book_app/tests/test_create_new_from_uri.py 2016-01-07 13:19:12 +0000
2726@@ -23,8 +23,6 @@
2727
2728 def test_save_new_contact(self):
2729 list_page = self.app.main_window.get_contact_list_page()
2730- #FIXME: contacts list object became invalid after push a new page
2731- #list_page.bottomEdgePageOpened.wait_for(True)
2732
2733 edit_page = self.app.main_window.get_contact_edit_page()
2734 self.assertThat(edit_page.visible, Eventually(Equals(True)))
2735
2736=== modified file 'tests/autopilot/address_book_app/tests/test_edit_contact.py'
2737--- tests/autopilot/address_book_app/tests/test_edit_contact.py 2015-10-20 03:38:07 +0000
2738+++ tests/autopilot/address_book_app/tests/test_edit_contact.py 2016-01-07 13:19:12 +0000
2739@@ -167,16 +167,14 @@
2740 self.clear_text_on_field(last_name_field)
2741
2742 # check if is possible to save a contact without name
2743- self.app.main_window.save()
2744- accept_button = self.app.main_window.get_action("save")
2745- self.assertThat(accept_button.enabled, Eventually(Equals(False)))
2746+ self.assertThat(edit_page.saveActionEnabled, Eventually(Equals(False)))
2747
2748 # Cancel edit
2749 self.app.main_window.cancel()
2750
2751 # Check if the names still there
2752 view_page = self.app.main_window.get_contact_view_page()
2753- self.assertThat(view_page.title, Eventually(Equals("Fulano de Tal")))
2754+ self.assertThat(view_page.headerTitle, Eventually(Equals("Fulano de Tal")))
2755
2756 def test_im_type(self):
2757 contact_editor = self.app.main_window.go_to_add_contact()
2758
2759=== modified file 'tests/autopilot/address_book_app/tests/test_import_vcard.py'
2760--- tests/autopilot/address_book_app/tests/test_import_vcard.py 2015-11-24 18:18:31 +0000
2761+++ tests/autopilot/address_book_app/tests/test_import_vcard.py 2016-01-07 13:19:12 +0000
2762@@ -30,7 +30,7 @@
2763 # check if app enter on import state
2764 list_page = self.app.main_window.get_contact_list_page()
2765 self.assertThat(list_page.state, Eventually(Equals('vcardImported')))
2766- self.assertThat(list_page.title, Eventually(Equals('Imported contacts')))
2767+ self.assertThat(list_page.headerTitle, Eventually(Equals('Imported contacts')))
2768 self.assertThat(len(list_page.get_contacts()), Equals(3))
2769
2770 #leave import state and show full contact list
2771
2772=== modified file 'tests/autopilot/address_book_app/tests/test_single_pick_mode.py'
2773--- tests/autopilot/address_book_app/tests/test_single_pick_mode.py 2015-04-17 15:22:08 +0000
2774+++ tests/autopilot/address_book_app/tests/test_single_pick_mode.py 2016-01-07 13:19:12 +0000
2775@@ -32,6 +32,7 @@
2776 if (contact.visible):
2777 item = contact.select_single("QQuickRectangle",
2778 objectName="mainItem")
2779+ self.assertThat(contact.selected, Eventually(Equals(False)))
2780 self.assertThat(item.color, Eventually(Equals(contact.color)))
2781 selected_items.append(item)
2782 item_to_contacts[item] = contact
2783
2784=== modified file 'tests/qml/tst_ContactPreviewPage.qml'
2785--- tests/qml/tst_ContactPreviewPage.qml 2015-11-16 15:44:43 +0000
2786+++ tests/qml/tst_ContactPreviewPage.qml 2016-01-07 13:19:12 +0000
2787@@ -104,13 +104,13 @@
2788 function test_preview_contact_with_name()
2789 {
2790 contactPreviewPage.contact = createContactWithName()
2791- tryCompare(contactPreviewPage, "title", "Fulano")
2792+ tryCompare(contactPreviewPage.header, "title", "Fulano")
2793 }
2794
2795 function test_preview_contact_with_name_and_avatar()
2796 {
2797 contactPreviewPage.contact = createContactWithNameAndAvatar()
2798- tryCompare(contactPreviewPage, "title", "Fulano")
2799+ tryCompare(contactPreviewPage.header, "title", "Fulano")
2800 var avatarField = findChild(root, "contactAvatarDetail")
2801 tryCompare(avatarField, "avatarUrl", "image://theme/address-book-app")
2802 }
2803@@ -120,7 +120,7 @@
2804 compare(vcardParser.contacts.length, 1)
2805 var contact = vcardParser.contacts[0]
2806 contactPreviewPage.contact = contact
2807- tryCompare(contactPreviewPage, "title", "Forrest Gump")
2808+ tryCompare(contactPreviewPage.header, "title", "Forrest Gump")
2809 // PhoneNumbers
2810 // TEL;TYPE=WORK,VOICE:(111) 555-12121
2811 // TEL;TYPE=HOME,VOICE:(404) 555-1212
2812@@ -263,8 +263,7 @@
2813 compare(vcardParser.contacts.length, 1)
2814 var contact = vcardParser.contacts[0]
2815 contactPreviewPage.contact = contact
2816- tryCompare(contactPreviewPage, "title", "Forrest Gump")
2817-
2818+ tryCompare(contactPreviewPage.header, "title", "Forrest Gump")
2819
2820 // page is enabled by default
2821 var avatar = findChild(contactPreviewPage, "avatar")

Subscribers

People subscribed via source and target branches