Merge lp:~renatofilho/address-book-app/keyboard-navigation into lp:address-book-app

Proposed by Renato Araujo Oliveira Filho
Status: Merged
Approved by: Bill Filler
Approved revision: 550
Merged at revision: 522
Proposed branch: lp:~renatofilho/address-book-app/keyboard-navigation
Merge into: lp:address-book-app
Diff against target: 1460 lines (+464/-156)
28 files modified
src/app/addressbookapp.cpp (+17/-1)
src/app/addressbookapp.h (+7/-0)
src/imports/ABContactEditorPage.qml (+7/-5)
src/imports/ABContactListPage.qml (+156/-32)
src/imports/ABContactViewPage.qml (+12/-0)
src/imports/BottomEdge.qml (+18/-1)
src/imports/MainWindow.qml (+27/-1)
src/imports/Settings/SettingsPage.qml (+59/-3)
src/imports/Ubuntu/AddressBook/Base/ContactDetailBase.qml (+22/-8)
src/imports/Ubuntu/AddressBook/Base/ContactDetailGroupBase.qml (+2/-1)
src/imports/Ubuntu/AddressBook/Base/RemoveContactsDialog.qml (+10/-2)
src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailAvatarEditor.qml (+1/-0)
src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailGroupWithTypeEditor.qml (+12/-23)
src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailNameEditor.qml (+1/-0)
src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailSyncTargetEditor.qml (+7/-2)
src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailWithTypeEditor.qml (+0/-1)
src/imports/Ubuntu/AddressBook/ContactEditor/ContactEditorPage.qml (+23/-17)
src/imports/Ubuntu/AddressBook/ContactEditor/TextInputDetail.qml (+24/-10)
src/imports/Ubuntu/AddressBook/ContactView/ActionButton.qml (+1/-0)
src/imports/Ubuntu/AddressBook/ContactView/BasicFieldView.qml (+1/-0)
src/imports/Ubuntu/AddressBook/ContactView/ContactDetailAvatarView.qml (+1/-0)
src/imports/Ubuntu/AddressBook/ContactView/ContactDetailGroupWithTypeView.qml (+13/-21)
src/imports/Ubuntu/AddressBook/ContactView/ContactDetailNameView.qml (+1/-1)
src/imports/Ubuntu/AddressBook/ContactView/ContactDetailSyncTargetView.qml (+2/-1)
src/imports/Ubuntu/AddressBook/ContactView/ContactDetailWithTypeView.qml (+3/-0)
src/imports/Ubuntu/Contacts/ContactListView.qml (+26/-1)
src/imports/Ubuntu/Contacts/ContactSimpleListView.qml (+7/-13)
src/imports/Ubuntu/Contacts/FastScroll.qml (+4/-12)
To merge this branch: bzr merge lp:~renatofilho/address-book-app/keyboard-navigation
Reviewer Review Type Date Requested Status
Florian Boucault (community) Approve
PS Jenkins bot continuous-integration Needs Fixing
Bill Filler (community) Needs Fixing
Review via email: mp+277176@code.launchpad.net

Commit message

Implement keyboard navigation for contact list.
Fix 'tab' navigation for contact editor page.
Added shortcuts for the page actions.

Description of the change

Available shortcuts:

General
=======
Esc = Back (Page stack)

Contact List
============
Ctrl + F = Search
Ctrl + N = Bottom edge (create new)

Contact View
============
Ctrl + E = Edit

Contact Editor
==============
Ctrl + S = Save
Ctrl + Delete = Delete contact

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

Fixed focus on test fields.

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

Fixed visual for field header.

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

Merged: ~renatofilho/address-book-app/fix-1511477

509. By Renato Araujo Oliveira Filho

Make ActionButton icons 'orange' when it has focus.
Fixed some focus misbehavior.

Revision history for this message
Bill Filler (bfiller) wrote :

Some problems:

1) Ctrl F to search, then type your search string, press enter, down arrow should navigate to first item in results list and select it

2) When at top of the contact list and press up arrow focus should go to the section selector
a) right/left arrow in section selector should navigate between sections
b) down arrow when in section selector should navigate back to list

3) After saving a new contact, the new contact should be selected in the list and scrolled into view, but this is not working and first item in list is selected instead.

4) When you create a new contact and cancel (via esc), we should either select what was previously selected or the first item, but something should be selected. Currently nothing selected and right panel is blank which is not desirable.

5) When you enter edit mode, focus needs to be placed in the first field

6) In edit mode, Up/Down arrow keys should navigate the fields on the edit screen same as tabs do

7) In edit mode, left arrow key should bring focus back to contact list, if in contact list right arrow should navigate to first field in edit mode

8) Seeing of lots of these errors in log: file:///usr/share/address-book-app/imports/ABContactEditorPage.qml:45:9: QML Action: Invalid shortcut:

8)

review: Needs Fixing
Revision history for this message
Bill Filler (bfiller) wrote :

When press Settings button, focus should move to first item in settings list and up/down arrow should navigate, right arrow/enter should expand the item

review: Needs Fixing
Revision history for this message
Bill Filler (bfiller) wrote :

In edit mode, tab key and arrow keys should navigate to the Add Field button and Delete buttons, currently these are skipped in the navigation.

review: Needs Fixing
Revision history for this message
Bill Filler (bfiller) wrote :

In edit mode after making changes and saving the contact, the next contact in the list is selected instead of keeping the selection on the contact you just edited.

review: Needs Fixing
Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

> Some problems:
>
> 1) Ctrl F to search, then type your search string, press enter, down arrow
> should navigate to first item in results list and select it
I do not like to use arrows to navigate btw objects, the arrows is alreary consumed by the text input to navigate internally (up/down when multilines).
Could we use "Tab" here?

>
> 2) When at top of the contact list and press up arrow focus should go to the
> section selector
> a) right/left arrow in section selector should navigate between sections
> b) down arrow when in section selector should navigate back to list

This need to be implemented by SDK. We can implement WORKAROUNDS here to support that but I would like to avoid it if possible.

The correct here is set focus to the page section and it handle the arrows or tab.

>
> 3) After saving a new contact, the new contact should be selected in the list
> and scrolled into view, but this is not working and first item in list is
> selected instead.
>
> 4) When you create a new contact and cancel (via esc), we should either select
> what was previously selected or the first item, but something should be
> selected. Currently nothing selected and right panel is blank which is not
> desirable.
I will implement that.

>
> 5) When you enter edit mode, focus needs to be placed in the first field
As discussed on IRC, this is not possible at the moment since we can not detect if there is a keyboard connect to device or not.
Implementing it ad default behavior you bring regressions on the phone use case.
check: https://bugs.launchpad.net/ubuntu/+source/address-book-app/+bug/1268042

>
> 6) In edit mode, Up/Down arrow keys should navigate the fields on the edit
> screen same as tabs do
Are you sure about that? This is not how desktop apps work. Most of the apps uses Tab to go foward and "shift+tab" to go backward on items in the screen.

>
> 7) In edit mode, left arrow key should bring focus back to contact list, if in
> contact list right arrow should navigate to first field in edit mode

Left and Right arrow is already used by the fields to navigate internally on the text. The correct will be use "tab".

But again we will need some big WORKAROUNDS here, since the item that receives focus on the page will be the header and it does not handle focus very well at the moment.

>
> 8) Seeing of lots of these errors in log: file:///usr/share/address-book-
> app/imports/ABContactEditorPage.qml:45:9: QML Action: Invalid shortcut:
This is because a WORKAROUND that I am using to avoid bug #1514856.

Right now all shortcuts are enable even if the action is not active/visible, because of that I am using this WORKAROUND:

shortcut: root.active ? "Ctrl+s": ""

this warning message appear when setting the shortcut to "" (empty).

>
> 8)

Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

> In edit mode, tab key and arrow keys should navigate to the Add Field button
> and Delete buttons, currently these are skipped in the navigation.

The current SDK does not have focus visual feedback for buttons, allowing it to receive focus can cause confusion since the user will never know which button or item has focus or not.

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: Approve (continuous-integration)
510. By Renato Araujo Oliveira Filho

Use tab to navigate to contact list from the search field.

511. By Renato Araujo Oliveira Filho

Move backwards on fields navigation when pressing 'Shif+Tab'

512. By Renato Araujo Oliveira Filho

Avoid load contact view page twice for the same contact.

513. By Renato Araujo Oliveira Filho

