[Merge] lp:~renatofilho/address-book-app/new-visual-contact-editor into lp:~phablet-team/address-book-app/staging
Leo Arias
leo.arias at canonical.com
Tue Jun 17 17:59:58 UTC 2014
Diff comments:
> === modified file 'src/app/imagescalethread.cpp'
> --- src/app/imagescalethread.cpp 2013-10-15 14:34:03 +0000
> +++ src/app/imagescalethread.cpp 2014-06-17 00:44:58 +0000
> @@ -57,7 +57,7 @@
> }
>
> // Create the temporary file
> - m_tmpFile = new QTemporaryFile();
> + m_tmpFile = new QTemporaryFile("avatar_XXXXXX.png");
> if (!m_tmpFile->open()) {
> return;
> }
>
> === modified file 'src/imports/Common/ContactDetailItem.qml'
> --- src/imports/Common/ContactDetailItem.qml 2014-05-16 00:06:46 +0000
> +++ src/imports/Common/ContactDetailItem.qml 2014-06-17 00:44:58 +0000
> @@ -23,6 +23,7 @@
>
> readonly property alias fieldDelegates: fieldsColumn.children
> property Component fieldDelegate: null
> + property alias spacing: fieldsColumn.spacing
>
> implicitHeight: fieldsColumn.height
> Column {
>
> === added file 'src/imports/ContactEdit/AddFieldDialog.qml'
> --- src/imports/ContactEdit/AddFieldDialog.qml 1970-01-01 00:00:00 +0000
> +++ src/imports/ContactEdit/AddFieldDialog.qml 2014-06-17 00:44:58 +0000
> @@ -0,0 +1,142 @@
> +/*
> + * Copyright (C) 2012-2014 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.2
> +import Ubuntu.Components 0.1
> +import QtContacts 5.0
> +import Ubuntu.Components.Popups 0.1 as Popups
> +
> +
> +Item {
> + id: root
> +
> + property QtObject contact: null
> + property var currentDialog: null
> + readonly property var validDetails: [ ContactDetail.PhoneNumber,
> + ContactDetail.Email,
> + ContactDetail.Address,
> + ContactDetail.OnlineAccount,
> + ContactDetail.Organization
> + // TODO: Not supported yet
> + // ContactDetail.Birthday,
> + // ContactDetail.Note,
> + // ContactDetail.Url
> + ]
> + readonly property var singleValueDetails: [ ContactDetail.Organization ]
> + signal fieldSelected(string fieldName, string qmlTypeName)
> +
> + function showOptions()
> + {
> + if (currentDialog == null) {
> + // make sure the OSK disappear
> + root.forceActiveFocus()
> + currentDialog = PopupUtils.open(addFieldDialog, null)
> + }
> + }
> +
> + function nameFromEnum(value)
> + {
> + switch (value)
> + {
> + case ContactDetail.PhoneNumber:
> + return i18n.tr("Phone")
> + case ContactDetail.Email:
> + return i18n.tr("Email")
> + case ContactDetail.Address:
> + return i18n.tr("Address")
> + case ContactDetail.OnlineAccount:
> + return i18n.tr("Social")
> + case ContactDetail.Organization:
> + return i18n.tr("Profissional Details")
This is a typo. It should be Professional.
> + default:
> + console.error("Invalid contact detail enum value:" + value)
> + return ""
> + }
> + }
> +
> + function qmlTypeFromEnum(value)
> + {
> + switch (value)
> + {
> + case ContactDetail.PhoneNumber:
> + return "PhoneNumber"
> + case ContactDetail.Email:
> + return "EmailAddress"
> + case ContactDetail.Address:
> + return "Address"
> + case ContactDetail.OnlineAccount:
> + return "OnlineAccount"
> + case ContactDetail.Organization:
> + return "Organization"
> + default:
> + console.error("Invalid contact detail enum value:" + value)
> + return ""
> + }
> + }
> +
> + // check which details will be allowed to create
> + // some details we only support one value
> + function filterSingleDetails(details, contact)
> + {
> + var result = []
> + for(var i=0; i < details.length; i++) {
> + var det = details[i]
> + if (singleValueDetails.indexOf(det) != -1) {
> + if (contact.details(det).length === 0) {
> + result.push(det)
> + }
> + } else {
> + result.push(det)
> + }
> + }
> + return result
> + }
> +
> + visible: false
> + Component {
> + id: addFieldDialog
> +
> + Popups.Dialog {
> + id: dialogue
> + objectName: "addFieldDialog"
> +
> + title: i18n.tr("Select a field")
> + Repeater {
> + model: root.filterSingleDetails(validDetails, root.contact)
> + Button {
> + objectName: text
> +
> + text: root.nameFromEnum(modelData)
> + onClicked: {
> + root.fieldSelected(text, root.qmlTypeFromEnum(modelData))
> + PopupUtils.close(root.currentDialog)
> + root.currentDialog = null
> + }
> + }
> + }
> + Button {
> + objectName: "cancel"
> +
> + text: i18n.tr("Cancel")
> + gradient: UbuntuColors.greyGradient
> + onClicked: {
> + PopupUtils.close(root.currentDialog)
> + root.currentDialog = null
> + }
> + }
> + }
> + }
> +}
>
> === added file 'src/imports/ContactEdit/AvatarImport.qml'
> --- src/imports/ContactEdit/AvatarImport.qml 1970-01-01 00:00:00 +0000
> +++ src/imports/ContactEdit/AvatarImport.qml 2014-06-17 00:44:58 +0000
> @@ -0,0 +1,78 @@
> +/*
> + * Copyright (C) 2012-2014 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.2
> +import Ubuntu.Components 0.1
> +import Ubuntu.Components.Popups 0.1 as Popups
> +import Ubuntu.Content 0.1 as ContentHub
> +
> +Item {
> + id: root
> +
> + property var activeTransfer: null
> + property var loadingDialog: null
> +
> + signal avatarReceived(string avatarUrl)
> +
> + function requestNewAvatar()
> + {
> + if (!root.loadingDialog) {
> + root.loadingDialog = PopupUtils.open(loadingDialog, null)
> + root.activeTransfer = defaultSource.request();
> + }
> + }
> +
> + ContentHub.ContentPeer {
> + id: defaultSource
> +
> + contentType: ContentHub.ContentType.Pictures
> + handler: ContentHub.ContentHandler.Source
> + selectionType: ContentHub.ContentTransfer.Single
> + }
> +
> + Connections {
> + target: root.activeTransfer
> + onStateChanged: {
> + var done = ((root.activeTransfer.state === ContentHub.ContentTransfer.Charged) ||
> + (root.activeTransfer.state === ContentHub.ContentTransfer.Aborted));
> +
> + if (root.activeTransfer.state === ContentHub.ContentTransfer.Charged) {
> + if (root.activeTransfer.items.length > 0) {
> + root.avatarReceived(root.activeTransfer.items[0].url)
> + }
> + }
> +
> + if (done) {
> + PopupUtils.close(root.loadingDialog)
> + root.loadingDialog = null
> + }
> + }
> + }
> +
> + Component {
> + id: loadingDialog
> +
> + Popups.Dialog {
> + id: dialogue
> +
> + title: i18n.tr("Loading")
> + ActivityIndicator {
> + running: true
> + visible: running
> + }
> + }
> + }
> +}
>
> === modified file 'src/imports/ContactEdit/CMakeLists.txt'
> --- src/imports/ContactEdit/CMakeLists.txt 2014-06-09 22:45:19 +0000
> +++ src/imports/ContactEdit/CMakeLists.txt 2014-06-17 00:44:58 +0000
> @@ -1,4 +1,6 @@
> set(CONTACT_EDIT_QMLS
> + AvatarImport.qml
> + AddFieldDialog.qml
> ContactDetailAddressesEditor.qml
> ContactDetailAvatarEditor.qml
> ContactDetailEmailsEditor.qml
>
> === modified file 'src/imports/ContactEdit/ContactDetailAvatarEditor.qml'
> --- src/imports/ContactEdit/ContactDetailAvatarEditor.qml 2014-05-06 13:18:07 +0000
> +++ src/imports/ContactEdit/ContactDetailAvatarEditor.qml 2014-06-17 00:44:58 +0000
> @@ -17,15 +17,13 @@
> import QtQuick 2.2
> import Ubuntu.Components 0.1
> import QtContacts 5.0
> -import Ubuntu.Content 0.1
> -import Ubuntu.Components.Popups 0.1 as Popups
>
> import "../Common"
>
> ContactDetailBase {
> id: root
>
> - readonly property string defaultAvatar: Qt.resolvedUrl("../../artwork/contact-default-profile.png")
> + readonly property string defaultAvatar: "image://theme/contact"
>
> function isEmpty() {
> return false;
> @@ -62,98 +60,51 @@
> }
>
> detail: contact ? contact.detail(ContactDetail.Avatar) : null
> - implicitHeight: units.gu(17)
> -
> - Image {
> - id: avatarImage
> -
> - anchors.fill: parent
> - source: root.getAvatar(root.detail)
> - asynchronous: true
> - fillMode: Image.PreserveAspectCrop
> - // When updating the avatar using the content picker the temporary file returned
> - // can contain the same name as the previous one and if the cache is enabled this
> - // will cause the image to not be updated
> - cache: false
> -
> - Component {
> - id: loadingDialog
> -
> - Popups.Dialog {
> - id: dialogue
> -
> - title: i18n.tr("Loading")
> -
> - ActivityIndicator {
> - id: activity
> -
> - anchors.centerIn: parent
> - running: true
> - visible: running
> - }
> - }
> - }
> -
> - Icon {
> - anchors {
> - right: parent.right
> - rightMargin: units.gu(1.5)
> - bottom: parent.bottom
> - bottomMargin: units.gu(2)
> - }
> - width: units.gu(3)
> - height: width
> - name: "import-image"
> - color: "white"
> - }
> -
> - ContentPeer {
> - id: defaultSource
> - contentType: ContentType.Pictures
> - handler: ContentHandler.Source
> - selectionType: ContentTransfer.Single
> - }
> -
> - MouseArea {
> - id: changeButton
> -
> - property var activeTransfer
> - property var loadingDialog: null
> -
> - anchors.fill: parent
> - onClicked: {
> - // make sure the OSK disappear
> - root.forceActiveFocus()
> - if (!changeButton.loadingDialog) {
> - changeButton.loadingDialog = PopupUtils.open(loadingDialog, null)
> - changeButton.activeTransfer = defaultSource.request();
> - }
> - }
> -
> - Connections {
> - target: changeButton.activeTransfer != null ? changeButton.activeTransfer : null
> - onStateChanged: {
> - var done = ((changeButton.activeTransfer.state === ContentTransfer.Charged) ||
> - (changeButton.activeTransfer.state === ContentTransfer.Aborted));
> -
> - if (changeButton.activeTransfer.state === ContentTransfer.Charged) {
> - if (changeButton.activeTransfer.items.length > 0) {
> - // remove the previous image, this is nessary to make sure that the new image
> - // get updated otherwise if the new image has the same name the image will not
> - // be updated
> - avatarImage.source = ""
> - // Update with the new valu
> - avatarImage.source = application.copyImage(root.contact, changeButton.activeTransfer.items[0].url);
> - }
> - }
> -
> - if (done) {
> - PopupUtils.close(changeButton.loadingDialog)
> - changeButton.loadingDialog = null
> - }
> - }
> - }
> + implicitHeight: units.gu(8)
> + implicitWidth: units.gu(8)
> +
> + UbuntuShape {
> + id: avatar
> +
> + radius: "medium"
> + anchors.fill: parent
> + image: Image {
> + id: avatarImage
> +
> + fillMode: Image.PreserveAspectCrop
> + asynchronous: true
> + source: root.getAvatar(root.detail)
> + height: units.gu(8)
> + width: units.gu(8)
> +
> + // When updating the avatar using the content picker the temporary file returned
> + // can contain the same name as the previous one and if the cache is enabled this
> + // will cause the image to not be updated
> + cache: false
> + }
> + }
> +
> + AvatarImport {
> + id: avatarImport
> +
> + onAvatarReceived: {
> + // remove the previous image, this is nessary to make sure that the new image
> + // get updated otherwise if the new image has the same name the image will not
> + // be updated
> + avatarImage.source = ""
> + // Update with the new value
> + avatarImage.source = application.copyImage(root.contact, avatarUrl);
> + }
> + }
> +
> + MouseArea {
> + anchors.fill: parent
> + onClicked: {
> + // make sure the OSK disappear
> + root.forceActiveFocus()
> + avatarImport.requestNewAvatar()
> }
> }
> }
>
> +
>
> === modified file 'src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml'
> --- src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml 2014-05-22 06:56:03 +0000
> +++ src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml 2014-06-17 00:44:58 +0000
> @@ -95,7 +95,7 @@
>
> return changed
> }
> - minimumHeight: units.gu(5)
> +
> headerDelegate: ListItem.Empty {
> id: header
> highlightWhenPressed: false
> @@ -117,35 +117,6 @@
> // style
> fontSize: "medium"
> }
> -
> - Icon {
> - objectName: "newDetailButton"
> -
> - anchors {
> - verticalCenter: parent.verticalCenter
> - right: parent.right
> - rightMargin: units.gu(2)
> - }
> - width: units.gu(2)
> - height: units.gu(2)
> - name: "add"
> - }
> -
> - // Mouse area fill all title area to avoid problems with swipe from the right gesture
> - MouseArea {
> - anchors.fill: parent
> - onClicked: {
> - if (detailQmlTypeName) {
> - var newDetail = Qt.createQmlObject("import QtContacts 5.0; " + detailQmlTypeName + "{}", root)
> - if (newDetail) {
> - var newDetailsCopy = root.newDetails
> - newDetailsCopy.push(newDetail)
> - root.newDetails = newDetailsCopy
> - root.contact.addDetail(newDetail)
> - }
> - }
> - }
> - }
> }
>
> detailDelegate: ContactDetailWithTypeEditor {
>
> === modified file 'src/imports/ContactEdit/ContactDetailNameEditor.qml'
> --- src/imports/ContactEdit/ContactDetailNameEditor.qml 2014-05-09 20:00:09 +0000
> +++ src/imports/ContactEdit/ContactDetailNameEditor.qml 2014-06-17 00:44:58 +0000
> @@ -51,6 +51,7 @@
> return changed
> }
>
> + spacing: units.gu(1)
> detail: root.contact ? root.contact.name : null
> fields: [ QtContacts.Name.FirstName, QtContacts.Name.LastName ]
>
>
> === modified file 'src/imports/ContactEdit/ContactDetailSyncTargetEditor.qml'
> --- src/imports/ContactEdit/ContactDetailSyncTargetEditor.qml 2014-05-28 15:22:38 +0000
> +++ src/imports/ContactEdit/ContactDetailSyncTargetEditor.qml 2014-06-17 00:44:58 +0000
> @@ -84,6 +84,12 @@
> height: units.gu(4)
> }
>
> + ListItem.ThinDivider {
> + id: divider
> +
> + anchors.top: label.bottom
> + }
> +
> OptionSelector {
> id: sources
>
> @@ -91,8 +97,8 @@
> anchors {
> left: parent.left
> leftMargin: units.gu(2)
> - top: label.bottom
> - topMargin: units.gu(1)
> + top: divider.bottom
> + topMargin: units.gu(2)
> right: parent.right
> rightMargin: units.gu(2)
> bottom: parent.bottom
>
> === modified file 'src/imports/ContactEdit/ContactDetailWithTypeEditor.qml'
> --- src/imports/ContactEdit/ContactDetailWithTypeEditor.qml 2014-05-16 01:59:36 +0000
> +++ src/imports/ContactEdit/ContactDetailWithTypeEditor.qml 2014-06-17 00:44:58 +0000
> @@ -73,13 +73,10 @@
> enabled: root.detail ? !root.detail.readOnly : false
> implicitHeight: detailTypeSelector.height + fieldValues.height + units.gu(2)
> opacity: enabled ? 1.0 : 0.5
> - ValueSelector {
> - id: detailTypeSelector
> - objectName: detail ? "type_" + detailToString(detail.type, -1) + "_" + index : ""
> -
> - readOnly: root.detail ? root.detail.readOnly : false
> - visible: (currentIndex != -1)
> - active: root.active
> +
> + Column {
> + id: fieldValues
> +
> anchors {
> left: parent.left
> leftMargin: units.gu(3)
> @@ -88,26 +85,7 @@
> top: parent.top
> topMargin: units.gu(1)
> }
> -
> - height: visible ? (root.active ? units.gu(4) : units.gu(3)) : 0
> - onExpandedChanged: {
> - // Make sure that the inputfield get focus when clicking on type selector
> - if (expanded) {
> - root.forceActiveFocus()
> - }
> - }
> - }
> -
> - Column {
> - id: fieldValues
> -
> - anchors {
> - left: detailTypeSelector.left
> - right: detailTypeSelector.right
> - top: detailTypeSelector.bottom
> - }
> height: childrenRect.height
> -
> Repeater {
> id: fieldRepeater
>
> @@ -141,4 +119,26 @@
> }
> }
> }
> +
> + ValueSelector {
> + id: detailTypeSelector
> + objectName: detail ? "type_" + detailToString(detail.type, -1) + "_" + index : ""
> +
> + anchors {
> + left: fieldValues.left
> + right: fieldValues.right
> + top: fieldValues.bottom
> + }
> +
> + readOnly: root.detail ? root.detail.readOnly : false
> + visible: (currentIndex != -1)
> + active: root.active
> + height: visible ? (root.active ? units.gu(4) : units.gu(3)) : 0
> + onExpandedChanged: {
> + // Make sure that the inputfield get focus when clicking on type selector
> + if (expanded) {
> + root.forceActiveFocus()
> + }
> + }
> + }
> }
>
> === modified file 'src/imports/ContactEdit/ContactEditor.qml'
> --- src/imports/ContactEdit/ContactEditor.qml 2014-06-16 12:59:15 +0000
> +++ src/imports/ContactEdit/ContactEditor.qml 2014-06-17 00:44:58 +0000
> @@ -161,7 +161,7 @@
> fill: parent
> bottomMargin: keyboard.height
> }
> - contentHeight: contents.height
> + contentHeight: contents.height + units.gu(2)
> contentWidth: parent.width
>
> //after add a new field we need to wait for the contentHeight to change to scroll to the correct position
> @@ -172,32 +172,48 @@
>
> anchors {
> top: parent.top
> + topMargin: units.gu(2)
> left: parent.left
> right: parent.right
> }
> height: childrenRect.height
>
> - ContactDetailNameEditor {
> - id: nameEditor
> -
> -
> - anchors {
> - left: parent.left
> - right: parent.right
> - }
> - height: nameEditor.implicitHeight + units.gu(3)
> - contact: contactEditor.contact
> - }
> -
> - ContactDetailAvatarEditor {
> - id: avatarEditor
> -
> - contact: contactEditor.contact
> - anchors {
> - left: parent.left
> - right: parent.right
> - }
> - height: implicitHeight
> + Row {
> + function save()
> + {
> + var avatarSave = avatarEditor.save()
> + var nameSave = nameEditor.save();
> +
> + return (nameSave || avatarSave);
> + }
> +
> + function isEmpty()
> + {
> + return (avatarEditor.isEmpty() && nameEditor.isEmpty())
> + }
> +
> + anchors {
> + left: parent.left
> + leftMargin: units.gu(2)
> + right: parent.right
> + }
> + height: Math.max(avatarEditor.height, nameEditor.height)
> +
> + ContactDetailAvatarEditor {
> + id: avatarEditor
> +
> + contact: contactEditor.contact
> + height: implicitHeight
> + width: implicitWidth
> + }
> +
> + ContactDetailNameEditor {
> + id: nameEditor
> +
> + width: parent.width - avatarEditor.width
> + height: nameEditor.implicitHeight + units.gu(3)
> + contact: contactEditor.contact
> + }
> }
>
> ContactDetailPhoneNumbersEditor {
> @@ -272,13 +288,48 @@
> height: implicitHeight
> }
>
> - // We need this extra element to correct align the deleteButton
> + ListItem.ThinDivider {}
> +
> Item {
> anchors {
> left: parent.left
> right: parent.right
> }
> - height: deleteButton.height + units.gu(2)
> + height: units.gu(2)
> + }
> +
> + Row {
> + anchors {
> + left: parent.left
> + right: parent.right
> + margins: units.gu(2)
> + }
> + height: units.gu(6)
> + spacing: units.gu(2)
> +
> + // WORKAROUND: SDK uses a old version of qtquick components
> + activeFocusOnTab: true
> + onActiveFocusChanged: {
> + if (activeFocus) {
> + addNewFieldButton.forceActiveFocus()
> + }
> + }
> +
> + Button {
> + id: addNewFieldButton
> + objectName: "addNewFieldButton"
> +
> + text: i18n.tr("Add Field")
> + gradient: UbuntuColors.greyGradient
> + anchors {
> + top: parent.top
> + bottom: parent.bottom
> + bottomMargin: units.gu(2)
> + }
> + width: (parent.width - units.gu(4)) / 2
> +
> + onClicked: addFieldDialog.showOptions()
> + }
>
> Button {
> id: deleteButton
> @@ -286,12 +337,11 @@
> text: i18n.tr("Delete")
> visible: !contactEditor.isNewContact
> anchors {
> - margins: units.gu(2)
> top: parent.top
> - left: parent.left
> - right: parent.right
> + bottom: parent.bottom
> + bottomMargin: units.gu(2)
> }
> -
> + width: (parent.width - units.gu(4)) / 2
> onClicked: {
> var dialog = Popups.PopupUtils.open(removeContactDialog, null)
> dialog.contacts = [contactEditor.contact]
> @@ -351,6 +401,23 @@
> }
> }
>
> + AddFieldDialog {
> + id: addFieldDialog
> +
> + contact: contactEditor.contact
> + onFieldSelected: {
> + if (qmlTypeName) {
> + var newDetail = Qt.createQmlObject("import QtContacts 5.0; " + qmlTypeName + "{}", addFieldDialog)
> + if (newDetail) {
> + var newDetailsCopy = contactEditor.newDetails
> + newDetailsCopy.push(newDetail)
> + contactEditor.newDetails = newDetailsCopy
> + contactEditor.contact.addDetail(newDetail)
> + }
> + }
> + }
> + }
> +
> Component {
> id: removeContactDialog
>
>
> === modified file 'src/imports/ContactEdit/TextInputDetail.qml'
> --- src/imports/ContactEdit/TextInputDetail.qml 2014-05-28 15:19:53 +0000
> +++ src/imports/ContactEdit/TextInputDetail.qml 2014-06-17 00:44:58 +0000
> @@ -80,6 +80,7 @@
> overlaySpacing: 0
> frameSpacing: 0
> background: Item {}
> + color: UbuntuColors.lightAubergine
> }
> onActiveFocusChanged: {
> if (activeFocus) {
>
> === modified file 'src/imports/ContactList/ContactListPage.qml'
> --- src/imports/ContactList/ContactListPage.qml 2014-06-16 12:59:54 +0000
> +++ src/imports/ContactList/ContactListPage.qml 2014-06-17 00:44:58 +0000
> @@ -43,10 +43,7 @@
> function createEmptyContact(phoneNumber) {
> var details = [ {detail: "PhoneNumber", field: "number", value: phoneNumber},
> {detail: "EmailAddress", field: "emailAddress", value: ""},
> - {detail: "OnlineAccount", field: "accountUri", value: ""},
> - {detail: "Address", field: "street", value: ""},
> - {detail: "Name", field: "firstName", value: ""},
> - {detail: "Organization", field: "name", value: ""}
> + {detail: "Name", field: "firstName", value: ""}
> ]
>
> var newContact = Qt.createQmlObject("import QtContacts 5.0; Contact{ }", mainPage)
>
> === modified file 'src/imports/ContactView/BasicFieldView.qml'
> --- src/imports/ContactView/BasicFieldView.qml 2014-06-17 00:44:58 +0000
> +++ src/imports/ContactView/BasicFieldView.qml 2014-06-17 00:44:58 +0000
> @@ -50,8 +50,8 @@
> objectName: detail && fields ? "label_" + detailToString(detail.type, fields[index]) + "_" + root.parentIndex + "." + index : ""
>
> anchors {
> - left: parent ? parent.left : null
> - right: parent ? parent.right : null
> + left: parent ? parent.left : undefined
> + right: parent ? parent.right : undefined
> }
> height: root.lineHeight
> verticalAlignment: Text.AlignVCenter
>
> === modified file 'src/imports/ContactView/ContactDetailAvatarView.qml'
> --- src/imports/ContactView/ContactDetailAvatarView.qml 2014-06-17 00:44:58 +0000
> +++ src/imports/ContactView/ContactDetailAvatarView.qml 2014-06-17 00:44:58 +0000
> @@ -24,9 +24,14 @@
> ContactDetailBase {
> id: root
>
> - implicitHeight: units.gu(10)
> + implicitHeight: units.gu(8)
> implicitWidth: units.gu(10)
>
> + Connections {
> + target: root.contact.avatar
> + onDetailChanged: avatar.reload()
> + }
> +
> ContactsUI.ContactAvatar {
> id: avatar
>
> @@ -34,7 +39,6 @@
> anchors {
> fill: parent
> leftMargin: units.gu(2)
> - topMargin: units.gu(2)
> }
> }
> }
>
> === modified file 'src/imports/ContactView/ContactView.qml'
> --- src/imports/ContactView/ContactView.qml 2014-06-17 00:44:58 +0000
> +++ src/imports/ContactView/ContactView.qml 2014-06-17 00:44:58 +0000
> @@ -67,7 +67,7 @@
> anchors.fill: parent
> //WORKAROUND: There is a bug on SDK page that causes the page to appear flicked with small contents
> // see bug #1223050
> - contentHeight: Math.max(contents.height, parent.height)
> + contentHeight: Math.max(contents.height, parent.height) + units.gu(2)
> contentWidth: parent.width
> visible: !busyIndicator.visible
>
> @@ -77,6 +77,7 @@
> height: childrenRect.height
> anchors {
> top: parent.top
> + topMargin: units.gu(2)
> left: parent.left
> right: parent.right
> }
>
> === modified file 'src/imports/Ubuntu/Contacts/ContactAvatar.qml'
> --- src/imports/Ubuntu/Contacts/ContactAvatar.qml 2014-06-06 17:52:58 +0000
> +++ src/imports/Ubuntu/Contacts/ContactAvatar.qml 2014-06-17 00:44:58 +0000
> @@ -24,7 +24,12 @@
>
> property var contactElement: null
> property string displayName: ContactsJS.formatToDisplay(contactElement, ContactDetail.Name, [Name.FirstName, Name.LastName])
> - readonly property string avatarUrl: ContactsJS.getAvatar(contactElement, "")
> + property string avatarUrl: ContactsJS.getAvatar(contactElement, "")
> +
> + function reload()
> + {
> + avatarUrl = ContactsJS.getAvatar(contactElement, "")
> + }
>
> radius: "medium"
> color: Theme.palette.normal.overlay
> @@ -34,13 +39,16 @@
> text: ContactsJS.getNameItials(displayName)
> font.pointSize: 88
> color: UbuntuColors.lightAubergine
> - visible: avatarUrl === ""
> + visible: (img.status != Image.Ready)
> }
>
> image: Image {
> + id: img
> +
> fillMode: Image.PreserveAspectCrop
> asynchronous: true
> source: avatarUrl
> - visible: source !== ""
> + height: avatar.height
> + width: avatar.width
> }
> }
>
> === modified file 'tests/autopilot/address_book_app/__init__.py'
> --- tests/autopilot/address_book_app/__init__.py 2014-06-06 17:30:28 +0000
> +++ tests/autopilot/address_book_app/__init__.py 2014-06-17 00:44:58 +0000
> @@ -65,6 +65,7 @@
> pages.ContactEditor, objectName="contactEditorPage")
> for p in contact_editor_pages:
> if p.active:
> + p.set_main_window(self)
> return p
> raise dbus.StateNotFoundError('contactEditorPage not found')
> return None
>
> === modified file 'tests/autopilot/address_book_app/pages/_contact_editor.py'
> --- tests/autopilot/address_book_app/pages/_contact_editor.py 2014-05-22 12:50:28 +0000
> +++ tests/autopilot/address_book_app/pages/_contact_editor.py 2014-06-17 00:44:58 +0000
> @@ -51,6 +51,37 @@
> class ContactEditor(_common.PageWithHeader):
> """Custom proxy object for the Contact Editor."""
>
> + _DETAIL_ALIAS = {
> + 'phones': 'Phone',
> + 'emails': 'Email',
> + 'ims': 'Social',
> + 'addresses': 'Address',
> + 'professionalDetails': 'Profissional Details'
> + }
> +
> + def set_main_window(self, main_window):
> + self._main_window = main_window
> +
> + def add_detail(self, detail_name, main_window = None):
As we are following closely the UI, it's easier to understand from the log if the methods are named after the buttons they click. add_field is a better name.
Also log when the test calls this method with the decorator:
@autopilot.logging.log_action(logger.info)
> + """Create a new field into the edit contact form.
> +
> + :param detail_name: The detail field name
> +
> + """
> +
> + btn = self.select_single("Button", objectName="addNewFieldButton")
For consistency, we are trying to use only single quotes on strings.
> + self._make_visible(btn)
_make_visible is cheating, because on the phone we won't be able to click TAB to make the field focused.
We can call btn.swipe_into_view()
> + self.pointing_device.click_object(btn)
> +
> + if not main_window:
> + main_window = self._main_window
> +
> + dlg = main_window.wait_select_single("Dialog", objectName="addFieldDialog")
This line is longer than 80 chars. That goes against pep8, that's the style guide to follow when writing python.
And here we don't need to call the main window. We can do self.get_root_instance().wait_select_single(...)
That will remove the need to pass main_window as parameter and to have it as an attribute.
> + new_field_btn = dlg.select_single("Button",
> + objectName=self._DETAIL_ALIAS[detail_name])
I like this. But wouldn't it be better to build the objectName of the button from the objectName of the detail?
Something like objectName='addFieldButton-{}'.format(detail_name). That of course will require to change the QML, so I'm not sure if it's better.
> +
> + self.pointing_device.click_object(new_field_btn)
> +
> @autopilot.logging.log_action(logger.info)
> def fill_form(self, contact_information):
> """Fill the edit contact form.
> @@ -90,7 +121,7 @@
> def _fill_detail_group(self, object_name, details):
> editor = self.select_single(
> ContactDetailGroupWithTypeEditor, objectName=object_name)
> - editor.fill(details)
> + editor.fill(self, details)
>
> def _get_form_values(self):
> first_name = _get_text_field(self, 'first_name').text
> @@ -112,6 +143,20 @@
> ContactDetailGroupWithTypeEditor, objectName=object_name)
> return editor.get_values(object_name)
>
> + def _get_keyboard(self):
> + return _get_text_field(self, 'first_name').keyboard
> +
> + def _make_visible(self, item):
> + keyboard = self._get_keyboard()
> +
> + while not item.activeFocus:
> + # XXX We should just swipe the text field into view.
> + # Update this once bug http://pad.lv/1286479 is implemented.
> + # --elopio - 2014-03-01
> + keyboard.press_and_release('Tab')
> + time.sleep(0.1)
> + self.wait_to_stop_moving()
> +
> def wait_to_stop_moving(self):
> flickable = self.select_single(
> 'QQuickFlickable', objectName='scrollArea')
> @@ -135,12 +180,12 @@
> 'professionalDetails': 'base_unknown_{}'
> }
>
> - def fill(self, details):
> + def fill(self, editor, details):
> """Fill a contact detail group."""
> - for index, detail in enumerate(details[:-1]):
> + for index, detail in enumerate(details):
> + if self.detailsCount <= index:
> + editor.add_detail(self.objectName)
> self._fill_detail(index, detail)
> - self._add_detail()
> - self._fill_detail(len(details) - 1, details[-1])
>
> def _fill_detail(self, index, detail):
> detail_editor = self._get_detail_editor_by_index(index)
> @@ -175,15 +220,20 @@
> """Custom proxy object for the ContactDetailWithTypeEditor widget."""
>
> def fill(self, field, index, detail):
> + self._fill_value(field, index, detail)
> self._select_type(detail)
> - self._fill_value(field, index, detail)
>
> def _select_type(self, detail):
> + # get keyboard
This one is harder to do with the mouse, so for now it's ok to do it with the keyboard. But it's better to get the keyboard from the toolkit helpers than from the contact editor.
ubuntuuitoolkit.get_keyboard()
> + contact_editor = self.get_root_instance().select_single(
> + ContactEditor, objectName='contactEditorPage', active=True)
> + keyboard = contact_editor._get_keyboard()
> type_index = detail.TYPES.index(detail.type)
> - selected_type_index = self._get_selected_type_index()
> - if type_index != selected_type_index:
> - # TODO --elopio - 2014-03-01
> - raise NotImplementedError('Type selection not yet implemented.')
> + value_selector = self.select_single('ValueSelector')
> +
> + while(value_selector.currentIndex != type_index):
> + keyboard.press_and_release("Shift+Right")
> + time.sleep(0.1)
>
> def _get_selected_type_index(self):
> value_selector = self.select_single('ValueSelector')
> @@ -210,16 +260,10 @@
> self._make_field_visible_and_write(text_field, value)
>
> def _make_field_visible_and_write(self, text_field, value):
> - while not text_field.activeFocus:
> - # XXX We should just swipe the text field into view.
> - # Update this once bug http://pad.lv/1286479 is implemented.
> - # --elopio - 2014-03-01
> - text_field.keyboard.press_and_release('Tab')
> - time.sleep(0.1)
> - contact_editor = self.get_root_instance().select_single(
> - ContactEditor, objectName='contactEditorPage', active=True)
> - contact_editor.wait_to_stop_moving()
> + contact_editor = self.get_root_instance().select_single(
> + ContactEditor, objectName='contactEditorPage', active=True)
>
> + contact_editor._make_visible(text_field)
> text_field.write(value)
>
> def _fill_address(self, index, address):
>
> === modified file 'tests/autopilot/address_book_app/tests/__init__.py'
> --- tests/autopilot/address_book_app/tests/__init__.py 2014-06-17 00:44:58 +0000
> +++ tests/autopilot/address_book_app/tests/__init__.py 2014-06-17 00:44:58 +0000
> @@ -143,6 +143,7 @@
> self.pointing_device.click_object(clear_button)
> self.assertThat(field.text, Eventually(Equals("")))
>
> + # FIXME: Remove this function use ContactEditor.add_detail
It's a good idea to sign your FIXMEs with your name and the date. -- renato - 2014-06-17.
> def create_new_detail(self, detailGroup):
> detCount = detailGroup.detailsCount
> add_button = detailGroup.select_single("Icon",
> @@ -155,14 +156,13 @@
> list_page = self.main_window.get_contact_list_page()
> list_page.open_contact(index)
>
> - list_page = self.main_window.get_contact_list_page()
> self.assertThat(list_page.visible, Eventually(Equals(False)))
> -
> view_page = self.main_window.get_contact_view_page()
> self.assertThat(view_page.visible, Eventually(Equals(True)))
>
> # Edit contact
> edit_page = view_page.go_to_edit_contact()
> + edit_page.set_main_window(self.main_window)
> self.assertThat(edit_page.visible, Eventually(Equals(True)))
>
> return edit_page
>
> === modified file 'tests/autopilot/address_book_app/tests/test_add_contact.py'
> --- tests/autopilot/address_book_app/tests/test_add_contact.py 2014-06-08 13:51:45 +0000
> +++ tests/autopilot/address_book_app/tests/test_add_contact.py 2014-06-17 00:44:58 +0000
> @@ -173,28 +173,23 @@
> self.assertThat(list_view.count, Eventually(Equals(1)))
>
> def test_email_label_save(self):
> - # execute add new contact
> contact_editor = self.app.main_window.go_to_add_contact()
>
> - # fill name
> - contact_editor.fill_form(
> - data.Contact(first_name='Sherlock', last_name='Holmes'))
> + my_emails = []
> + my_emails.append(data.Email(type_="Home", address="home at email.com"))
> + my_emails.append(data.Email(type_="Work", address="work at email.com"))
> + my_emails.append(data.Email(type_="Other", address="other at email.com"))
>
> - # Home
> - self.set_email_address(0, "home at email.com", 0)
> - # Work
> - self.set_email_address(1, "work at email.com", 1)
> - # Other
> - self.set_email_address(2, "other at email.com", 2)
> + test_contact = data.Contact(first_name="Sherlock",
> + last_name="Holmes",
> + emails=my_emails)
> + contact_editor.fill_form(test_contact)
>
> # Save contact
> self.app.main_window.save()
>
> list_page = self.app.main_window.get_contact_list_page()
> - list_page.open_contact(0)
> -
> - # check if contacts was saved with the correct labels
> - view_page = self.app.main_window.get_contact_view_page()
> + view_page = list_page.open_contact(0)
> self.assertThat(view_page.visible, Eventually(Equals(True)))
>
> # check if we have 3 emails"""
> @@ -223,33 +218,26 @@
> self.assertThat(len(emails), Equals(0))
>
> def test_phone_label_save(self):
> - # execute add new contact
> contact_editor = self.app.main_window.go_to_add_contact()
>
> - # fill name
> - contact_editor.fill_form(
> - data.Contact(first_name='Sherlock', last_name='Holmes'))
> + my_phones = []
> + my_phones.append(data.Phone(type_="Home", number="(000) 000-0000"))
> + my_phones.append(data.Phone(type_="Work", number="(000) 000-0001"))
> + my_phones.append(data.Phone(type_="Mobile", number="(000) 000-0002"))
> + my_phones.append(data.Phone(type_="Work Mobile", number="(000) 000-0003"))
> + my_phones.append(data.Phone(type_="Other", number="(000) 000-0004"))
>
> - # Home
> - self.set_phone_number(0, "(000) 000-0000", 0)
> - # Work
> - self.set_phone_number(1, "(000) 000-0001", 1)
> - # Mobile
> - self.set_phone_number(2, "(000) 000-0002", 2)
> - # Work Mobile
> - self.set_phone_number(3, "(000) 000-0003", 3)
> - # Other
> - self.set_phone_number(4, "(000) 000-0004", 4)
> + test_contact = data.Contact(first_name="Sherlock",
> + last_name="Holmes",
> + phones=my_phones)
> + contact_editor.fill_form(test_contact)
>
> # Save contact
> self.app.main_window.save()
>
> # Open contact view
> list_page = self.app.main_window.get_contact_list_page()
> - list_page.open_contact(0)
> -
> - # check if contacts was saved with the correct labels
> - view_page = self.app.main_window.get_contact_view_page()
> + view_page = list_page.open_contact(0)
> self.assertThat(view_page.visible, Eventually(Equals(True)))
>
> # check if we have five phones"""
>
> === modified file 'tests/autopilot/address_book_app/tests/test_edit_contact.py'
> --- tests/autopilot/address_book_app/tests/test_edit_contact.py 2014-05-30 07:11:42 +0000
> +++ tests/autopilot/address_book_app/tests/test_edit_contact.py 2014-06-17 00:44:58 +0000
> @@ -37,10 +37,7 @@
> edit_page = self.edit_contact(0)
>
> # Add a new phone
> - phoneGroup = edit_page.select_single(
> - "ContactDetailGroupWithTypeEditor",
> - objectName="phones")
> - self.create_new_detail(phoneGroup)
> + edit_page.add_detail("phones")
>
> # fill phone number
> phone_number_1 = self.app.main_window.select_single(
> @@ -67,11 +64,18 @@
> self.assertThat(phone_label_1.text, Eventually(Equals(self.PHONE_NUMBERS[1])))
>
> def test_remove_phone(self):
> - self.add_contact("Fulano", "de Tal", self.PHONE_NUMBERS[1:3])
> - edit_page = self.edit_contact(0)
> + contact_editor = self.app.main_window.go_to_add_contact()
> + my_phones = []
> + for n in self.PHONE_NUMBERS[1:3]:
> + my_phones.append(data.Phone(type_='Mobile', number=n))
> +
> + test_contact = data.Contact(first_name="Fulano",
> + last_name="de Tal",
> + phones=my_phones)
> + contact_editor.fill_form(test_contact)
>
> # clear phone 1
> - phone_number_1 = edit_page.select_single(
> + phone_number_1 = contact_editor.wait_select_single(
> "TextInputDetail",
> objectName="phoneNumber_1")
> self.clear_text_on_field(phone_number_1)
> @@ -79,8 +83,11 @@
> # Save contact
> self.app.main_window.save()
>
> + # Go to contact view
> + list_page = self.main_window.get_contact_list_page()
> +
> # check if we have onlye one phone
> - view_page = self.app.main_window.get_contact_view_page()
> + view_page = list_page.open_contact(0)
> phone_group = view_page.select_single(
> "ContactDetailGroupWithTypeView",
> objectName="phones")
> @@ -95,11 +102,7 @@
> def test_add_email(self):
> self.add_contact("Fulano", "")
> edit_page = self.edit_contact(0)
> -
> - emailGroup = edit_page.select_single(
> - "ContactDetailGroupWithTypeEditor",
> - objectName="emails")
> - self.create_new_detail(emailGroup)
> + edit_page.add_detail("emails")
>
> # fill email address
> email_field = edit_page.select_single(
> @@ -174,7 +177,17 @@
> self.assertThat(view_page.title, Eventually(Equals("Fulano de Tal")))
>
> def test_im_type(self):
> - self.add_contact("Fulano", "de Tal", im_address=["im at account.com"])
> + contact_editor = self.app.main_window.go_to_add_contact()
> + alias = data.SocialAlias(type_="Skype", alias="im at account.com")
> + test_contact = data.Contact(first_name="Fulano",
> + last_name="de Tal",
> + social_aliases=[alias])
> + contact_editor.fill_form(test_contact)
> +
> + # Save contact
> + self.app.main_window.save()
> +
> + # edit again
> edit_page = self.edit_contact(0)
>
> # Change Im type
>
--
https://code.launchpad.net/~renatofilho/address-book-app/new-visual-contact-editor/+merge/223009
Your team Ubuntu Phablet Team is requested to review the proposed merge of lp:~renatofilho/address-book-app/new-visual-contact-editor into lp:~phablet-team/address-book-app/staging.
More information about the Ubuntu-reviews
mailing list