From d308d0a347f59b8e5b1f5873b22af3e8767f93f1 Mon Sep 17 00:00:00 2001 From: Ronan Abhamon Date: Thu, 15 Dec 2016 14:26:50 +0100 Subject: [PATCH] feat(ui/views/App/MainWindow/ContactEdit): supports the add of new contacts --- tests/assets/images/edit_hovered.svg | 13 ++ tests/assets/images/edit_normal.svg | 13 ++ tests/assets/images/edit_pressed.svg | 13 ++ tests/resources.qrc | 3 + tests/src/components/contact/ContactModel.cpp | 14 ++ tests/src/components/contact/ContactModel.hpp | 1 + tests/src/components/contact/VcardModel.cpp | 60 ++++---- tests/src/components/contact/VcardModel.hpp | 5 +- .../components/contacts/ContactsListModel.cpp | 33 ++++- .../components/contacts/ContactsListModel.hpp | 1 + tests/src/components/core/CoreManager.cpp | 9 +- tests/src/components/core/CoreManager.hpp | 8 +- tests/ui/modules/Common/Colors.qml | 6 +- .../Common/Form/AbstractTextButton.qml | 89 ++++++++---- tests/ui/modules/Common/Form/ListForm.qml | 68 +++++---- tests/ui/modules/Common/Form/TextButtonA.qml | 4 +- tests/ui/modules/Common/Form/TextButtonB.qml | 4 +- .../Common/Styles/Form/TextButtonAStyle.qml | 4 +- .../Common/Styles/Form/TextButtonBStyle.qml | 4 +- tests/ui/views/App/MainWindow/ContactEdit.qml | 136 ++++++++++++------ 20 files changed, 325 insertions(+), 163 deletions(-) create mode 100644 tests/assets/images/edit_hovered.svg create mode 100644 tests/assets/images/edit_normal.svg create mode 100644 tests/assets/images/edit_pressed.svg diff --git a/tests/assets/images/edit_hovered.svg b/tests/assets/images/edit_hovered.svg new file mode 100644 index 000000000..573854bd0 --- /dev/null +++ b/tests/assets/images/edit_hovered.svg @@ -0,0 +1,13 @@ + + + + options_over + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/tests/assets/images/edit_normal.svg b/tests/assets/images/edit_normal.svg new file mode 100644 index 000000000..c3b080307 --- /dev/null +++ b/tests/assets/images/edit_normal.svg @@ -0,0 +1,13 @@ + + + + options_default + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/tests/assets/images/edit_pressed.svg b/tests/assets/images/edit_pressed.svg new file mode 100644 index 000000000..7046014e8 --- /dev/null +++ b/tests/assets/images/edit_pressed.svg @@ -0,0 +1,13 @@ + + + + options_clic + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/tests/resources.qrc b/tests/resources.qrc index e32ddb67e..ca2d2825b 100644 --- a/tests/resources.qrc +++ b/tests/resources.qrc @@ -53,6 +53,9 @@ assets/images/delete_hovered.svg assets/images/delete_normal.svg assets/images/delete_pressed.svg + assets/images/edit_hovered.svg + assets/images/edit_normal.svg + assets/images/edit_pressed.svg assets/images/ended_call.svg assets/images/filter.svg assets/images/fullscreen_hovered.svg diff --git a/tests/src/components/contact/ContactModel.cpp b/tests/src/components/contact/ContactModel.cpp index 19d7406b0..3485777a1 100644 --- a/tests/src/components/contact/ContactModel.cpp +++ b/tests/src/components/contact/ContactModel.cpp @@ -1,3 +1,5 @@ +#include + #include "../../app/App.hpp" #include "ContactModel.hpp" @@ -16,6 +18,18 @@ ContactModel::ContactModel (shared_ptr linphone_friend) { App::getInstance()->getEngine()->setObjectOwnership(m_vcard.get(), QQmlEngine::CppOwnership); } +ContactModel::ContactModel (VcardModel *vcard) { + QQmlEngine *engine = App::getInstance()->getEngine(); + if (engine->objectOwnership(vcard) == QQmlEngine::CppOwnership) + throw std::invalid_argument("A contact is already linked to this vcard."); + + m_linphone_friend = linphone::Friend::newFromVcard(vcard->m_vcard); + m_linphone_friend->setData(NAME, *this); + m_vcard.reset(vcard); + + engine->setObjectOwnership(vcard, QQmlEngine::CppOwnership); +} + Presence::PresenceStatus ContactModel::getPresenceStatus () const { return Presence::PresenceStatus::Offline; } diff --git a/tests/src/components/contact/ContactModel.hpp b/tests/src/components/contact/ContactModel.hpp index e166e649e..9ce99789e 100644 --- a/tests/src/components/contact/ContactModel.hpp +++ b/tests/src/components/contact/ContactModel.hpp @@ -22,6 +22,7 @@ class ContactModel : public QObject { public: ContactModel (std::shared_ptr linphone_friend); + ContactModel (VcardModel *vcard); static const char *NAME; diff --git a/tests/src/components/contact/VcardModel.cpp b/tests/src/components/contact/VcardModel.cpp index 0ec6d8700..a082fdc31 100644 --- a/tests/src/components/contact/VcardModel.cpp +++ b/tests/src/components/contact/VcardModel.cpp @@ -109,8 +109,7 @@ bool VcardModel::setAvatar (const QString &path) { } // 4. Update. - shared_ptr photo = - belcard::BelCardGeneric::create(); + shared_ptr photo = belcard::BelCardGeneric::create(); photo->setValue(VCARD_SCHEME + ::Utils::qStringToLinphoneString(file_id)); if (!belcard->addPhoto(photo)) @@ -145,8 +144,7 @@ bool VcardModel::setAddress (const QVariantMap &address) { while (!addresses.empty()) belcard->removeAddress(addresses.front()); - shared_ptr belcard_address = - belcard::BelCardGeneric::create(); + shared_ptr belcard_address = belcard::BelCardGeneric::create(); belcard_address->setStreet(::Utils::qStringToLinphoneString(address["street"].toString())); belcard_address->setLocality(::Utils::qStringToLinphoneString(address["locality"].toString())); @@ -163,42 +161,35 @@ bool VcardModel::setAddress (const QVariantMap &address) { QVariantList VcardModel::getSipAddresses () const { QVariantList list; - for (const auto &address : m_vcard->getSipAddresses()) - list.append(::Utils::linphoneStringToQString(address->asString())); + for (const auto &address : m_vcard->getBelcard()->getImpp()) + list.append(::Utils::linphoneStringToQString(address->getValue())); return list; } bool VcardModel::addSipAddress (const QString &sip_address) { - shared_ptr address = CoreManager::getInstance()->getCore()->createAddress( - ::Utils::qStringToLinphoneString(sip_address) - ); - - if (!address) { - qWarning() << QStringLiteral("Unable to add invalid sip address: `%1`.").arg(sip_address); - return false; - } + shared_ptr belcard = m_vcard->getBelcard(); + shared_ptr value = belcard::BelCardGeneric::create(); + value->setValue(::Utils::qStringToLinphoneString(sip_address)); qInfo() << QStringLiteral("Add new sip address: `%1`.").arg(sip_address); - m_vcard->addSipAddress(address->asStringUriOnly()); + + if (!belcard->addImpp(value)) { + qWarning() << QStringLiteral("Unable to add sip address: `%1`.").arg(sip_address); + return false; + } emit vcardUpdated(); return true; } void VcardModel::removeSipAddress (const QString &sip_address) { - list > addresses = m_vcard->getSipAddresses(); - string match = ::Utils::qStringToLinphoneString(sip_address); + shared_ptr belcard = m_vcard->getBelcard(); + list > addresses = belcard->getImpp(); + shared_ptr value = findBelCardValue(addresses, sip_address); - auto it = find_if( - addresses.cbegin(), addresses.cend(), [&match](const shared_ptr &address) { - return match == address->asString(); - } - ); - - if (it == addresses.cend()) { - qWarning() << QStringLiteral("Unable to found sip address: `%1`.") - .arg(sip_address); + if (!value) { + qWarning() << QStringLiteral("Unable to remove sip address: `%1`.").arg(sip_address); return; } @@ -209,7 +200,7 @@ void VcardModel::removeSipAddress (const QString &sip_address) { } qInfo() << QStringLiteral("Remove sip address: `%1`.").arg(sip_address); - m_vcard->removeSipAddress((*it)->asStringUriOnly()); + belcard->removeImpp(value); emit vcardUpdated(); } @@ -241,8 +232,10 @@ bool VcardModel::addCompany (const QString &company) { qInfo() << QStringLiteral("Add new company: `%1`.").arg(company); - if (!belcard->addRole(value)) + if (!belcard->addRole(value)) { + qWarning() << QStringLiteral("Unable to add company: `%1`.").arg(company); return false; + } emit vcardUpdated(); return true; @@ -285,14 +278,15 @@ QVariantList VcardModel::getEmails () const { bool VcardModel::addEmail (const QString &email) { shared_ptr belcard = m_vcard->getBelcard(); - shared_ptr value = - belcard::BelCardGeneric::create(); + shared_ptr value = belcard::BelCardGeneric::create(); value->setValue(::Utils::qStringToLinphoneString(email)); qInfo() << QStringLiteral("Add new email: `%1`.").arg(email); - if (!belcard->addEmail(value)) + if (!belcard->addEmail(value)) { + qWarning() << QStringLiteral("Unable to add email: `%1`.").arg(email); return false; + } emit vcardUpdated(); return true; @@ -340,8 +334,10 @@ bool VcardModel::addUrl (const QString &url) { qInfo() << QStringLiteral("Add new url: `%1`.").arg(url); - if (!belcard->addURL(value)) + if (!belcard->addURL(value)) { + qWarning() << QStringLiteral("Unable to add url: `%1`.").arg(url); return false; + } emit vcardUpdated(); return true; diff --git a/tests/src/components/contact/VcardModel.hpp b/tests/src/components/contact/VcardModel.hpp index 709641d12..3606fca63 100644 --- a/tests/src/components/contact/VcardModel.hpp +++ b/tests/src/components/contact/VcardModel.hpp @@ -17,11 +17,13 @@ class VcardModel : public QObject { Q_PROPERTY(QVariantList emails READ getEmails NOTIFY vcardUpdated); Q_PROPERTY(QVariantList urls READ getUrls NOTIFY vcardUpdated); - friend class ContactsListProxyModel; + friend class ContactModel; public: VcardModel (std::shared_ptr vcard) : m_vcard(vcard) {} + QString getUsername () const; + ~VcardModel () = default; public slots: @@ -45,7 +47,6 @@ signals: void vcardUpdated (); private: - QString getUsername () const; void setUsername (const QString &username); QString getAvatar () const; diff --git a/tests/src/components/contacts/ContactsListModel.cpp b/tests/src/components/contacts/ContactsListModel.cpp index aba27f5b5..fd6b38a48 100644 --- a/tests/src/components/contacts/ContactsListModel.cpp +++ b/tests/src/components/contacts/ContactsListModel.cpp @@ -27,7 +27,7 @@ ContactsListModel::ContactsListModel (QObject *parent) : QAbstractListModel(pare } } -int ContactsListModel::rowCount (const QModelIndex&) const { +int ContactsListModel::rowCount (const QModelIndex &) const { return m_list.count(); } @@ -49,8 +49,8 @@ QVariant ContactsListModel::data (const QModelIndex &index, int role) const { return QVariant(); } -bool ContactsListModel::removeRow (int row, const QModelIndex&) { - return removeRows(row, 1); +bool ContactsListModel::removeRow (int row, const QModelIndex &parent) { + return removeRows(row, 1, parent); } bool ContactsListModel::removeRows (int row, int count, const QModelIndex &parent) { @@ -76,19 +76,38 @@ bool ContactsListModel::removeRows (int row, int count, const QModelIndex &paren // ----------------------------------------------------------------------------- ContactModel *ContactsListModel::mapSipAddressToContact (const QString &sipAddress) const { - // TODO: Maybe use a hashtable in future version to get a lower cost? shared_ptr friend_ = m_linphone_friends->findFriendByUri( ::Utils::qStringToLinphoneString(sipAddress) ); - if (!friend_) { - qInfo() << QStringLiteral("Unable to map sip address: `%1`.").arg(sipAddress); + if (!friend_) return nullptr; - } return &friend_->getData(ContactModel::NAME); } +void ContactsListModel::addContact (VcardModel *vcard) { + ContactModel *contact = new ContactModel(vcard); + App::getInstance()->getEngine()->setObjectOwnership(contact, QQmlEngine::CppOwnership); + + qInfo() << "Add contact:" << contact; + + if ( + m_linphone_friends->addFriend(contact->m_linphone_friend) != + linphone::FriendListStatus::FriendListStatusOK + ) { + qWarning() << "Unable to add friend from vcard:" << vcard; + delete contact; + return; + } + + int row = rowCount(); + + beginInsertRows(QModelIndex(), row, row); + m_list << contact; + endInsertRows(); +} + void ContactsListModel::removeContact (ContactModel *contact) { qInfo() << "Removing contact:" << contact; diff --git a/tests/src/components/contacts/ContactsListModel.hpp b/tests/src/components/contacts/ContactsListModel.hpp index 3c2af153b..524a984d6 100644 --- a/tests/src/components/contacts/ContactsListModel.hpp +++ b/tests/src/components/contacts/ContactsListModel.hpp @@ -27,6 +27,7 @@ public: public slots: ContactModel *mapSipAddressToContact (const QString &sipAddress) const; + void addContact (VcardModel *vcard); void removeContact (ContactModel *contact); private: diff --git a/tests/src/components/core/CoreManager.cpp b/tests/src/components/core/CoreManager.cpp index 184a8941c..4132187b3 100644 --- a/tests/src/components/core/CoreManager.cpp +++ b/tests/src/components/core/CoreManager.cpp @@ -6,12 +6,15 @@ CoreManager *CoreManager::m_instance = nullptr; -CoreManager::CoreManager (QObject *parent) : QObject(parent), m_core( - linphone::Factory::get()->createCore(nullptr, "", "", nullptr) -) { +CoreManager::CoreManager (QObject *parent) : QObject(parent), + m_core(linphone::Factory::get()->createCore(nullptr, "", "", nullptr)) { setDatabasesPaths(); } +VcardModel *CoreManager::createDetachedVcardModel () { + return new VcardModel(linphone::Factory::get()->createVcard()); +} + void CoreManager::setDatabasesPaths () { std::string database_path; diff --git a/tests/src/components/core/CoreManager.hpp b/tests/src/components/core/CoreManager.hpp index 02e0e24d2..cb6b26a62 100644 --- a/tests/src/components/core/CoreManager.hpp +++ b/tests/src/components/core/CoreManager.hpp @@ -4,6 +4,8 @@ #include #include +#include "../contact/VcardModel.hpp" + // =================================================================== class CoreManager : public QObject { @@ -24,14 +26,16 @@ public: return m_core; } +public slots: + VcardModel *createDetachedVcardModel (); + private: CoreManager (QObject *parent = Q_NULLPTR); void setDatabasesPaths (); - static CoreManager *m_instance; - std::shared_ptr m_core; + static CoreManager *m_instance; }; #endif // CORE_MANAGER_H_ diff --git a/tests/ui/modules/Common/Colors.qml b/tests/ui/modules/Common/Colors.qml index f0ed65003..c8253c54e 100644 --- a/tests/ui/modules/Common/Colors.qml +++ b/tests/ui/modules/Common/Colors.qml @@ -1,13 +1,11 @@ pragma Singleton import QtQuick 2.7 -// =================================================================== +// ============================================================================= QtObject { property color a: 'transparent' - property color o: '#232323' // TextButtonA Hovered. - property color q: '#E6E6E6' property color d: '#5A585B' @@ -24,6 +22,7 @@ QtObject { property color g20: '#336B7A86' property color h: '#687680' property color i: '#FE5E00' + property color i30: '#4DFE5E00' property color j: '#4B5964' property color j75: '#BF4B5964' property color k: '#FFFFFF' @@ -31,6 +30,7 @@ QtObject { property color l: '#000000' property color m: '#D1D1D1' property color n: '#C0C0C0' + property color o: '#232323' property color p: '#E2E9EF' property color r: '#595759' property color s: '#D64D00' diff --git a/tests/ui/modules/Common/Form/AbstractTextButton.qml b/tests/ui/modules/Common/Form/AbstractTextButton.qml index b8b397685..255fc14ea 100644 --- a/tests/ui/modules/Common/Form/AbstractTextButton.qml +++ b/tests/ui/modules/Common/Form/AbstractTextButton.qml @@ -3,48 +3,79 @@ import QtQuick.Controls 2.0 import Common.Styles 1.0 -// =================================================================== +// ============================================================================= -Button { - id: button +Item { + id: wrappedButton + property color colorDisabled property color colorHovered property color colorNormal property color colorPressed // By default textColorNormal is the hovered/pressed text color. + property color textColorDisabled property color textColorHovered: textColorNormal property color textColorNormal property color textColorPressed: textColorNormal - background: Rectangle { - color: button.down - ? colorPressed - : (button.hovered - ? colorHovered - : colorNormal - ) - implicitHeight: AbstractTextButtonStyle.background.height - implicitWidth: AbstractTextButtonStyle.background.width - radius: AbstractTextButtonStyle.background.radius - } - contentItem: Text { - color: button.down - ? textColorPressed - : (button.hovered - ? textColorHovered - : textColorNormal - ) + property alias text: button.text + property bool enabled: true - font { - bold: true - pointSize: AbstractTextButtonStyle.text.fontSize + signal clicked + + // --------------------------------------------------------------------------- + + function _getBackgroundColor () { + if (!wrappedButton.enabled) { + return colorDisabled } - elide: Text.ElideRight - horizontalAlignment: Text.AlignHCenter - text: button.text - verticalAlignment: Text.AlignVCenter + return button.down + ? colorPressed + : (button.hovered ? colorHovered : colorNormal) + } + + function _getTextColor () { + if (!wrappedButton.enabled) { + return textColorDisabled + } + + return button.down + ? textColorPressed + : (button.hovered ? textColorHovered : textColorNormal) + } + + // --------------------------------------------------------------------------- + + implicitHeight: button.height + implicitWidth: button.width + + // --------------------------------------------------------------------------- + + Button { + id: button + + background: Rectangle { + color: _getBackgroundColor() + implicitHeight: AbstractTextButtonStyle.background.height + implicitWidth: AbstractTextButtonStyle.background.width + radius: AbstractTextButtonStyle.background.radius + } + contentItem: Text { + color: _getTextColor() + font { + bold: true + pointSize: AbstractTextButtonStyle.text.fontSize + } + + elide: Text.ElideRight + horizontalAlignment: Text.AlignHCenter + text: button.text + verticalAlignment: Text.AlignVCenter + } + hoverEnabled: true + + onClicked: wrappedButton.enabled && parent.clicked() } - hoverEnabled: true } diff --git a/tests/ui/modules/Common/Form/ListForm.qml b/tests/ui/modules/Common/Form/ListForm.qml index b8bea4ea5..36999c0be 100644 --- a/tests/ui/modules/Common/Form/ListForm.qml +++ b/tests/ui/modules/Common/Form/ListForm.qml @@ -5,21 +5,23 @@ import Common 1.0 import Common.Styles 1.0 import Utils 1.0 -// =================================================================== +// ============================================================================= RowLayout { id: listForm property alias placeholder: placeholder.text property alias title: text.text + property bool readOnly: false property int inputMethodHints property var defaultData: [] property var minValues + readonly property int count: values.count - signal changed (int index, string default_value, string new_value) + signal changed (int index, string defaultValue, string newValue) signal removed (int index, string value) - // ----------------------------------------------------------------- + // --------------------------------------------------------------------------- function setInvalid (index, status) { Utils.assert( @@ -48,47 +50,41 @@ RowLayout { } function _handleEditionFinished (index, text) { + var model = values.model + var defaultValue = model.get(index).$value + if (text.length === 0) { - // Remove. - var default_value = values.model.get(index).$value - - if (minValues != null && minValues >= values.model.count) { - var model = values.model - + // No changes. It must exists at least n min values. + if (minValues != null && minValues >= model.count) { // Unable to set property directly. Qt uses a cache of the value. model.remove(index) model.insert(index, { $isInvalid: false, - $value: default_value + $value: defaultValue }) return } - values.model.remove(index) + model.remove(index) - if (default_value.length !== 0) { - listForm.removed(index, default_value) - } - } else { - // Update. - var default_value = values.model.get(index).$value - - // If no changes, no signal. - if (text !== default_value) { - listForm.changed(index, default_value, text) + if (defaultValue.length !== 0) { + listForm.removed(index, defaultValue) } + } else if (text !== defaultValue) { + // Update changes. + listForm.changed(index, defaultValue, text) } addButton.enabled = true } - // ----------------------------------------------------------------- + // --------------------------------------------------------------------------- spacing: 0 - // ----------------------------------------------------------------- + // --------------------------------------------------------------------------- // Title area. - // ----------------------------------------------------------------- + // --------------------------------------------------------------------------- RowLayout { Layout.alignment: Qt.AlignTop @@ -100,8 +96,9 @@ RowLayout { icon: 'add' iconSize: ListFormStyle.titleArea.iconSize + opacity: _edition ? 1 : 0 - onClicked: _addValue('') + onClicked: _edition && _addValue('') } Text { @@ -118,9 +115,9 @@ RowLayout { } } - // ----------------------------------------------------------------- + // --------------------------------------------------------------------------- // Placeholder. - // ----------------------------------------------------------------- + // --------------------------------------------------------------------------- Text { id: placeholder @@ -135,7 +132,7 @@ RowLayout { } padding: ListFormStyle.value.text.padding - visible: values.model.count === 0 + visible: values.model.count === 0 && !listForm.readOnly verticalAlignment: Text.AlignVCenter MouseArea { @@ -144,9 +141,9 @@ RowLayout { } } - // ----------------------------------------------------------------- + // --------------------------------------------------------------------------- // Values. - // ----------------------------------------------------------------- + // --------------------------------------------------------------------------- ListView { id: values @@ -165,6 +162,7 @@ RowLayout { inputMethodHints: listForm.inputMethodHints isInvalid: $isInvalid + readOnly: listForm.readOnly text: $value height: ListFormStyle.lineHeight @@ -190,16 +188,14 @@ RowLayout { model: ListModel {} - // --------------------------------------------------------------- + // ------------------------------------------------------------------------- // Init values. - // --------------------------------------------------------------- + // ------------------------------------------------------------------------- Component.onCompleted: { - if (!defaultData) { - return + if (defaultData) { + setData(defaultData) } - - setData(defaultData) } } } diff --git a/tests/ui/modules/Common/Form/TextButtonA.qml b/tests/ui/modules/Common/Form/TextButtonA.qml index e07d31786..e290716f4 100644 --- a/tests/ui/modules/Common/Form/TextButtonA.qml +++ b/tests/ui/modules/Common/Form/TextButtonA.qml @@ -1,12 +1,14 @@ import Common.Styles 1.0 -// =================================================================== +// ============================================================================= AbstractTextButton { + colorDisabled: TextButtonAStyle.backgroundColor.disabled colorHovered: TextButtonAStyle.backgroundColor.hovered colorNormal: TextButtonAStyle.backgroundColor.normal colorPressed: TextButtonAStyle.backgroundColor.pressed + textColorDisabled: TextButtonAStyle.textColor.disabled textColorHovered: TextButtonAStyle.textColor.hovered textColorNormal: TextButtonAStyle.textColor.normal textColorPressed: TextButtonAStyle.textColor.pressed diff --git a/tests/ui/modules/Common/Form/TextButtonB.qml b/tests/ui/modules/Common/Form/TextButtonB.qml index aacaf6c22..f11f6e196 100644 --- a/tests/ui/modules/Common/Form/TextButtonB.qml +++ b/tests/ui/modules/Common/Form/TextButtonB.qml @@ -1,12 +1,14 @@ import Common.Styles 1.0 -// =================================================================== +// ============================================================================= AbstractTextButton { + colorDisabled: TextButtonBStyle.backgroundColor.disabled colorHovered: TextButtonBStyle.backgroundColor.hovered colorNormal: TextButtonBStyle.backgroundColor.normal colorPressed: TextButtonBStyle.backgroundColor.pressed + textColorDisabled: TextButtonBStyle.textColor.disabled textColorHovered: TextButtonBStyle.textColor.hovered textColorNormal: TextButtonBStyle.textColor.normal textColorPressed: TextButtonBStyle.textColor.pressed diff --git a/tests/ui/modules/Common/Styles/Form/TextButtonAStyle.qml b/tests/ui/modules/Common/Styles/Form/TextButtonAStyle.qml index 4cb056cdd..73a8e3059 100644 --- a/tests/ui/modules/Common/Styles/Form/TextButtonAStyle.qml +++ b/tests/ui/modules/Common/Styles/Form/TextButtonAStyle.qml @@ -3,16 +3,18 @@ import QtQuick 2.7 import Common 1.0 -// =================================================================== +// ============================================================================= QtObject { property QtObject backgroundColor: QtObject { + property color disabled: Colors.o property color hovered: Colors.o property color normal: Colors.j property color pressed: Colors.i } property QtObject textColor: QtObject { + property color disabled: Colors.k property color hovered: Colors.k property color normal: Colors.k property color pressed: Colors.k diff --git a/tests/ui/modules/Common/Styles/Form/TextButtonBStyle.qml b/tests/ui/modules/Common/Styles/Form/TextButtonBStyle.qml index 25ae38053..5ea66f2a6 100644 --- a/tests/ui/modules/Common/Styles/Form/TextButtonBStyle.qml +++ b/tests/ui/modules/Common/Styles/Form/TextButtonBStyle.qml @@ -3,16 +3,18 @@ import QtQuick 2.7 import Common 1.0 -// =================================================================== +// ============================================================================= QtObject { property QtObject backgroundColor: QtObject { + property color disabled: Colors.i30 property color hovered: Colors.s property color normal: Colors.i property color pressed: Colors.t } property QtObject textColor: QtObject { + property color disabled: Colors.k property color hovered: Colors.k property color normal: Colors.k property color pressed: Colors.k diff --git a/tests/ui/views/App/MainWindow/ContactEdit.qml b/tests/ui/views/App/MainWindow/ContactEdit.qml index cf8d471b9..17028c3d2 100644 --- a/tests/ui/views/App/MainWindow/ContactEdit.qml +++ b/tests/ui/views/App/MainWindow/ContactEdit.qml @@ -5,22 +5,45 @@ import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 -import LinphoneUtils 1.0 import Utils 1.0 import App.Styles 1.0 -// =================================================================== +// ============================================================================= ColumnLayout { id: contactEdit - property string sipAddress: '' + property string sipAddress + property bool _edition: false property var _contact property var _vcard - // ----------------------------------------------------------------- + // --------------------------------------------------------------------------- + + function _editContact () { + _contact.startEdit() + _edition = true + } + + function _save () { + if (_contact) { + _contact.endEdit() + _edition = false + } else { + _contact = ContactsListModel.addContact(_vcard) + } + } + + function _cancel () { + if (_contact) { + _contact.abortEdit() + _edition = false + } else { + window.setView('Contacts') + } + } function _removeContact () { Utils.openConfirmDialog(window, { @@ -46,45 +69,55 @@ ColumnLayout { usernameInput.text = _vcard.username } - function _handleSipAddressChanged (index, default_value, new_value) { - if (!Utils.startsWith(new_value, 'sip:')) { - new_value = 'sip:' + new_value + function _handleSipAddressChanged (index, defaultValue, newValue) { + if (!Utils.startsWith(newValue, 'sip:')) { + newValue = 'sip:' + newValue - if (new_value === default_value) { + if (newValue === defaultValue) { return } } - var so_far_so_good = (default_value.length === 0) - ? _vcard.addSipAddress(new_value) - : _vcard.updateSipAddress(default_value, new_value) + var so_far_so_good = (defaultValue.length === 0) + ? _vcard.addSipAddress(newValue) + : _vcard.updateSipAddress(defaultValue, newValue) addresses.setInvalid(index, !so_far_so_good) } - function _handleUrlChanged (index, default_value, new_value) { - var url = Utils.extractFirstUri(new_value) - if (url === default_value) { + function _handleUrlChanged (index, defaultValue, newValue) { + var url = Utils.extractFirstUri(newValue) + if (url === defaultValue) { return } - var so_far_so_good = (default_value.length === 0) - ? url && _vcard.addUrl(new_value) - : url && _vcard.updateUrl(default_value, new_value) + var so_far_so_good = (defaultValue.length === 0) + ? url && _vcard.addUrl(newValue) + : url && _vcard.updateUrl(defaultValue, newValue) urls.setInvalid(index, !so_far_so_good) } - // ----------------------------------------------------------------- + // --------------------------------------------------------------------------- spacing: 0 Component.onCompleted: { _contact = ContactsListModel.mapSipAddressToContact(sipAddress) - _vcard = _contact.vcard + + if (!_contact) { + _vcard = CoreManager.createDetachedVcardModel() + _edition = true + } else { + _vcard = _contact.vcard + } } - // ----------------------------------------------------------------- + Component.onDestruction: { + // TODO: Remove photo if contact not created. + } + + // --------------------------------------------------------------------------- FileDialog { id: avatarChooser @@ -95,9 +128,9 @@ ColumnLayout { onAccepted: _setAvatar(fileUrls[0]) } - // ----------------------------------------------------------------- + // --------------------------------------------------------------------------- // Info bar. - // ----------------------------------------------------------------- + // --------------------------------------------------------------------------- Rectangle { Layout.fillWidth: true @@ -124,7 +157,7 @@ ColumnLayout { anchors.fill: parent image: _vcard.avatar - username: LinphoneUtils.getContactUsername(_contact) || 'John Doe' + username: _vcard.username visible: isLoaded() && !parent.hovered } } @@ -155,22 +188,30 @@ ColumnLayout { ActionButton { icon: 'history' + onClicked: window.setView('Conversation', { sipAddress: contactEdit.sipAddress }) } + ActionButton { + icon: 'edit' + visible: !_edition + onClicked: _editContact() + } + ActionButton { icon: 'delete' + onClicked: _removeContact() } } } } - // ----------------------------------------------------------------- + // --------------------------------------------------------------------------- // Info list. - // ----------------------------------------------------------------- + // --------------------------------------------------------------------------- Flickable { id: flick @@ -197,12 +238,13 @@ ColumnLayout { Layout.rightMargin: ContactEditStyle.values.rightMargin Layout.topMargin: ContactEditStyle.values.topMargin - defaultData: _vcard.sipAddresses - minValues: 1 + defaultData: _vcard.sipAddresses + minValues: _contact ? 1 : 0 placeholder: qsTr('sipAccountsInput') + readOnly: !_edition title: qsTr('sipAccounts') - onChanged: _handleSipAddressChanged(index, default_value, new_value) + onChanged: _handleSipAddressChanged(index, defaultValue, newValue) onRemoved: _vcard.removeSipAddress(value) } @@ -220,11 +262,12 @@ ColumnLayout { defaultData: _vcard.companies placeholder: qsTr('companiesInput') + readOnly: !_edition title: qsTr('companies') - onChanged: default_value.length === 0 - ? _vcard.addCompany(new_value) - : _vcard.updateCompany(default_value, new_value) + onChanged: defaultValue.length === 0 + ? _vcard.addCompany(newValue) + : _vcard.updateCompany(defaultValue, newValue) onRemoved: _vcard.removeCompany(value) } @@ -243,11 +286,12 @@ ColumnLayout { defaultData: _vcard.emails inputMethodHints: Qt.ImhEmailCharactersOnly placeholder: qsTr('emailsInput') + readOnly: !_edition title: qsTr('emails') - onChanged: default_value.length === 0 - ? _vcard.addEmail(new_value) - : _vcard.updateEmail(default_value, new_value) + onChanged: defaultValue.length === 0 + ? _vcard.addEmail(newValue) + : _vcard.updateEmail(defaultValue, newValue) onRemoved: _vcard.removeEmail(value) } @@ -266,9 +310,10 @@ ColumnLayout { defaultData: _vcard.urls inputMethodHints: Qt.ImhUrlCharactersOnly placeholder: qsTr('webSitesInput') + readOnly: !_edition title: qsTr('webSites') - onChanged: _handleUrlChanged(index, default_value, new_value) + onChanged: _handleUrlChanged(index, defaultValue, newValue) onRemoved: _vcard.removeUrl(value) } @@ -278,20 +323,21 @@ ColumnLayout { color: ContactEditStyle.values.separator.color } - Loader { + Row { Layout.alignment: Qt.AlignHCenter Layout.topMargin: ContactEditStyle.buttons.topMargin + spacing: ContactEditStyle.buttons.spacing + visible: _edition - sourceComponent: Row { - spacing: ContactEditStyle.buttons.spacing + TextButtonB { + enabled: _vcard.sipAddresses.length > 0 + text: qsTr('save') + onClicked: _save() + } - TextButtonB { - text: qsTr('save') - } - - TextButtonA { - text: qsTr('cancel') - } + TextButtonA { + text: qsTr('cancel') + onClicked: _cancel() } }