Make sure the contact list currentIndex does not change after editing a contact.

514. By Renato Araujo Oliveira Filho

Make sure that the app alwasy start with a selected contact when running on multi column.

Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

> Some problems:
>
> 1) Ctrl F to search, then type your search string, press enter, down arrow
> should navigate to first item in results list and select it
Fixed on rev. 510

> 3) After saving a new contact, the new contact should be selected in the list
> and scrolled into view, but this is not working and first item in list is
> selected instead.
Fixed on rev. 513

>
> 4) When you create a new contact and cancel (via esc), we should either select
> what was previously selected or the first item, but something should be
> selected. Currently nothing selected and right panel is blank which is not
> desirable.
Fixed on rev. 513

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

Move focus to contact list when pressing 'down' key on search field.

516. By Renato Araujo Oliveira Filho

WORKAROUND: SDK does not allow us to disable focus for items due bug: #1514822
because of that we create a new property called '_allowFocus' that we chack before set focus for any object.

517. By Renato Araujo Oliveira Filho

Added support for shortcuts on RemoveContactsDialog.

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

Trunk merged.

519. By Renato Araujo Oliveira Filho

Fixed bottom edge visibility.

520. By Renato Araujo Oliveira Filho

Implemented keyboard navigation on settings page.

521. By Renato Araujo Oliveira Filho

Fixed duplicated shortcut while editing a contact while searching.

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)
522. By Renato Araujo Oliveira Filho

Only set focus on list item after the key down pressed.

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

Fixed focus chain on TextField.

524. By Renato Araujo Oliveira Filho

Fixed currentIndex change while editing a contact.
Right arrow on search field give focus to First Field on editor page.

525. By Renato Araujo Oliveira Filho

"Right" arrow give focus for first field while editing.

526. By Renato Araujo Oliveira Filho

Get rid of 'invalid shortcut' error messages.

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

Fixed bottom edge editor page behaviour.

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)
528. By Renato Araujo Oliveira Filho

Avoid move contact index after editing it.

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

Trunk merged.

530. By Renato Araujo Oliveira Filho

Fixed view page connections after edit a contact.

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)
531. By Renato Araujo Oliveira Filho

Update FastScroll colors.

532. By Renato Araujo Oliveira Filho

Move list to desired contact on 'positionViewAtContact'.

533. By Renato Araujo Oliveira Filho

Change selected item color to organge if the contact list has focus.

534. By Renato Araujo Oliveira Filho

Do not move focus to next item when in the bottom of the list.

535. By Renato Araujo Oliveira Filho

Avoid warning messagins while creating Connection signals with null targes.
Move focus to editor when pressing tab in the contact list.

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

Disable contact editor page while on bottom edge to avoid focus problems.

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

Centralize contact when moving the list to it.

538. By Renato Araujo Oliveira Filho

Do not select any contact while creating a new one.

539. By Renato Araujo Oliveira Filho

Deos not change the foregroundColor of item on settings page if using the single column layout.

540. By Renato Araujo Oliveira Filho

Do not move to next item on right arrow press, while in search field.

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

Removed FastScroll magnified border.

542. By Renato Araujo Oliveira Filho

Does not move focus to search field on right key press.

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

use gray background color for fasctroll tooltip.

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

Close settings page with 'Esc' or 'left arrow'.

545. By Renato Araujo Oliveira Filho

Activate actions on settings page by pressing 'right' key.

546. By Renato Araujo Oliveira Filho

Use a orange rectangle to show the selected item in the settings page.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Florian Boucault (fboucault) wrote :

Can you explain the purpose of forceActiveFocus() in src/imports/ABContactListPage.qml

    function reloadContact() {
        [...]
        contactList.forceActiveFocus()
    }

Revision history for this message
Florian Boucault (fboucault) wrote :

Can you explain the purpose of
    Timer {
        id: fetchNewContactTimer
        [...]
    }

Is it a workaround? if so of what?

547. By Renato Araujo Oliveira Filho

Fixed severam comments from reviewer;

548. By Renato Araujo Oliveira Filho

Removed 'focus' property change.

549. By Renato Araujo Oliveira Filho

Fixed selected color.

550. By Renato Araujo Oliveira Filho

Use palette color instead of hardcoded one.

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)
Revision history for this message
Florian Boucault (fboucault) wrote :

Much better! Thank you, code is good to go.

review: Approve
551. By Renato Araujo Oliveira Filho

Trunk merged.

552. By Renato Araujo Oliveira Filho

Created 'saveActionEnabled' property to avoid problems on tests.
Used that new property instead of search for Action.

553. By Renato Araujo Oliveira Filho

Switch to keyboard visuals as soon as any key is pressed.
Avoid crash on Contact list page with 'esc' key.

554. By Renato Araujo Oliveira Filho

Avoid crash app while saving a contact using shortcut.

555. By Renato Araujo Oliveira Filho

Avoid ambiguous shortcut while search and view contact page openend.

556. By Renato Araujo Oliveira Filho

Removed workarounds necessary for bug #1518420 (fixed)

557. By Renato Araujo Oliveira Filho

Trunk merged.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/app/addressbookapp.cpp'
2--- src/app/addressbookapp.cpp 2015-12-11 16:43:39 +0000
3+++ src/app/addressbookapp.cpp 2015-12-16 18:38:23 +0000
4@@ -104,7 +104,8 @@
5 m_netManager(new QNetworkConfigurationManager),
6 m_pickingMode(false),
7 m_testMode(false),
8- m_withArgs(false)
9+ m_withArgs(false),
10+ m_withKeyboard(false)
11 {
12 s_elapsed.start();
13 setOrganizationName(SETTINGS_ORGANIZATION_NAME);
14@@ -447,6 +448,16 @@
15 qDebug() << "ELAPSED:" << s_elapsed.elapsed() / 1000.0;
16 }
17
18+bool AddressBookApp::notify(QObject *obj, QEvent *event)
19+{
20+ if ((event->type() == QEvent::KeyPress) && !m_withKeyboard) {
21+ m_withKeyboard = true;
22+ Q_EMIT usingKeyboardChanged();
23+ return true;
24+ }
25+ return QGuiApplication::notify(obj, event);
26+}
27+
28 QString AddressBookApp::callbackApplication() const
29 {
30 return m_callbackApplication;
31@@ -475,3 +486,8 @@
32 {
33 return !m_updateWatcher.isNull();
34 }
35+
36+bool AddressBookApp::usingKeyboard() const
37+{
38+ return m_withKeyboard;
39+}
40
41=== modified file 'src/app/addressbookapp.h'
42--- src/app/addressbookapp.h 2015-12-11 16:43:39 +0000
43+++ src/app/addressbookapp.h 2015-12-16 18:38:23 +0000
44@@ -32,6 +32,7 @@
45 Q_PROPERTY(bool isOnline READ isOnline NOTIFY isOnlineChanged)
46 Q_PROPERTY(bool serverSafeMode READ serverSafeMode NOTIFY serverSafeModeChanged)
47 Q_PROPERTY(bool updating READ updating NOTIFY updatingChanged)
48+ Q_PROPERTY(bool usingKeyboard READ usingKeyboard NOTIFY usingKeyboardChanged)
49
50 public:
51 AddressBookApp(int &argc, char **argv);
52@@ -45,12 +46,14 @@
53 bool isOnline() const;
54 bool serverSafeMode() const;
55 bool updating() const;
56+ bool usingKeyboard() const;
57
58 Q_SIGNALS:
59 void callbackApplicationChanged();
60 void isOnlineChanged();
61 void serverSafeModeChanged();
62 void updatingChanged();
63+ void usingKeyboardChanged();
64 void sourcesChanged();
65
66 public Q_SLOTS:
67@@ -66,6 +69,9 @@
68 // debug
69 void elapsed() const;
70
71+protected:
72+ bool notify(QObject *obj, QEvent *event);
73+
74 private Q_SLOTS:
75 void onUpdateCallFinished(QDBusPendingCallWatcher *watcher);
76
77@@ -84,6 +90,7 @@
78 bool m_pickingMode;
79 bool m_testMode;
80 bool m_withArgs;
81+ bool m_withKeyboard;
82 };
83
84 #endif
85
86=== modified file 'src/imports/ABContactEditorPage.qml'
87--- src/imports/ABContactEditorPage.qml 2015-12-11 14:38:17 +0000
88+++ src/imports/ABContactEditorPage.qml 2015-12-16 18:38:23 +0000
89@@ -38,9 +38,11 @@
90
91 text: i18n.tr("Cancel")
92 iconName: "back"
93- onTriggered: {
94- root.cancel()
95- }
96+ // WORKAROUND: SDK does not unregister shortcut on object destruction
97+ // we need to do it manually. (bug #1518420)
98+ enabled: root.active && root.enabled
99+ shortcut: enabled ? "Esc" : undefined
100+ onTriggered: root.cancel()
101 }
102
103 head.actions: [
104@@ -51,8 +53,8 @@
105
106 text: i18n.tr("Save")
107 iconName: "ok"
108- // disable save button while avatar scale still running
109- enabled: root.isContactValid
110+ enabled: root.isContactValid && root.active && root.enabled
111+ shortcut: "Ctrl+s"
112 onTriggered: root.save()
113 }
114 ]
115
116=== modified file 'src/imports/ABContactListPage.qml'
117--- src/imports/ABContactListPage.qml 2015-12-07 08:13:36 +0000
118+++ src/imports/ABContactListPage.qml 2015-12-16 18:38:23 +0000
119@@ -38,10 +38,11 @@
120 property QtObject contactIndex: null
121 property string newPhoneToAdd: ""
122 property alias contactManager: contactList.manager
123- property Page contactViewPage: null
124- property Page contactEditorPage: null
125+ property alias contactViewPage: contactViewPageConnections.target
126+ property alias contactEditorPage: contactEditorPageConnections.target
127 property var _busyDialog: null
128 property bool _importingTestData: false
129+ property bool _creatingContact: false
130
131 readonly property bool bottomEdgePageOpened: bottomEdge.opened && bottomEdge.fullLoaded
132 readonly property bool isEmpty: (contactList.count === 0)
133@@ -87,6 +88,12 @@
134
135 function showContact(contact)
136 {
137+ var currentContact = contactList.listModel.contacts[contactList.currentIndex]
138+ if (currentContact && contactViewPage && contactViewPage.contact && (contactViewPage.contact.contactId === currentContact.contactId)) {
139+ console.debug("Skip show contact")
140+ return
141+ }
142+
143 // go back to normal state if not searching
144 if ((state !== "searching") &&
145 (state !== "vcardImported")) {
146@@ -138,14 +145,12 @@
147
148 function moveListToContact(contact)
149 {
150- // skipt it if searching or importing contacts
151- if ((state === "searching") ||
152- (state === "vcardImported")) {
153- return
154+ if ((state !== "searching") &&
155+ (state !== "vcardImported")) {
156+ mainPage.state = "default"
157 }
158
159 contactIndex = contact
160- mainPage.state = "default"
161 // this means a new contact was created
162 if (mainPage.allowToQuit) {
163 application.goBackToSourceApp()
164@@ -163,22 +168,53 @@
165 contactList.currentIndex = -1;
166 mainPage.contactEditorPage = editorPage;
167 pageStack.addPageToNextColumn(mainPage, editorPage);
168- editorPage.ready();
169 editorPage.contactSaved.connect(onNewContactSaved);
170+ editorPage.enabled = true
171 }
172
173 function onNewContactSaved(contact) {
174+ _creatingContact = true
175+ moveListToContact(contact)
176 if (pageStack.columns > 1) {
177 showContact(contact);
178 }
179 }
180
181+ // Delay contact fetch for some msecs (check 'fetchNewContactTimer')
182+ function delayFetchContact()
183+ {
184+ fetchNewContactTimer.restart()
185+ }
186+
187+ function fetchContact()
188+ {
189+ if ((contactList.currentIndex >= 0) && (pageStack.columns > 1)) {
190+ var currentContact = contactList.listModel.contacts[contactList.currentIndex]
191+ if (contactViewPage && contactViewPage.contact && (contactViewPage.contact.contactId === currentContact.contactId))
192+ return
193+
194+ contactList.view._fetchContact(contactList.currentIndex, currentContact)
195+ }
196+ }
197+
198+ // This timer is to avoid fetch unecessary contact if the user select the contacts too fast
199+ // while navigating on contact list with keyboard
200+ Timer {
201+ id: fetchNewContactTimer
202+
203+ interval: 300
204+ repeat: false
205+ onTriggered: mainPage.fetchContact()
206+ }
207+
208 title: i18n.tr("Contacts")
209-
210 flickable: null
211+
212 ContactsUI.ContactListView {
213 id: contactList
214 objectName: "contactListView"
215+
216+ focus: true
217 showImportOptions: !mainPage.pickMode &&
218 mainPage.newPhoneToAdd === "" &&
219 (!mainPage.contactEditorPage || !mainPage.contactEditorPage.active)
220@@ -188,10 +224,11 @@
221 bottom: keyboard.top
222 right: parent.right
223 }
224+ currentIndex: 0
225 filterTerm: searchField.text
226 multiSelectionEnabled: true
227 multipleSelection: (mainPage.pickMode && mainPage.pickMultipleContacts) || !mainPage.pickMode
228- highlightSelected: pageStack.columns > 1
229+ highlightSelected: application.usingKeyboard && !mainPage._creatingContact
230 onAddContactClicked: mainPage.createContactWithPhoneNumber(label)
231 onAddNewContactClicked: mainPage.createContactWithPhoneNumber(mainPage.newPhoneToAdd)
232
233@@ -210,25 +247,71 @@
234 }
235
236 onError: pageStack.contactModelError(error)
237+ onActiveFocusChanged: {
238+ if (activeFocus && (contactList.currentIndex === -1)) {
239+ contactList.currentIndex = 0
240+ }
241+ }
242+ onCountChanged: {
243+ if (mainPage.active &&
244+ (pageStack.columns > 1) &&
245+ (contactList.currentIndex === -1)) {
246+ contactList.currentIndex = 0
247+ }
248+ mainPage.delayFetchContact()
249+ }
250+ onCurrentIndexChanged: {
251+ if (!mainPage.contactIndex)
252+ mainPage.delayFetchContact()
253+ }
254+
255+ Keys.onReturnPressed: {
256+ var currentContact = contactList.listModel.contacts[contactList.currentIndex]
257+ if (contactViewPage && contactViewPage.contact && (contactViewPage.contact.contactId === currentContact.contactId))
258+ return
259+
260+ contactList.view._fetchContact(contactList.currentIndex, currentContact)
261+ }
262+
263+ //WORKAROUND: SDK does not allow us to disable focus for items due bug: #1514822
264+ //because of that we need this
265+ Keys.onRightPressed: {
266+ // only move focus away when in edit mode
267+ if (mainPage.contactEditorPage) {
268+ var next = pageStack._nextItemInFocusChain(view, true)
269+ if (next === searchField) {
270+ pageStack._nextItemInFocusChain(next, true)
271+ }
272+ }
273+ }
274+ Keys.onTabPressed: {
275+ var next = pageStack._nextItemInFocusChain(view, true)
276+ if (next === searchField) {
277+ pageStack._nextItemInFocusChain(next, true)
278+ }
279+ }
280 }
281
282+
283+
284 TextField {
285 id: searchField
286
287+ //WORKAROUND: SDK does not allow us to disable focus for items due bug: #1514822
288+ //because of that we need this
289+ readonly property bool _allowFocus: true
290+
291 anchors {
292- left: parent.left
293- right: parent.right
294+ left: parent ? parent.left : undefined
295+ right: parent ? parent.right : undefined
296 rightMargin: units.gu(2)
297 }
298- focus: false
299+
300 visible: false
301- onTextChanged: contactList.currentIndex = -1
302 inputMethodHints: Qt.ImhNoPredictiveText
303 placeholderText: i18n.tr("Search...")
304- onFocusChanged: {
305- if (visible && focus)
306- searchField.forceActiveFocus()
307- }
308+ Keys.onTabPressed: contactList.forceActiveFocus()
309+ Keys.onDownPressed: contactList.forceActiveFocus()
310 }
311
312 Connections {
313@@ -267,6 +350,8 @@
314 text: i18n.tr("Search")
315 iconName: "search"
316 visible: !mainPage.isEmpty
317+ enabled: mainPage.state === "default"
318+ shortcut: "Ctrl+F"
319 onTriggered: {
320 mainPage.state = (mainPage.state === "newphone" ? "newphoneSearching" : "searching")
321 contactList.showAllContacts()
322@@ -289,9 +374,21 @@
323 Action {
324 text: i18n.tr("Settings")
325 iconName: "settings"
326- onTriggered: pageStack.addPageToNextColumn(mainPage,
327- Qt.resolvedUrl("./Settings/SettingsPage.qml"),
328- {"contactListModel": contactList.listModel})
329+ onTriggered:{
330+ var incubator = pageStack.addPageToNextColumn(mainPage,
331+ Qt.resolvedUrl("./Settings/SettingsPage.qml"),
332+ {"contactListModel": contactList.listModel})
333+ incubator.onStatusChanged = function(status) {
334+ if (status === Component.Ready) {
335+ incubator.object.onActiveChanged.connect(function(active) {
336+ if (!incubator.object.active) {
337+ mainPage.delayFetchContact()
338+ contactList.forceActiveFocus()
339+ }
340+ })
341+ }
342+ }
343+ }
344 }
345 ]
346 PropertyChanges {
347@@ -305,6 +402,10 @@
348 target: searchField
349 text: ""
350 }
351+ PropertyChanges {
352+ target: bottomEdge
353+ enabled: true
354+ }
355 },
356 PageHeadState {
357 id: searchingState
358@@ -313,10 +414,12 @@
359 backAction: Action {
360 iconName: "back"
361 text: i18n.tr("Cancel")
362+ enabled: mainPage.state === "searching" && !mainPage.contactEditorPage && mainPage.active
363+ shortcut: "Esc"
364 onTriggered: {
365- contactList.forceActiveFocus()
366 mainPage.head.sections.selectedIndex = 0
367 mainPage.state = (mainPage.state === "newphoneSearching" ? "newphone" : "default")
368+ contactList.forceActiveFocus()
369 }
370 }
371
372@@ -349,6 +452,8 @@
373 backAction: Action {
374 text: i18n.tr("Cancel selection")
375 iconName: "back"
376+ enabled: mainPage.state === "selection"
377+ shortcut: "Esc"
378 onTriggered: contactList.cancelSelection()
379 }
380 actions: [
381@@ -495,8 +600,11 @@
382 if (active && contactList.showAddNewButton) {
383 contactList.positionViewAtBeginning()
384 }
385+
386 if (active && (state === "searching")) {
387 searchField.forceActiveFocus()
388+ } else if (active) {
389+ contactList.forceActiveFocus()
390 }
391 }
392
393@@ -712,18 +820,19 @@
394 var newContact = ContactsJS.createEmptyContact("", mainPage);
395 createObjectAsynchronously(Qt.resolvedUrl("ABContactEditorPage.qml"),
396 {model: contactList.listModel,
397+ enabled: false,
398 contact: newContact,
399 initialFocusSection: "name"},
400- showContactEditorPage);
401+ showContactEditorPage);
402 }
403
404 anchors.fill: parent
405- contentComponent: pageStack.columns == 1 ? editorPageBottomEdge : emptyContact
406+ contentComponent: pageStack.columns === 1 ? editorPageBottomEdge : emptyContact
407 flickable: contactList
408 iconName: "contact-new"
409- enabled: !contactList.isInSelectionMode
410 backGroundEffectEnabled: pageStack.columns === 1
411
412+ onBottomEdgeLoaded: contactList.forceActiveFocus()
413 onOpenBegin: {
414 contactList.prepareNewContact = true;
415 contactList.positionViewAtBeginning();
416@@ -732,7 +841,6 @@
417 }
418 }
419 onOpenEnd: {
420- bottomEdge.visible = false;
421 contactList.showNewContact = true;
422 if (pageStack.columns <= 1) {
423 showContactEditorPage(bottomEdge.content);
424@@ -746,10 +854,13 @@
425
426 Connections {
427 target: mainPage.contactModel
428+
429 onContactsChanged: {
430 if (contactIndex) {
431 contactList.positionViewAtContact(mainPage.contactIndex)
432 mainPage.contactIndex = null
433+ // at this point the operation has finished already
434+ mainPage._creatingContact = false
435 }
436 }
437 onImportCompleted: {
438@@ -779,20 +890,33 @@
439 }
440
441 Connections {
442- target: mainPage.contactViewPage
443- onEditContact: {
444- openEditPage(editPageProperties, mainPage.contactViewPage);
445+ id: contactViewPageConnections
446+
447+ ignoreUnknownSignals: true
448+ onEditContact: openEditPage(editPageProperties, mainPage.contactViewPage);
449+ onActiveChanged: {
450+ if (mainPage.contactViewPage &&
451+ !mainPage.contactViewPage.active &&
452+ (mainPage.contactEditorPage == null)) { // not editing
453+ mainPage.contactViewPage = null
454+ }
455 }
456 }
457
458 Connections {
459- target: mainPage.contactEditorPage
460+ id: contactEditorPageConnections
461+
462+ ignoreUnknownSignals: true
463 onActiveChanged: {
464- if (!mainPage.contactEditorPage.active) {
465+ if (mainPage.contactEditorPage && !mainPage.contactEditorPage.active) {
466 contactList.prepareNewContact = false;
467 contactList.showNewContact = false;
468- bottomEdge.visible = true;
469 bottomEdge.close();
470+ mainPage.contactEditorPage = null
471+ contactList.forceActiveFocus()
472+ bottomEdge.enabled = true
473+ } else if (mainPage.contactEditorPage && !mainPage.contactEditorPage.active) {
474+ bottomEdge.enabled = false
475 }
476 }
477 }
478
479=== modified file 'src/imports/ABContactViewPage.qml'
480--- src/imports/ABContactViewPage.qml 2015-10-28 01:08:39 +0000
481+++ src/imports/ABContactViewPage.qml 2015-12-16 18:38:23 +0000
482@@ -30,6 +30,16 @@
483 property string addPhoneToContact: ""
484 signal editContact(var editPageProperties)
485
486+ // Override Action buttom to add shortcut to it
487+ Action {
488+ id: backAction
489+
490+ name: "cancel"
491+ enabled: root.active && root.enabled && (pageStack.columns === 1)
492+ shortcut: "Esc"
493+ onTriggered: pageStack.removePages(root)
494+ }
495+
496 head.actions: [
497 Action {
498 objectName: "share"
499@@ -50,6 +60,8 @@
500
501 text: i18n.tr("Edit")
502 iconName: "edit"
503+ enabled: root.active
504+ shortcut: "Ctrl+e"
505 onTriggered: {
506 editContact({model: root.model,
507 contact: root.contact});
508
509=== modified file 'src/imports/BottomEdge.qml'
510--- src/imports/BottomEdge.qml 2015-11-19 01:02:12 +0000
511+++ src/imports/BottomEdge.qml 2015-12-16 18:38:23 +0000
512@@ -32,7 +32,7 @@
513 signal openBegin
514 signal openEnd
515 signal clicked
516- visible: enabled
517+ signal bottomEdgeLoaded
518
519 function open() {
520 bottomEdge.state = "expanded";
521@@ -42,6 +42,13 @@
522 bottomEdge.state = "collapsed";
523 }
524
525+ Action {
526+ text: i18n.tr("New contact")
527+ enabled: bottomEdge.visible
528+ shortcut: "Ctrl+N"
529+ onTriggered: bottomEdge.clicked()
530+ }
531+
532 Rectangle {
533 id: darkBg
534
535@@ -90,6 +97,10 @@
536 sourceComponent: bottomEdge.contentComponent
537 asynchronous: true
538 active: bottomEdge.enabled
539+ onStatusChanged: {
540+ if (status === Loader.Ready)
541+ bottomEdge.bottomEdgeLoaded()
542+ }
543 }
544 }
545
546@@ -318,4 +329,10 @@
547 dragDirection = yOffset > 0 ? "BottomToTop" : "TopToBottom";
548 }
549 }
550+
551+ Binding {
552+ target: bottomEdge
553+ property: 'visible'
554+ value: bottomEdge.enabled && !bottomEdge.opened
555+ }
556 }
557
558=== modified file 'src/imports/MainWindow.qml'
559--- src/imports/MainWindow.qml 2015-10-26 13:18:11 +0000
560+++ src/imports/MainWindow.qml 2015-12-16 18:38:23 +0000
561@@ -105,7 +105,6 @@
562 AdaptivePageLayout {
563 id: mainStack
564
565- primaryPage: contactPage
566 property var contactListPage: null
567
568 function resetStack()
569@@ -113,6 +112,28 @@
570 mainStack.removePages(primaryPage);
571 }
572
573+ function _nextItemInFocusChain(item, foward)
574+ {
575+ var next = item.nextItemInFocusChain(foward)
576+ var first = next
577+ //WORKAROUND: SDK does not allow us to disable focus for items due bug: #1514822
578+ //because of that we need this
579+ while (!next || !next.hasOwnProperty("_allowFocus")) {
580+ next = next.nextItemInFocusChain(foward)
581+
582+ // avoid loop
583+ if (next === first) {
584+ next = null
585+ break
586+ }
587+ }
588+ if (next) {
589+ next.forceActiveFocus()
590+ }
591+ return next
592+ }
593+
594+ primaryPage: contactPage
595 onContactListPageChanged: {
596 if (contentHubLoader.status === Loader.Ready) {
597 contentHubLoader.item.pageStack = mainStack
598@@ -141,6 +162,7 @@
599 }
600 }
601 ]
602+
603 }
604
605 ABContactListPage {
606@@ -196,6 +218,10 @@
607
608 // If application was called from uri handler and lost the focus reset the app to normal state
609 onAppActiveChanged: {
610+ if (appActive) {
611+ mainStack.forceActiveFocus()
612+ }
613+
614 if (!appActive && mainStack.contactListPage) {
615 mainStack.contactListPage.returnToNormalState()
616 }
617
618=== modified file 'src/imports/Settings/SettingsPage.qml'
619--- src/imports/Settings/SettingsPage.qml 2015-12-11 13:14:05 +0000
620+++ src/imports/Settings/SettingsPage.qml 2015-12-16 18:38:23 +0000
621@@ -1,4 +1,4 @@
622-/*
623+/*
624 * Copyright (C) 2015 Canonical, Ltd.
625 *
626 * This program is free software; you can redistribute it and/or modify
627@@ -64,15 +64,66 @@
628 onCountChanged: numberFlickable.contentY = 0
629 }
630 ListItem.Standard {
631+ id: addGoogleAccountItem
632+
633+ function activate()
634+ {
635+ onlineAccountsHelper.setupExec()
636+ }
637+
638 text: i18n.tr("Add Google account")
639 progression: true
640- onClicked: onlineAccountsHelper.setupExec()
641+
642+ onClicked: addGoogleAccountItem.activate()
643+ Keys.onRightPressed: addGoogleAccountItem.activate()
644+ Keys.onDownPressed: {
645+ if (importFromSimItem.enabled) {
646+ importFromSimItem.forceActiveFocus()
647+ }
648+ }
649+
650+ // Selection visual feedback
651+ //
652+ // FIXME: Using a private property here. This uses the old list item and the only way to change the text
653+ // color is with this property.
654+ // We should remove it when update the app to the new ListItem.
655+ __foregroundColor: (activeFocus && (pageStack.columns > 1)) ? Theme.palette.normal.foregroundText :
656+ Theme.palette.normal.foreground
657+ Rectangle {
658+ color: UbuntuColors.orange
659+ anchors.fill: parent
660+ visible:addGoogleAccountItem.activeFocus
661+ z: -1
662+ }
663 }
664 ListItem.Standard {
665+ id: importFromSimItem
666+
667+ function activate()
668+ {
669+ pageStack.addPageToCurrentColumn(root, simCardImportPageComponent)
670+ }
671+
672 text: i18n.tr("Import from SIM")
673 progression: true
674- onClicked: pageStack.addPageToCurrentColumn(root, simCardImportPageComponent)
675 enabled: (simList.sims.length > 0) && (simList.present.length > 0)
676+ onClicked: importFromSimItem.activate()
677+ Keys.onRightPressed: importFromSimItem.activate()
678+ Keys.onUpPressed: addGoogleAccountItem.forceActiveFocus()
679+
680+ // selection visual feedback
681+ //
682+ // FIXME: Using a private property here. This uses the old list item and the only way to change the text
683+ // color is with this property.
684+ // We should remove it when update the app to the new ListItem.
685+ __foregroundColor: (activeFocus && (pageStack.columns > 1)) ? Theme.palette.normal.foregroundText :
686+ Theme.palette.normal.foreground
687+ Rectangle {
688+ color: UbuntuColors.orange
689+ anchors.fill: parent
690+ visible: importFromSimItem.activeFocus
691+ z: -1
692+ }
693 }
694 SettingsDefaultSyncTarget {
695 id: defaultSyncTarget
696@@ -97,8 +148,13 @@
697 }
698 }
699
700+ Keys.onDownPressed: addGoogleAccountItem.forceActiveFocus()
701+ Keys.onRightPressed: addGoogleAccountItem.forceActiveFocus()
702+ Keys.onLeftPressed: pageStack.removePages(root)
703+ Keys.onEscapePressed: pageStack.removePages(root)
704 onActiveChanged: {
705 if (active) {
706+ root.forceActiveFocus()
707 defaultSyncTarget.update()
708 }
709 }
710
711=== modified file 'src/imports/Ubuntu/AddressBook/Base/ContactDetailBase.qml'
712--- src/imports/Ubuntu/AddressBook/Base/ContactDetailBase.qml 2015-10-26 13:18:11 +0000
713+++ src/imports/Ubuntu/AddressBook/Base/ContactDetailBase.qml 2015-12-16 18:38:23 +0000
714@@ -18,15 +18,19 @@
715 import QtContacts 5.0 as QtContacts
716 import Ubuntu.Components.ListItems 1.3 as ListItem
717
718-ListItem.Empty {
719+FocusScope {
720 id: root
721 objectName: detail ? "base_" + detailToString(detail.type, -1) + "_" + index : ""
722
723+ property variant action: null
724 property QtObject contact: null
725 property QtObject detail: null
726 property variant fields: null
727 // help to test used to retrieve the correct element
728 property int index: -1
729+ property alias highlightOnFocus: highlight.visible
730+
731+ signal clicked()
732
733 function detailToString(detail, field)
734 {
735@@ -98,13 +102,23 @@
736 }
737 }
738
739- highlightWhenPressed: false
740- showDivider: false
741-
742 Rectangle {
743- anchors.fill: parent
744- opacity: 0.1
745- visible: root.selected
746- z: 100
747+ id: highlight
748+
749+ anchors.fill: parent
750+ visible: root.activeFocus
751+ color: Theme.palette.selected.background
752+ z: -1
753+ }
754+
755+ MouseArea {
756+ anchors.fill: parent
757+
758+ onClicked: {
759+ if (action) {
760+ action.triggered(action)
761+ }
762+ root.clicked()
763+ }
764 }
765 }
766
767=== modified file 'src/imports/Ubuntu/AddressBook/Base/ContactDetailGroupBase.qml'
768--- src/imports/Ubuntu/AddressBook/Base/ContactDetailGroupBase.qml 2015-10-26 13:18:11 +0000
769+++ src/imports/Ubuntu/AddressBook/Base/ContactDetailGroupBase.qml 2015-12-16 18:38:23 +0000
770@@ -33,6 +33,7 @@
771 property int minimumHeight: 0
772 property bool loaded: false
773 property bool showEmpty: true
774+ property bool forceFocusOnFieldCreation: false
775
776 signal newFieldAdded(var index)
777
778@@ -146,7 +147,7 @@
779 newFields.push(detailItem.item)
780 root.newFieldAdded(detailItem.item)
781 root.inputFields = newFields
782- if (root.loaded) {
783+ if (root.loaded && root.forceFocusOnFieldCreation) {
784 item.forceActiveFocus()
785 }
786 }
787
788=== modified file 'src/imports/Ubuntu/AddressBook/Base/RemoveContactsDialog.qml'
789--- src/imports/Ubuntu/AddressBook/Base/RemoveContactsDialog.qml 2015-10-26 13:18:11 +0000
790+++ src/imports/Ubuntu/AddressBook/Base/RemoveContactsDialog.qml 2015-12-16 18:38:23 +0000
791@@ -53,6 +53,7 @@
792 }
793
794 Button {
795+ id: acceptButton
796 objectName: "removeContactsDialog.Yes"
797 anchors {
798 left: parent.left
799@@ -61,10 +62,14 @@
800 }
801 text: i18n.dtr("address-book-app", "Yes")
802 color: UbuntuColors.green
803- onClicked: accepted()
804+ action: Action {
805+ shortcut: "return"
806+ onTriggered: accepted()
807+ }
808 }
809
810 Button {
811+ id: cancelButton
812 objectName: "removeContactsDialog.No"
813 anchors {
814 left: parent.left
815@@ -73,6 +78,9 @@
816 }
817 text: i18n.dtr("address-book-app", "No")
818 color: UbuntuColors.red
819- onClicked: canceled()
820+ action: Action {
821+ shortcut: "esc"
822+ onTriggered: canceled()
823+ }
824 }
825 }
826
827=== modified file 'src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailAvatarEditor.qml'
828--- src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailAvatarEditor.qml 2015-10-29 18:11:08 +0000
829+++ src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailAvatarEditor.qml 2015-12-16 18:38:23 +0000
830@@ -68,6 +68,7 @@
831 detail: contact ? contact.detail(ContactDetail.Avatar) : null
832 implicitHeight: units.gu(8)
833 implicitWidth: units.gu(8)
834+ highlightOnFocus: false
835
836 UbuntuShape {
837 id: avatar
838
839=== modified file 'src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailGroupWithTypeEditor.qml'
840--- src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailGroupWithTypeEditor.qml 2015-10-26 13:18:11 +0000
841+++ src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailGroupWithTypeEditor.qml 2015-12-16 18:38:23 +0000
842@@ -98,29 +98,19 @@
843 return changed
844 }
845
846- headerDelegate: Empty {
847+ forceFocusOnFieldCreation: true
848+ headerDelegate: Label {
849 id: header
850- highlightWhenPressed: false
851-
852- width: root.width
853- height: units.gu(5)
854- // disable listview mouse area
855- __mouseArea.visible: false
856- divider.anchors.leftMargin: units.gu(2)
857- divider.anchors.rightMargin: units.gu(2)
858-
859- Label {
860- anchors {
861- verticalCenter: parent.verticalCenter
862- left: parent.left
863- right: parent.right
864- margins: units.gu(2)
865- }
866-
867- text: root.title
868-
869- // style
870- fontSize: "medium"
871+
872+ width: root.width - units.gu(4)
873+ x: units.gu(2)
874+ height: units.gu(4)
875+ text: root.title
876+ // style
877+ fontSize: "medium"
878+ verticalAlignment: Text.AlignVCenter
879+ ThinDivider {
880+ anchors.bottom: parent.bottom
881 }
882 }
883
884@@ -157,7 +147,6 @@
885 fields: root.fields
886 height: implicitHeight
887 width: root.width
888- selected: activeFocus
889
890 inputMethodHints: root.inputMethodHints
891 onDetailChanged: updateCombo(false)
892
893=== modified file 'src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailNameEditor.qml'
894--- src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailNameEditor.qml 2015-10-26 13:18:11 +0000
895+++ src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailNameEditor.qml 2015-12-16 18:38:23 +0000
896@@ -55,6 +55,7 @@
897 spacing: units.gu(1)
898 detail: root.contact ? root.contact.name : null
899 fields: [ Name.FirstName, Name.LastName ]
900+ highlightOnFocus: false
901
902 fieldDelegate: TextInputDetail {
903 id: textInputDetail
904
905=== modified file 'src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailSyncTargetEditor.qml'
906--- src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailSyncTargetEditor.qml 2015-12-11 16:43:39 +0000
907+++ src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailSyncTargetEditor.qml 2015-12-16 18:38:23 +0000
908@@ -75,6 +75,7 @@
909
910 detail: root.contact ? contact.detail(ContactDetail.SyncTarget) : null
911 implicitHeight: root.isNewContact && sources.model && (sources.model.count > 1) ? myHeight : 0
912+ visible: height > 0
913
914 ContactModel {
915 id: sourceModel
916@@ -188,8 +189,12 @@
917 ThinDivider {
918 id: divider
919
920- anchors.top: label.bottom
921- }
922+ anchors {
923+ top: label.bottom
924+ leftMargin: units.gu(2)
925+ rightMargin: units.gu(2)
926+ }
927+ }
928
929 OptionSelector {
930 id: sources
931
932=== modified file 'src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailWithTypeEditor.qml'
933--- src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailWithTypeEditor.qml 2015-10-26 13:18:11 +0000
934+++ src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailWithTypeEditor.qml 2015-12-16 18:38:23 +0000
935@@ -70,7 +70,6 @@
936 }
937
938 // disable listview mouse area
939- __mouseArea.visible: false
940 enabled: root.detail ? !root.detail.readOnly : false
941 implicitHeight: detailTypeSelector.height + fieldValues.height + units.gu(2)
942 opacity: enabled ? 1.0 : 0.5
943
944=== modified file 'src/imports/Ubuntu/AddressBook/ContactEditor/ContactEditorPage.qml'
945--- src/imports/Ubuntu/AddressBook/ContactEditor/ContactEditorPage.qml 2015-10-26 13:18:11 +0000
946+++ src/imports/Ubuntu/AddressBook/ContactEditor/ContactEditorPage.qml 2015-12-16 18:38:23 +0000
947@@ -31,7 +31,6 @@
948 property QtObject model: null
949 property QtObject activeItem: null
950
951-
952 property string initialFocusSection: ""
953 property var newDetails: []
954
955@@ -40,9 +39,6 @@
956
957 signal contactSaved(var contact);
958
959- // priv
960- property bool _edgeReady: false
961-
962 function cancel() {
963 for (var i = 0; i < contactEditor.newDetails.length; ++i) {
964 contactEditor.contact.removeDetail(contactEditor.newDetails[i])
965@@ -105,7 +101,7 @@
966 }
967
968 function makeMeVisible(item) {
969- if (!_edgeReady || !item) {
970+ if (!enabled || !item) {
971 return
972 }
973
974@@ -133,8 +129,6 @@
975 function ready()
976 {
977 enabled = true
978- _edgeReady = true
979-
980 switch (contactEditor.initialFocusSection)
981 {
982 case "phones":
983@@ -153,6 +147,7 @@
984 }
985
986 title: isNewContact ? i18n.dtr("address-book-app", "New contact") : i18n.dtr("address-book-app", "Edit")
987+ enabled: false
988
989 Timer {
990 id: focusTimer
991@@ -181,7 +176,9 @@
992 contentWidth: parent.width
993
994 //after add a new field we need to wait for the contentHeight to change to scroll to the correct position
995- onContentHeightChanged: contactEditor.makeMeVisible(contactEditor.activeItem)
996+ onContentHeightChanged: {
997+ contactEditor.makeMeVisible(contactEditor.activeItem)
998+ }
999
1000 Column {
1001 id: contents
1002@@ -195,6 +192,7 @@
1003 height: childrenRect.height
1004
1005 Row {
1006+ id: editEditor
1007 function save()
1008 {
1009 var avatarSave = avatarEditor.save()
1010@@ -213,7 +211,7 @@
1011 leftMargin: units.gu(2)
1012 right: parent.right
1013 }
1014- height: Math.max(avatarEditor.height, nameEditor.height) - units.gu(4)
1015+ height: Math.max(avatarEditor.height, nameEditor.height) - units.gu(2)
1016
1017 ContactDetailAvatarEditor {
1018 id: avatarEditor
1019@@ -221,6 +219,7 @@
1020 contact: contactEditor.contact
1021 height: implicitHeight
1022 width: implicitWidth
1023+ anchors.verticalCenter: editEditor.verticalCenter
1024 }
1025
1026 ContactDetailNameEditor {
1027@@ -232,6 +231,7 @@
1028 }
1029 }
1030
1031+
1032 ContactDetailPhoneNumbersEditor {
1033 id: phonesEditor
1034 objectName: "phones"
1035@@ -301,8 +301,6 @@
1036 left: parent.left
1037 right: parent.right
1038 }
1039- height: implicitHeight
1040-
1041 onChanged: {
1042 if (contactEditor.enabled &&
1043 !contactEditor.isNewContact &&
1044@@ -383,9 +381,13 @@
1045 right: parent.right
1046 margins: units.gu(2)
1047 }
1048- onClicked: {
1049- var dialog = PopupUtils.open(removeContactDialog, null)
1050- dialog.contacts = [contactEditor.contact]
1051+ action: Action {
1052+ enabled: contactEditor.active && deleteButton.visible
1053+ shortcut: "Ctrl+Delete"
1054+ onTriggered: {
1055+ var dialog = PopupUtils.open(removeContactDialog, null)
1056+ dialog.contacts = [contactEditor.contact]
1057+ }
1058 }
1059 }
1060
1061@@ -410,8 +412,8 @@
1062 }
1063 }
1064
1065- Component.onCompleted: {
1066- if (!enabled) {
1067+ onActiveChanged: {
1068+ if (!active) {
1069 return
1070 }
1071
1072@@ -475,10 +477,14 @@
1073 }
1074
1075 // hide virtual keyboard if necessary
1076- Component.onCompleted: Qt.inputMethod.hide()
1077+ Component.onCompleted: {
1078+ contactEditor.enabled = false
1079+ Qt.inputMethod.hide()
1080+ }
1081
1082 // WORKAROUND: SDK element crash if pop the page where the dialog was created
1083 Component.onDestruction: {
1084+ contactEditor.enabled = true
1085 if (popPages) {
1086 if (contactEditor.pageStack.removePages) {
1087 contactEditor.pageStack.removePages(contactEditor)
1088
1089=== modified file 'src/imports/Ubuntu/AddressBook/ContactEditor/TextInputDetail.qml'
1090--- src/imports/Ubuntu/AddressBook/ContactEditor/TextInputDetail.qml 2015-10-26 13:18:11 +0000
1091+++ src/imports/Ubuntu/AddressBook/ContactEditor/TextInputDetail.qml 2015-12-16 18:38:23 +0000
1092@@ -25,7 +25,11 @@
1093 FocusScope {
1094 id: root
1095
1096+ //WORKAROUND: SDK does not allow us to disable focus for items due bug: #1514822
1097+ //because of that we need this
1098+ readonly property bool _allowFocus: true
1099 readonly property bool isTextField: true
1100+
1101 property QtObject detail
1102 property int field: -1
1103 property variant originalValue: root.detail && (root.field >= 0) ? root.detail.value(root.field) : null
1104@@ -67,6 +71,22 @@
1105 PhoneNumberField {
1106 id: field
1107
1108+ //WORKAROUND: Due the SDK bug #1514822, #1514850 we can not disable focus for some items
1109+ //because of that we keep the focus only for textFields. This will block the user
1110+ //to use keyboard on "add-field" combo box and some other functionalities
1111+ function forceActiveFocusForNextField(keyEvent)
1112+ {
1113+ var backward = (keyEvent.modifiers & Qt.ShiftModifier)
1114+ var next = field.nextItemInFocusChain(!backward)
1115+ // only focus on TextInputDetails
1116+ while (!next || !next.hasOwnProperty("isTextField")) {
1117+ next = next.nextItemInFocusChain(!backward)
1118+ }
1119+ if (next) {
1120+ next.forceActiveFocus()
1121+ }
1122+ }
1123+
1124 anchors.fill: parent
1125 defaultRegion: PhoneUtils.defaultRegion
1126 autoFormat: false
1127@@ -93,15 +113,9 @@
1128 family: "Ubuntu"
1129 pixelSize: activeFocus ? FontUtils.sizeToPixels("large") : FontUtils.sizeToPixels("medium")
1130 }
1131- Keys.onReturnPressed: {
1132- var next = field.nextItemInFocusChain(true)
1133- // only focus on TextInputDetails
1134- while (!next || !next.hasOwnProperty("isTextField")) {
1135- next = next.nextItemInFocusChain(true)
1136- }
1137- if (next) {
1138- next.forceActiveFocus()
1139- }
1140- }
1141+
1142+ Keys.onReturnPressed: forceActiveFocusForNextField(event)
1143+ Keys.onTabPressed: forceActiveFocusForNextField(event)
1144+ Keys.onBacktabPressed: forceActiveFocusForNextField(event)
1145 }
1146 }
1147
1148=== modified file 'src/imports/Ubuntu/AddressBook/ContactView/ActionButton.qml'
1149--- src/imports/Ubuntu/AddressBook/ContactView/ActionButton.qml 2015-10-26 13:18:11 +0000
1150+++ src/imports/Ubuntu/AddressBook/ContactView/ActionButton.qml 2015-12-16 18:38:23 +0000
1151@@ -30,5 +30,6 @@
1152 anchors.centerIn: parent
1153 height: root.iconSize
1154 width: root.iconSize
1155+ color: root.activeFocus ? UbuntuColors.orange : "gray"
1156 }
1157 }
1158
1159=== modified file 'src/imports/Ubuntu/AddressBook/ContactView/BasicFieldView.qml'
1160--- src/imports/Ubuntu/AddressBook/ContactView/BasicFieldView.qml 2015-10-26 13:18:11 +0000
1161+++ src/imports/Ubuntu/AddressBook/ContactView/BasicFieldView.qml 2015-12-16 18:38:23 +0000
1162@@ -27,6 +27,7 @@
1163 property variant fields: null
1164 property int parentIndex: -1
1165
1166+ activeFocusOnTab: false
1167 implicitHeight: typeLabel.height + fieldValues.height + units.gu(2)
1168
1169 Column {
1170
1171=== modified file 'src/imports/Ubuntu/AddressBook/ContactView/ContactDetailAvatarView.qml'
1172--- src/imports/Ubuntu/AddressBook/ContactView/ContactDetailAvatarView.qml 2015-11-16 20:12:41 +0000
1173+++ src/imports/Ubuntu/AddressBook/ContactView/ContactDetailAvatarView.qml 2015-12-16 18:38:23 +0000
1174@@ -29,6 +29,7 @@
1175
1176 implicitHeight: units.gu(12)
1177 implicitWidth: parent.width
1178+ activeFocusOnTab: false
1179
1180 Connections {
1181 id: connections
1182
1183=== modified file 'src/imports/Ubuntu/AddressBook/ContactView/ContactDetailGroupWithTypeView.qml'
1184--- src/imports/Ubuntu/AddressBook/ContactView/ContactDetailGroupWithTypeView.qml 2015-10-26 13:18:11 +0000
1185+++ src/imports/Ubuntu/AddressBook/ContactView/ContactDetailGroupWithTypeView.qml 2015-12-16 18:38:23 +0000
1186@@ -18,8 +18,7 @@
1187 import QtContacts 5.0 as QtContacts
1188
1189 import Ubuntu.Components 1.3
1190-import Ubuntu.Components.ListItems 1.3 as ListItem
1191-
1192+import Ubuntu.Components.ListItems 1.3
1193 import Ubuntu.AddressBook.Base 0.1
1194
1195 ContactDetailGroupWithTypeBase {
1196@@ -29,25 +28,18 @@
1197 signal actionTrigerred(string actionName, QtObject detail)
1198
1199 showEmpty: false
1200- headerDelegate: ListItem.Empty {
1201- highlightWhenPressed: false
1202-
1203- divider.anchors.leftMargin: units.gu(2)
1204- divider.anchors.rightMargin: units.gu(2)
1205- width: root.width
1206- height: units.gu(5)
1207- Label {
1208- anchors {
1209- verticalCenter: parent.verticalCenter
1210- left: parent.left
1211- right: parent.right
1212- margins: units.gu(2)
1213- }
1214-
1215- text: root.title
1216-
1217- // style
1218- fontSize: "medium"
1219+ headerDelegate: Label {
1220+ id: header
1221+
1222+ width: root.width - units.gu(4)
1223+ x: units.gu(2)
1224+ height: units.gu(4)
1225+ text: root.title
1226+ // style
1227+ fontSize: "medium"
1228+ verticalAlignment: Text.AlignVCenter
1229+ ThinDivider {
1230+ anchors.bottom: parent.bottom
1231 }
1232 }
1233
1234
1235=== modified file 'src/imports/Ubuntu/AddressBook/ContactView/ContactDetailNameView.qml'
1236--- src/imports/Ubuntu/AddressBook/ContactView/ContactDetailNameView.qml 2015-10-26 13:18:11 +0000
1237+++ src/imports/Ubuntu/AddressBook/ContactView/ContactDetailNameView.qml 2015-12-16 18:38:23 +0000
1238@@ -25,7 +25,7 @@
1239
1240 detail: root.contact ? root.contact.name : null
1241 implicitHeight: label.paintedHeight + (label.anchors.margins * 2)
1242-
1243+ activeFocusOnTab: false
1244
1245 Label {
1246 id: label
1247
1248=== modified file 'src/imports/Ubuntu/AddressBook/ContactView/ContactDetailSyncTargetView.qml'
1249--- src/imports/Ubuntu/AddressBook/ContactView/ContactDetailSyncTargetView.qml 2015-10-26 13:18:11 +0000
1250+++ src/imports/Ubuntu/AddressBook/ContactView/ContactDetailSyncTargetView.qml 2015-12-16 18:38:23 +0000
1251@@ -50,6 +50,7 @@
1252 defaultIcon: "image://theme/contact-group"
1253 detailType: ContactDetail.SyncTarget
1254 typeModel: null
1255+ activeFocusOnTab: false
1256
1257 fields: [ SyncTarget.SyncTarget ]
1258
1259@@ -76,7 +77,7 @@
1260
1261 height: implicitHeight
1262 width: root.width
1263-
1264+ activeFocusOnTab: false
1265 onClicked: root.actionTrigerred(root.defaultAction.name, detail)
1266
1267 function overrideValue(detail, field)
1268
1269=== modified file 'src/imports/Ubuntu/AddressBook/ContactView/ContactDetailWithTypeView.qml'
1270--- src/imports/Ubuntu/AddressBook/ContactView/ContactDetailWithTypeView.qml 2015-10-26 13:18:11 +0000
1271+++ src/imports/Ubuntu/AddressBook/ContactView/ContactDetailWithTypeView.qml 2015-12-16 18:38:23 +0000
1272@@ -43,6 +43,7 @@
1273 return detail.value(field)
1274 }
1275
1276+ activeFocusOnTab: icon.visible
1277 implicitHeight: view.implicitHeight
1278 onIsReadyChanged: populateValues()
1279
1280@@ -78,5 +79,7 @@
1281 width: root.action && (root.action.iconName !== "") ? units.gu(2.5) : 0
1282 height: width
1283 name: root.action ? root.action.iconName : ""
1284+ color: root.activeFocus ? UbuntuColors.orange : "gray"
1285+ visible: width > 0
1286 }
1287 }
1288
1289=== modified file 'src/imports/Ubuntu/Contacts/ContactListView.qml'
1290--- src/imports/Ubuntu/Contacts/ContactListView.qml 2015-11-16 22:12:29 +0000
1291+++ src/imports/Ubuntu/Contacts/ContactListView.qml 2015-12-16 18:38:23 +0000
1292@@ -42,7 +42,7 @@
1293 }
1294 \endqml
1295 */
1296-Item {
1297+FocusScope {
1298 id: root
1299
1300 readonly property alias view: view
1301@@ -253,6 +253,10 @@
1302
1303 property var _busyDialog: null
1304
1305+ //WORKAROUND: SDK does not allow us to disable focus for items due bug: #1514822
1306+ //because of that we need this
1307+ property bool _allowFocus: true
1308+
1309 /*!
1310 This handler is called when the selection mode is finished without be canceled
1311 */
1312@@ -372,6 +376,8 @@
1313 buteoSync.startSyncByCategory("contacts")
1314 }
1315
1316+ focus: true
1317+
1318 ContactSimpleListView {
1319 id: view
1320
1321@@ -624,4 +630,23 @@
1322 !view.favouritesIsSelected &&
1323 !isSearching ? sourceFile : ""
1324 }
1325+
1326+ Keys.onUpPressed: {
1327+ //WORKAROUND: SDK does not allow us to disable focus for items due bug: #1514822
1328+ //because of that we need this
1329+ if (view.currentIndex == 0) {
1330+ pageStack._nextItemInFocusChain(view, false)
1331+ } else {
1332+ view.currentIndex -= 1
1333+ }
1334+ }
1335+ Keys.onDownPressed: {
1336+ //WORKAROUND: SDK does not allow us to disable focus for items due bug: #1514822
1337+ //because of that we need this
1338+ if (view.currentIndex == (view.count - 1)) {
1339+ //DO nothing
1340+ } else {
1341+ view.currentIndex += 1
1342+ }
1343+ }
1344 }
1345
1346=== modified file 'src/imports/Ubuntu/Contacts/ContactSimpleListView.qml'
1347--- src/imports/Ubuntu/Contacts/ContactSimpleListView.qml 2015-11-16 22:12:29 +0000
1348+++ src/imports/Ubuntu/Contacts/ContactSimpleListView.qml 2015-12-16 18:38:23 +0000
1349@@ -204,7 +204,8 @@
1350 */
1351 function positionViewAtContact(contact)
1352 {
1353- positionViewAtIndex(getIndex(contact), ListView.Center)
1354+ currentIndex = getIndex(contact)
1355+ positionViewAtIndex(currentIndex, ListView.Center)
1356 }
1357
1358 /*!
1359@@ -213,7 +214,9 @@
1360 */
1361 function _fetchContact(index, contact)
1362 {
1363- contactFetch.fetchContact(contact.contactId)
1364+ if (contact) {
1365+ contactFetch.fetchContact(contact.contactId)
1366+ }
1367 }
1368
1369 function _updateSwipeState(item)
1370@@ -235,7 +238,6 @@
1371 }
1372
1373 highlightFollowsCurrentItem: true
1374- currentIndex: -1
1375 section {
1376 property: showSections ? "contact.tag.tag" : ""
1377 criteria: ViewSection.FirstCharacter
1378@@ -255,7 +257,6 @@
1379 dirtyModel.restart()
1380 }
1381
1382- onFlickStarted: view.currentIndex = -1
1383 listDelegate: ContactDelegate {
1384 id: contactDelegate
1385
1386@@ -270,6 +271,8 @@
1387 width: parent.width
1388 selected: (contactListView.multiSelectionEnabled && contactListView.isSelected(contactDelegate))
1389 || (contactListView.highlightSelected && (contactListView.currentIndex == index))
1390+ selectedColor: contactListView.parent.activeFocus && !contactListView.isInSelectionMode ? UbuntuColors.orange :
1391+ Theme.palette.selected.background
1392 selectionMode: contactListView.isInSelectionMode
1393 defaultAvatarUrl: contactListView.defaultAvatarImageUrl
1394 isCurrentItem: ListView.isCurrentItem
1395@@ -371,13 +374,4 @@
1396 property int pendingTargetIndex: 0
1397 property variant pendingTargetMode: null
1398 }
1399-
1400- Connections {
1401- target: Qt.application
1402- onActiveChanged: {
1403- if (!Qt.application.active) {
1404- currentIndex = -1
1405- }
1406- }
1407- }
1408 }
1409
1410=== modified file 'src/imports/Ubuntu/Contacts/FastScroll.qml'
1411--- src/imports/Ubuntu/Contacts/FastScroll.qml 2015-10-26 13:18:11 +0000
1412+++ src/imports/Ubuntu/Contacts/FastScroll.qml 2015-12-16 18:38:23 +0000
1413@@ -71,19 +71,11 @@
1414 }
1415 }
1416
1417- Connections {
1418- target: listView
1419- onCurrentIndexChanged: {
1420- if (currentIndex != -1) {
1421- rail.opacity = 0.0
1422- }
1423- }
1424- }
1425
1426 Rectangle {
1427 id: magnified
1428
1429- color: Theme.palette.normal.overlay
1430+ color: Theme.palette.normal.foreground
1431 radius: height * 0.3
1432 height: pinSize * 2
1433 width: height
1434@@ -99,6 +91,7 @@
1435 }
1436
1437 Label {
1438+ color: Theme.palette.normal.foregroundText
1439 anchors.fill: parent
1440 horizontalAlignment: Text.AlignHCenter
1441 verticalAlignment: Text.AlignVCenter
1442@@ -141,8 +134,7 @@
1443 id: rail
1444
1445 property bool isVisible: root.enabled &&
1446- (listView.flicking || dragArea.pressed) &&
1447- (listView.currentIndex == -1)
1448+ (listView.flicking || dragArea.pressed)
1449 anchors {
1450 right: parent.right
1451 rightMargin: units.gu(2)
1452@@ -181,7 +173,7 @@
1453 horizontalAlignment: Text.AlignHCenter
1454 text: modelData
1455 fontSize: "x-small"
1456- color: cursor.y === y ? "white" : Theme.palette.selected.backgroundText
1457+ color: cursor.y === y ? Theme.palette.normal.foregroundText : Theme.palette.selected.backgroundText
1458 opacity: !internal.modelDirty && Sections.contains(text) ? 1.0 : 0.5
1459 }
1460 }

Subscribers

People subscribed via source and target branches