From de2612b40e3af29039d936fa36ea92b65e2cfb60 Mon Sep 17 00:00:00 2001 From: Julien Wadel Date: Wed, 20 Nov 2024 15:47:15 +0100 Subject: [PATCH] Contacts list refactoring + Update SDK for fixing LDAP timeout. --- Linphone/core/proxy/LimitProxy.cpp | 11 +- Linphone/core/proxy/LimitProxy.hpp | 4 + Linphone/core/proxy/SortFilterProxy.hpp | 5 +- Linphone/core/search/MagicSearchList.cpp | 14 + Linphone/core/search/MagicSearchList.hpp | 1 + Linphone/core/search/MagicSearchProxy.cpp | 84 ++-- Linphone/core/search/MagicSearchProxy.hpp | 28 +- Linphone/view/CMakeLists.txt | 1 + .../Display/Contact/AllContactListView.qml | 332 +++++++++++++ .../Display/Contact/ContactListItem.qml | 24 +- .../Display/Contact/ContactListView.qml | 469 ++++-------------- Linphone/view/Page/Form/Call/NewCallForm.qml | 22 +- .../Page/Form/Meeting/AddParticipantsForm.qml | 45 +- Linphone/view/Page/Layout/Main/MainLayout.qml | 45 +- .../view/Page/Main/Contact/ContactPage.qml | 15 +- external/linphone-sdk | 2 +- 16 files changed, 549 insertions(+), 553 deletions(-) create mode 100644 Linphone/view/Control/Display/Contact/AllContactListView.qml diff --git a/Linphone/core/proxy/LimitProxy.cpp b/Linphone/core/proxy/LimitProxy.cpp index 6877a1b04..7a3c61547 100644 --- a/Linphone/core/proxy/LimitProxy.cpp +++ b/Linphone/core/proxy/LimitProxy.cpp @@ -24,6 +24,7 @@ LimitProxy::LimitProxy(QObject *parent) : QSortFilterProxyModel(parent) { connect(this, &LimitProxy::rowsInserted, this, &LimitProxy::countChanged); connect(this, &LimitProxy::rowsRemoved, this, &LimitProxy::countChanged); connect(this, &LimitProxy::modelReset, this, &LimitProxy::countChanged); + connect(this, &LimitProxy::countChanged, this, &LimitProxy::haveMoreChanged); } /* LimitProxy::LimitProxy(QAbstractItemModel *sortFilterProxy, QObject *parent) : QSortFilterProxyModel(parent) { @@ -79,7 +80,7 @@ int LimitProxy::getInitialDisplayItems() const { } void LimitProxy::setInitialDisplayItems(int initialItems) { - if (initialItems != 0 && mInitialDisplayItems != initialItems) { + if (mInitialDisplayItems != initialItems) { mInitialDisplayItems = initialItems; if (getMaxDisplayItems() <= mInitialDisplayItems) setMaxDisplayItems(initialItems); if (getDisplayItemsStep() <= 0) setDisplayItemsStep(initialItems); @@ -99,7 +100,7 @@ int LimitProxy::getMaxDisplayItems() const { return mMaxDisplayItems; } void LimitProxy::setMaxDisplayItems(int maxItems) { - if (maxItems != 0 && mMaxDisplayItems != maxItems) { + if (mMaxDisplayItems != maxItems) { auto model = sourceModel(); int modelCount = model ? model->rowCount() : 0; int oldCount = getDisplayCount(modelCount); @@ -125,6 +126,12 @@ void LimitProxy::setDisplayItemsStep(int step) { } } +bool LimitProxy::getHaveMore() const { + auto model = sourceModel(); + int modelCount = model ? model->rowCount() : 0; + return getCount() < modelCount; +} + //-------------------------------------------------------------------------------------------------- QString LimitProxy::getFilterText() const { diff --git a/Linphone/core/proxy/LimitProxy.hpp b/Linphone/core/proxy/LimitProxy.hpp index eb4ed190e..9891013f1 100644 --- a/Linphone/core/proxy/LimitProxy.hpp +++ b/Linphone/core/proxy/LimitProxy.hpp @@ -32,6 +32,7 @@ public: initialDisplayItemsChanged) Q_PROPERTY(int maxDisplayItems READ getMaxDisplayItems WRITE setMaxDisplayItems NOTIFY maxDisplayItemsChanged) Q_PROPERTY(int displayItemsStep READ getDisplayItemsStep WRITE setDisplayItemsStep NOTIFY displayItemsStepChanged) + Q_PROPERTY(bool haveMore READ getHaveMore NOTIFY haveMoreChanged) // Propagation Q_PROPERTY(QString filterText READ getFilterText WRITE setFilterText NOTIFY filterTextChanged) @@ -72,6 +73,8 @@ public: int getDisplayItemsStep() const; void setDisplayItemsStep(int step); + bool getHaveMore() const; + //------------------------------------------------------------- QString getFilterText() const; void setFilterText(const QString &filter); @@ -92,6 +95,7 @@ signals: void initialDisplayItemsChanged(); void maxDisplayItemsChanged(); void displayItemsStepChanged(); + void haveMoreChanged(); //----------------------------------------------------------------- void filterTypeChanged(int filterType); void filterTextChanged(); diff --git a/Linphone/core/proxy/SortFilterProxy.hpp b/Linphone/core/proxy/SortFilterProxy.hpp index 8caeeada5..f604de42f 100644 --- a/Linphone/core/proxy/SortFilterProxy.hpp +++ b/Linphone/core/proxy/SortFilterProxy.hpp @@ -71,14 +71,15 @@ public: Q_INVOKABLE void remove(int index, int count = 1); void invalidateFilter(); + int mFilterType = 0; + QString mFilterText; + signals: void countChanged(); void filterTypeChanged(int filterType); void filterTextChanged(); protected: - int mFilterType = 0; - QString mFilterText; bool mDeleteSourceModel = false; }; diff --git a/Linphone/core/search/MagicSearchList.cpp b/Linphone/core/search/MagicSearchList.cpp index 97e02f2cd..73a3f1c55 100644 --- a/Linphone/core/search/MagicSearchList.cpp +++ b/Linphone/core/search/MagicSearchList.cpp @@ -224,3 +224,17 @@ int MagicSearchList::findFriendIndexByAddress(const QString &address) { } return -1; } + +QSharedPointer MagicSearchList::findFriendByAddress(const QString &address) { + for (int i = 0; i < getCount(); ++i) { + auto friendCore = getAt(i); + if (!friendCore) continue; + for (auto &friendAddress : friendCore->getAllAddresses()) { + auto map = friendAddress.toMap(); + if (map["address"].toString() == address) { + return friendCore; + } + } + } + return nullptr; +} diff --git a/Linphone/core/search/MagicSearchList.hpp b/Linphone/core/search/MagicSearchList.hpp index f3b820333..84168ec94 100644 --- a/Linphone/core/search/MagicSearchList.hpp +++ b/Linphone/core/search/MagicSearchList.hpp @@ -58,6 +58,7 @@ public: virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int findFriendIndexByAddress(const QString &address); + QSharedPointer findFriendByAddress(const QString &address); signals: void diff --git a/Linphone/core/search/MagicSearchProxy.cpp b/Linphone/core/search/MagicSearchProxy.cpp index d275add05..d954b4137 100644 --- a/Linphone/core/search/MagicSearchProxy.cpp +++ b/Linphone/core/search/MagicSearchProxy.cpp @@ -68,9 +68,7 @@ void MagicSearchProxy::setList(QSharedPointer newList) { } auto sortFilterList = new SortFilterList(mList.get(), Qt::AscendingOrder); if (oldModel) { - sortFilterList->mShowFavoritesOnly = oldModel->mShowFavoritesOnly; - sortFilterList->mHideSuggestions = oldModel->mHideSuggestions; - sortFilterList->mShowLdapContacts = oldModel->mShowLdapContacts; + sortFilterList->mFilterType = oldModel->mFilterType; sortFilterList->mHideListProxy = oldModel->mHideListProxy; if (sortFilterList->mHideListProxy) { connect(sortFilterList->mHideListProxy, &MagicSearchProxy::countChanged, sortFilterList, @@ -82,7 +80,7 @@ void MagicSearchProxy::setList(QSharedPointer newList) { connect( mList.get(), &MagicSearchList::friendStarredChanged, this, [this, sortFilterList]() { - if (showFavoritesOnly()) sortFilterList->invalidate(); + if ((getFilterType() & (int)FilteringTypes::Favorites) > 0) sortFilterList->invalidate(); }, Qt::QueuedConnection); setSourceModels(sortFilterList); @@ -98,6 +96,27 @@ int MagicSearchProxy::findFriendIndexByAddress(const QString &address) { } else return -1; } +FriendGui *MagicSearchProxy::findFriendByAddress(const QString &address) { + auto magicSearchList = getListModel(); + if (magicSearchList) { + auto friendCore = magicSearchList->findFriendByAddress(address); + return friendCore ? new FriendGui(friendCore) : nullptr; + } else return nullptr; +} + +int MagicSearchProxy::loadUntil(const QString &address) { + auto magicSearchList = getListModel(); + if (magicSearchList) { + auto listIndex = magicSearchList->findFriendIndexByAddress(address); + if (listIndex == -1) return -1; + listIndex = + dynamic_cast(sourceModel())->mapFromSource(magicSearchList->index(listIndex, 0)).row(); + if (mMaxDisplayItems < listIndex) setMaxDisplayItems(listIndex + 1); + return listIndex; + } + return -1; +} + QString MagicSearchProxy::getSearchText() const { return mSearchText; } @@ -125,31 +144,6 @@ void MagicSearchProxy::setMaxResults(int flags) { mList->setMaxResults(flags); } -bool MagicSearchProxy::showFavoritesOnly() const { - return dynamic_cast(sourceModel())->mShowFavoritesOnly; -} - -void MagicSearchProxy::setShowFavoritesOnly(bool show) { - auto list = dynamic_cast(sourceModel()); - if (list->mShowFavoritesOnly != show) { - list->mShowFavoritesOnly = show; - list->invalidate(); - emit showFavoriteOnlyChanged(); - } -} - -bool MagicSearchProxy::getHideSuggestions() const { - return dynamic_cast(sourceModel())->mHideSuggestions; -} -void MagicSearchProxy::setHideSuggestions(bool data) { - auto list = dynamic_cast(sourceModel()); - if (list->mHideSuggestions != data) { - list->mHideSuggestions = data; - list->invalidate(); - emit hideSuggestionsChanged(); - } -} - MagicSearchProxy *MagicSearchProxy::getParentProxy() const { return mParentProxy; } @@ -193,9 +187,23 @@ bool MagicSearchProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QMo auto friendCore = getItemAtSource(sourceRow); auto toShow = false; if (friendCore) { - toShow = (!mHideSuggestions || friendCore->getIsStored() || friendCore->isLdap()) && - (!mShowFavoritesOnly || friendCore->getStarred()) && - (mShowLdapContacts || (!friendCore->isLdap() || friendCore->getIsStored())); + if (mFilterType == (int)FilteringTypes::None) return false; + if ((mFilterType & (int)FilteringTypes::Favorites) > 0) { + toShow = friendCore->getStarred(); + if (!toShow) return false; + } + if ((mFilterType & (int)FilteringTypes::Ldap) > 0) { + toShow = friendCore->isLdap(); + // if (!toShow) return false; + } + if (!toShow && (mFilterType & (int)FilteringTypes::App) > 0) { + toShow = friendCore->getIsStored(); + // if (!toShow) return false; + } + if (!toShow && (mFilterType & (int)FilteringTypes::Other) > 0) { + toShow = !friendCore->getIsStored() && !friendCore->isLdap(); + } + if (toShow && mHideListProxy) { for (auto &friendAddress : friendCore->getAllAddresses()) { toShow = mHideListProxy->findFriendIndexByAddress(friendAddress.toMap()["address"].toString()) == -1; @@ -222,15 +230,3 @@ bool MagicSearchProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, c } return true; } - -bool MagicSearchProxy::showLdapContacts() const { - return dynamic_cast(sourceModel())->mShowLdapContacts; -} - -void MagicSearchProxy::setShowLdapContacts(bool show) { - auto list = dynamic_cast(sourceModel()); - if (list->mShowLdapContacts != show) { - list->mShowLdapContacts = show; - list->invalidateFilter(); - } -} diff --git a/Linphone/core/search/MagicSearchProxy.hpp b/Linphone/core/search/MagicSearchProxy.hpp index 90a76db19..399d53bdd 100644 --- a/Linphone/core/search/MagicSearchProxy.hpp +++ b/Linphone/core/search/MagicSearchProxy.hpp @@ -22,7 +22,6 @@ #define MAGIC_SEARCH_PROXY_H_ #include "../proxy/LimitProxy.hpp" -#include "core/friend/FriendGui.hpp" #include "core/search/MagicSearchList.hpp" #include "tool/LinphoneEnums.hpp" @@ -30,24 +29,22 @@ class MagicSearchProxy : public LimitProxy { Q_OBJECT - + Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") // Avoid name clashes Q_PROPERTY(QString searchText READ getSearchText WRITE setSearchText NOTIFY searchTextChanged) Q_PROPERTY(int sourceFlags READ getSourceFlags WRITE setSourceFlags NOTIFY sourceFlagsChanged) Q_PROPERTY(int maxResults READ getMaxResults WRITE setMaxResults NOTIFY maxResultsChanged) Q_PROPERTY(LinphoneEnums::MagicSearchAggregation aggregationFlag READ getAggregationFlag WRITE setAggregationFlag NOTIFY aggregationFlagChanged) - Q_PROPERTY(bool showFavoritesOnly READ showFavoritesOnly WRITE setShowFavoritesOnly NOTIFY showFavoriteOnlyChanged) + Q_PROPERTY(MagicSearchProxy *parentProxy READ getParentProxy WRITE setParentProxy NOTIFY parentProxyChanged) Q_PROPERTY(MagicSearchProxy *hideListProxy READ getHideListProxy WRITE setHideListProxy NOTIFY hideListProxyChanged) - Q_PROPERTY(bool showLdapContacts READ showLdapContacts WRITE setShowLdapContacts CONSTANT) - Q_PROPERTY(bool hideSuggestions READ getHideSuggestions WRITE setHideSuggestions NOTIFY hideSuggestionsChanged) - public: - DECLARE_SORTFILTER_CLASS(bool mShowFavoritesOnly = false; bool mShowLdapContacts = true; - bool mHideSuggestions = false; - MagicSearchProxy *mHideListProxy = nullptr;) + enum class FilteringTypes { None = 0, Favorites = 1, App = 2, Ldap = 4, Other = 8 }; + Q_ENUM(FilteringTypes) + + DECLARE_SORTFILTER_CLASS(MagicSearchProxy *mHideListProxy = nullptr;) MagicSearchProxy(QObject *parent = Q_NULLPTR); ~MagicSearchProxy(); @@ -63,15 +60,6 @@ public: int getMaxResults() const; void setMaxResults(int maxResults); - bool showFavoritesOnly() const; - void setShowFavoritesOnly(bool show); - - bool showLdapContacts() const; - void setShowLdapContacts(bool show); - - bool getHideSuggestions() const; - void setHideSuggestions(bool data); - MagicSearchProxy *getParentProxy() const; void setList(QSharedPointer list); Q_INVOKABLE void setParentProxy(MagicSearchProxy *proxy); @@ -81,6 +69,8 @@ public: // Q_INVOKABLE forceUpdate(); Q_INVOKABLE int findFriendIndexByAddress(const QString &address); + Q_INVOKABLE FriendGui *findFriendByAddress(const QString &address); + Q_INVOKABLE int loadUntil(const QString &address); signals: void searchTextChanged(); @@ -89,8 +79,6 @@ signals: void maxResultsChanged(int maxResults); void forceUpdate(); void localFriendCreated(int index); - void showFavoriteOnlyChanged(); - void hideSuggestionsChanged(); void parentProxyChanged(); void hideListProxyChanged(); void initialized(); diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index d7c3498a4..cafa85155 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -49,6 +49,7 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Control/Display/Contact/Contact.qml view/Control/Display/Contact/ContactListItem.qml view/Control/Display/Contact/ContactListView.qml + view/Control/Display/Contact/AllContactListView.qml view/Control/Display/Contact/Voicemail.qml view/Control/Display/Meeting/MeetingListView.qml view/Control/Display/Participant/ParticipantDeviceListView.qml diff --git a/Linphone/view/Control/Display/Contact/AllContactListView.qml b/Linphone/view/Control/Display/Contact/AllContactListView.qml new file mode 100644 index 000000000..28338a559 --- /dev/null +++ b/Linphone/view/Control/Display/Contact/AllContactListView.qml @@ -0,0 +1,332 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls.Basic as Control + +import Linphone +import UtilsCpp 1.0 +import ConstantsCpp 1.0 +import SettingsCpp + +Flickable{ + id: mainItem + + property bool showInitials: true // Display Initials of Display name. + property bool showDefaultAddress: true // Display address below display name. + property bool showActions: false // Display actions layout (call buttons) + property bool showContactMenu: true // Display the dot menu for contacts. + property bool showFavorites: true // Display the favorites in the header + property bool hideSuggestions: false // Hide not stored contacts (not suggestions) + property string highlightText: searchText // Bold characters in Display name. + property var sourceFlags: LinphoneEnums.MagicSearchSource.All + + property bool displayNameCapitalization: true // Capitalize display name. + + property bool selectionEnabled: true // Contact can be selected + property bool multiSelectionEnabled: false //Multiple items can be selected. + property list selectedContacts // List of default address on selected contacts. + //property FriendGui selectedContact//: model.getAt(currentIndex) || null + property FriendGui highlightedContact + + property bool searchOnEmpty: true + property bool loading: false + property bool pauseSearch: false // true = don't search on text change + + // Model properties + // set searchBarText without specifying a model to bold + // matching names + property string searchBarText + property string searchText// Binding is done on searchBarTextChanged + property ConferenceInfoGui confInfoGui + + property bool haveFavorites: false + property bool haveContacts: count > 0 + property int sectionsPixelSize: 16 * DefaultStyle.dp + property int sectionsWeight: 800 * DefaultStyle.dp + property int sectionsSpacing: 18 * DefaultStyle.dp + + property int itemsRightMargin: 39 * DefaultStyle.dp + property int count: contactsList.count + + signal resultsReceived() + signal contactStarredChanged() + signal contactDeletionRequested(FriendGui contact) + signal contactAddedToSelection(string address) + signal contactRemovedFromSelection(string address) + signal contactSelected(FriendGui contact) + + contentWidth: width + contentHeight: contentsLayout.height + + + function selectContact(address) { + var index = contactsProxy.loadUntil(address)// Be sure to have this address in proxy if it exists + if (index != -1) { + contactsList.selectIndex(index) + } + return index + + } + function addContactToSelection(address) { + if (multiSelectionEnabled) { + var indexInSelection = selectedContacts.indexOf(address) + if (indexInSelection == -1) { + selectedContacts.push(address) + contactAddedToSelection(address) + } + } + } + function removeContactFromSelection(indexInSelection) { + var addressToRemove = selectedContacts[indexInSelection] + if (indexInSelection != -1) { + selectedContacts.splice(indexInSelection, 1) + contactRemovedFromSelection(addressToRemove) + } + } + function removeSelectedContactByAddress(address) { + var index = selectedContacts.indexOf(address) + if (index != -1) { + selectedContacts.splice(index, 1) + contactRemovedFromSelection(address) + } + } + function haveAddress(address){ + var index = magicSearchProxy.findFriendIndexByAddress(address) + return index != -1 + } + + onHighlightedContactChanged:{ + favoritesList.highlightedContact = highlightedContact + contactsList.highlightedContact = highlightedContact + suggestionsList.highlightedContact = highlightedContact + } + + onResultsReceived: { + loading = false + mainItem.contentY = 0 + } + onSearchBarTextChanged: { + if(!pauseSearch && (mainItem.searchOnEmpty || searchBarText != '')) { + searchText = searchBarText.length === 0 ? "*" : searchBarText + } + } + onPauseSearchChanged: { + if(!pauseSearch && (mainItem.searchOnEmpty || searchBarText != '')){ + searchText = searchBarText.length === 0 ? "*" : searchBarText + } + } + onSearchTextChanged: loading = true + onAtYEndChanged: if(atYEnd) { + if( contactsProxy.haveMore || mainItem.hideSuggestions) contactsProxy.displayMore() + else suggestionsProxy.displayMore() + } + function findNextList(item, count, direction){ + if(count == 3) return null + var nextItem + switch(item){ + case suggestionsList:nextItem=(direction > 0 ? favoritesList : contactsList);break; + case contactsList:nextItem=(direction > 0 ? suggestionsList : favoritesList);break; + case favoritesList:nextItem=(direction > 0 ? contactsList : suggestionsList);break; + default: return null + } + if( nextItem.model.count > 0) return nextItem + else return findNextList(nextItem, count+1, direction) + } + function updatePosition(list){ + var item = list.itemAtIndex(list.currentIndex) + var centerPos = list.y - height/2 + if( item){ + // For debugging just in case + //var listPosition = item.mapToItem(favoriteList, item.x, item.y) + //var newPosition = favoriteList.mapToItem(mainItem, listPosition.x, listPosition.y) + //console.log("item pos: " +item.x + " / " +item.y) + //console.log("fav pos: " +favoriteList.x + " / " +favoriteList.y) + //console.log("fav content: " +favoriteList.contentX + " / " +favoriteList.contentY) + //console.log("main pos: " +mainItem.x + " / " +mainItem.y) + //console.log("main content: " +mainItem.contentX + " / " +mainItem.contentY) + //console.log("list pos: " +listPosition.x + " / " +listPosition.y) + //console.log("new pos: " +newPosition.x + " / " +newPosition.y) + //console.log("header pos: " +headerItem.x + " / " +headerItem.y) + //console.log("Moving to " + (headerItem.y+item.y)) + centerPos += item.y + } + mainItem.contentY = Math.max(0, centerPos) + } + Behavior on contentY{ + NumberAnimation { + duration: 500 + easing.type: Easing.OutExpo + } + } + Keys.onPressed: (event)=> { + if(!event.accepted){ + if(event.key == Qt.Key_Up || event.key == Qt.Key_Down){ + var newItem + var direction = (event.key == Qt.Key_Up ? -1 : 1) + if(suggestionsList.activeFocus) newItem = findNextList(suggestionsList, 0, direction) + if(contactsList.activeFocus) newItem = findNextList(contactsList, 0, direction) + if(favoritesList.activeFocus) newItem = findNextList(favoritesList, 0, direction) + + if(newItem){ + newItem.selectIndex(direction > 0 ? -1 : newItem.model.count - 1) + event.accepted = true + } + } + } + } + Component.onCompleted: { + if (confInfoGui) { + for(var i = 0; i < confInfoGui.core.participants.length; ++i) { + selectedContacts.push(confInfoGui.core.getParticipantAddressAt(i)); + } + } + } + + Connections { + target: SettingsCpp + onLdapConfigChanged: { + if (SettingsCpp.syncLdapContacts) + magicSearchProxy.forceUpdate() + } + } + + property MagicSearchProxy mainModel: MagicSearchProxy { + id: magicSearchProxy + searchText: mainItem.searchText + aggregationFlag: LinphoneEnums.MagicSearchAggregation.Friend + sourceFlags: mainItem.sourceFlags + onModelReset: mainItem.resultsReceived() + + onInitialized: { + if(mainItem.searchOnEmpty || searchText != '' ) { + mainItem.loading = true + forceUpdate() + } + } + } + + + Control.ScrollBar.vertical: ScrollBar { + id: scrollbar + rightPadding: 8 * DefaultStyle.dp + topPadding: mainItem.haveFavorites ? 24 * DefaultStyle.dp : 0 // Avoid to be on top of collapse button + + active: true + interactive: true + policy: mainItem.contentHeight > mainItem.height ? Control.ScrollBar.AlwaysOn : Control.ScrollBar.AlwaysOff + } + + + ColumnLayout{ + id: contentsLayout + width: parent.width + + BusyIndicator { + Layout.alignment: Qt.AlignCenter + Layout.preferredHeight: visible ? 60 * DefaultStyle.dp : 0 + Layout.preferredWidth: 60 * DefaultStyle.dp + indicatorHeight: 60 * DefaultStyle.dp + indicatorWidth: 60 * DefaultStyle.dp + visible: mainItem.loading + indicatorColor: DefaultStyle.main1_500_main + } + + ContactListView{ + id: favoritesList + Layout.fillWidth: true + Layout.preferredHeight: implicitHeight + interactive: false + highlightText: mainItem.highlightText + showActions: mainItem.showActions + showInitials: mainItem.showInitials + showContactMenu: mainItem.showContactMenu + showDefaultAddress: mainItem.showDefaultAddress + selectionEnabled: mainItem.selectionEnabled + multiSelectionEnabled: mainItem.multiSelectionEnabled + selectedContacts: mainItem.selectedContacts + title: qsTr('Favoris') + + onHighlightedContactChanged: mainItem.highlightedContact = highlightedContact + onContactSelected: (contactGui) => { + mainItem.contactSelected(contactGui) + } + onUpdatePosition: mainItem.updatePosition(favoritesList) + onContactDeletionRequested: (contact) => {mainItem.contactDeletionRequested(contact)} + onAddContactToSelection: (address) => {mainItem.addContactToSelection(address)} + onRemoveContactFromSelection: (index) => {mainItem.removeContactFromSelection(index)} + + property MagicSearchProxy proxy: MagicSearchProxy { + parentProxy: mainItem.mainModel + filterType: MagicSearchProxy.FilteringTypes.Favorites + } + model : mainItem.showFavorites && (mainItem.searchBarText == ''|| mainItem.searchBarText == '*')? proxy : [] + } + + ContactListView{ + id: contactsList + Layout.fillWidth: true + Layout.preferredHeight: implicitHeight + interactive: false + highlightText: mainItem.highlightText + showActions: mainItem.showActions + showInitials: mainItem.showInitials + showContactMenu: mainItem.showContactMenu + showDefaultAddress: mainItem.showDefaultAddress + selectionEnabled: mainItem.selectionEnabled + multiSelectionEnabled: mainItem.multiSelectionEnabled + selectedContacts: mainItem.selectedContacts + title: qsTr('Contacts') + + onHighlightedContactChanged: mainItem.highlightedContact = highlightedContact + onContactSelected: (contactGui) => { + mainItem.contactSelected(contactGui) + } + onUpdatePosition: mainItem.updatePosition(contactsList) + onContactDeletionRequested: (contact) => {mainItem.contactDeletionRequested(contact)} + onAddContactToSelection: (address) => {mainItem.addContactToSelection(address)} + onRemoveContactFromSelection: (index) => {mainItem.removeContactFromSelection(index)} + + model:MagicSearchProxy { + id: contactsProxy + parentProxy: mainItem.mainModel + filterType: MagicSearchProxy.FilteringTypes.App | (mainItem.searchText != '*' && mainItem.searchText != '' || SettingsCpp.syncLdapContacts ? MagicSearchProxy.FilteringTypes.Ldap : 0) + initialDisplayItems: 20 + displayItemsStep: 5 + onLocalFriendCreated: (index) => { + contactsList.selectIndex(index) + } + } + } + ContactListView{ + id: suggestionsList + Layout.fillWidth: true + Layout.preferredHeight: implicitHeight + interactive: false + showInitials: false + highlightText: mainItem.highlightText + showActions: mainItem.showActions + showContactMenu: mainItem.showContactMenu + showDefaultAddress: mainItem.showDefaultAddress + selectionEnabled: mainItem.selectionEnabled + multiSelectionEnabled: mainItem.multiSelectionEnabled + selectedContacts: mainItem.selectedContacts + title: qsTr('Suggestions') + + onHighlightedContactChanged: mainItem.highlightedContact = highlightedContact + onContactSelected: (contactGui) => { + mainItem.contactSelected(contactGui) + } + onUpdatePosition: mainItem.updatePosition(suggestionsList) + onContactDeletionRequested: (contact) => {mainItem.contactDeletionRequested(contact)} + onAddContactToSelection: (address) => {mainItem.addContactToSelection(address)} + onRemoveContactFromSelection: (index) => {mainItem.removeContactFromSelection(index)} + + model:MagicSearchProxy { + id: suggestionsProxy + parentProxy: mainItem.mainModel + filterType: mainItem.hideSuggestions ? MagicSearchProxy.FilteringTypes.None : MagicSearchProxy.FilteringTypes.Other + initialDisplayItems: 0 + displayItemsStep: 5 + } + } + } +} diff --git a/Linphone/view/Control/Display/Contact/ContactListItem.qml b/Linphone/view/Control/Display/Contact/ContactListItem.qml index cb550758c..99e42c89b 100644 --- a/Linphone/view/Control/Display/Contact/ContactListItem.qml +++ b/Linphone/view/Control/Display/Contact/ContactListItem.qml @@ -8,7 +8,7 @@ import ConstantsCpp 1.0 import SettingsCpp FocusScope { id: mainItem - implicitHeight: 56 * DefaultStyle.dp + implicitHeight: visible ? 56 * DefaultStyle.dp : 0 property var searchResultItem property bool showInitials: true // Display Initials of Display name. property bool showDefaultAddress: true // Display address below display name. @@ -21,10 +21,8 @@ FocusScope { property bool selectionEnabled: true // Contact can be selected property bool multiSelectionEnabled: false //Multiple items can be selected. property list selectedContacts // List of default address on selected contacts. - property int selectedContactCount: selectedContacts.length property bool isSelected: false // selected in list => currentIndex == index - - + property var previousInitial // Use directly previous initial property int itemsRightMargin: 39 * DefaultStyle.dp @@ -32,15 +30,8 @@ FocusScope { property string initial: displayName ? displayName[0].toLocaleLowerCase(ConstantsCpp.DefaultLocale) : '' signal clicked(var mouse) - signal contactStarredChanged() signal contactDeletionRequested(FriendGui contact) - Connections { - enabled: searchResultItem.core - target: searchResultItem.core - function onStarredChanged() { mainItem.contactStarredChanged()} - } - Text { id: initial anchors.left: parent.left @@ -63,7 +54,8 @@ FocusScope { anchors.left: initial.visible ? initial.right : parent.left anchors.right: parent.right anchors.rightMargin: mainItem.itemsRightMargin - anchors.verticalCenter: parent.verticalCenter + anchors.top: parent.top + anchors.bottom: parent.bottom spacing: 16 * DefaultStyle.dp z: 1 Avatar { @@ -106,19 +98,11 @@ FocusScope { spacing: visible ? 16 * DefaultStyle.dp : 0 EffectImage { id: isSelectedCheck - // visible: mainItem.multiSelectionEnabled && (mainItem.confInfoGui.core.getParticipantIndex(searchResultItem.core.defaultAddress) != -1) visible: mainItem.multiSelectionEnabled && (mainItem.selectedContacts.indexOf(searchResultItem.core.defaultAddress) != -1) Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredHeight: 24 * DefaultStyle.dp imageSource: AppIcons.check colorizationColor: DefaultStyle.main1_500_main - Connections { - target: mainItem - // onParticipantsChanged: isSelectedCheck.visible = mainItem.confInfoGui.core.getParticipantIndex(searchResultItem.core.defaultAddress) != -1 - function onSelectedContactCountChanged(){ - isSelectedCheck.visible = (mainItem.selectedContacts.indexOf(searchResultItem.core.defaultAddress) != -1) - } - } } RowLayout{ id: actionButtons diff --git a/Linphone/view/Control/Display/Contact/ContactListView.qml b/Linphone/view/Control/Display/Contact/ContactListView.qml index 3c7fd3ff5..78f8a8fc4 100644 --- a/Linphone/view/Control/Display/Contact/ContactListView.qml +++ b/Linphone/view/Control/Display/Contact/ContactListView.qml @@ -11,6 +11,7 @@ import SettingsCpp ListView { id: mainItem + property string title property bool showInitials: true // Display Initials of Display name. property bool showDefaultAddress: true // Display address below display name. property bool showActions: false // Display actions layout (call buttons) @@ -25,141 +26,73 @@ ListView { property bool selectionEnabled: true // Contact can be selected property bool multiSelectionEnabled: false //Multiple items can be selected. property list selectedContacts // List of default address on selected contacts. - property FriendGui selectedContact//: model.getAt(currentIndex) || null - - property bool searchOnInitialization: false - property bool loading: false - property bool pauseSearch: false // true = don't search on text change + property FriendGui highlightedContact // Model properties // set searchBarText without specifying a model to bold // matching names - property string searchBarText - property string searchText// Binding is done on searchBarTextChanged + property string searchText property ConferenceInfoGui confInfoGui property bool haveFavorites: false - property bool haveContacts: count > 0 || (showFavorites && headerItem.list.count > 0) + property bool haveContacts: count > 0 property int sectionsPixelSize: 16 * DefaultStyle.dp property int sectionsWeight: 800 * DefaultStyle.dp property int sectionsSpacing: 18 * DefaultStyle.dp property int itemsRightMargin: 39 * DefaultStyle.dp + property bool expanded: true signal resultsReceived() - signal contactStarredChanged() signal contactDeletionRequested(FriendGui contact) - signal contactAddedToSelection(string address) - signal contactRemovedFromSelection(string address) - signal contactClicked(FriendGui contact) + signal contactSelected(FriendGui contact) // Click/Space/Enter + signal addContactToSelection(var address) + signal removeContactFromSelection(var indexInSelection) + signal updatePosition() clip: true - highlightFollowsCurrentItem: true + highlightFollowsCurrentItem: false cacheBuffer: 400 - // Binding loop hack - onContentHeightChanged: Qt.callLater(function(){cacheBuffer = Math.max(0,contentHeight)}) - - function selectContact(address) { - var index = magicSearchProxy.findFriendIndexByAddress(address) - if (index != -1) { - mainItem.currentIndex = index - } - return index - } - function addContactToSelection(address) { - if (multiSelectionEnabled) { - var indexInSelection = selectedContacts.indexOf(address) - if (indexInSelection == -1) { - selectedContacts.push(address) - contactAddedToSelection(address) - } - } - } - function removeContactFromSelection(indexInSelection) { - var addressToRemove = selectedContacts[indexInSelection] - if (indexInSelection != -1) { - selectedContacts.splice(indexInSelection, 1) - contactRemovedFromSelection(addressToRemove) - } - } - function removeSelectedContactByAddress(address) { - var index = selectedContacts.indexOf(address) - if (index != -1) { - selectedContacts.splice(index, 1) - contactRemovedFromSelection(address) - } - } - function haveAddress(address){ - var index = magicSearchProxy.findFriendIndexByAddress(address) - return index != -1 - } + implicitHeight: contentHeight + headerItem?.height + spacing: 4 * DefaultStyle.dp + + property bool _moveToIndex: false - onResultsReceived: { - loading = false - mainItem.positionViewAtBeginning() - } - onSearchBarTextChanged: { - loading = true - if(!pauseSearch) { - searchText = searchBarText.length === 0 ? "*" : searchBarText + function selectIndex(index){ + if(mainItem.expanded && index >= 0){ + mainItem.currentIndex = index + var item = itemAtIndex(mainItem.currentIndex) + if(item){// Item is ready and available + mainItem.highlightedContact = item.searchResultItem + item.forceActiveFocus() + updatePosition() + }else{// Move on the next items load. + _moveToIndex = true + } + }else{ + mainItem.currentIndex = -1 + mainItem.highlightedContact = null + if(headerItem) headerItem.forceActiveFocus() } } - onPauseSearchChanged: { - if(!pauseSearch){ - searchText = searchBarText.length === 0 ? "*" : searchBarText - } + onCountChanged: if(_moveToIndex >= 0 && count > mainItem.currentIndex ){ + _moveToIndex = false + selectIndex(mainItem.currentIndex) } - onAtYEndChanged: if(atYEnd) magicSearchProxy.displayMore() + onContactSelected: updatePosition() keyNavigationEnabled: false Keys.onPressed: (event)=> { - if(header.activeFocus) return; if(event.key == Qt.Key_Up || event.key == Qt.Key_Down){ - if (currentIndex == 0 && event.key == Qt.Key_Up) { - if( headerItem.list.count > 0) { - mainItem.highlightFollowsCurrentItem = false - currentIndex = -1 - headerItem.list.currentIndex = headerItem.list.count -1 - var item = headerItem.list.itemAtIndex(headerItem.list.currentIndex) - mainItem.selectedContact = item.searchResultItem - item.forceActiveFocus() - headerItem.updatePosition() - event.accepted = true; - }else{ - mainItem.currentIndex = mainItem.count - 1 - var item = itemAtIndex(mainItem.currentIndex) - mainItem.selectedContact = item.searchResultItem - item.forceActiveFocus() + if(event.key == Qt.Key_Up && !headerItem.activeFocus) { + if(currentIndex >= 0 ) { + selectIndex(mainItem.currentIndex-1) event.accepted = true; } - }else if(currentIndex >= mainItem.count -1 && event.key == Qt.Key_Down){ - if( headerItem.list.count > 0) { - mainItem.highlightFollowsCurrentItem = false - mainItem.currentIndex = -1 - headerItem.list.currentIndex = 0 - var item = headerItem.list.itemAtIndex(headerItem.list.currentIndex) - mainItem.selectedContact = item.searchResultItem - item.forceActiveFocus() - headerItem.updatePosition() - event.accepted = true; - }else{ - mainItem.currentIndex = 0 - var item = itemAtIndex(mainItem.currentIndex) - mainItem.selectedContact = item.searchResultItem - item.forceActiveFocus() + }else if(event.key == Qt.Key_Down && mainItem.expanded){ + if(currentIndex < model.count - 1) { + selectIndex(mainItem.currentIndex+1) event.accepted = true; } - }else if(event.key == Qt.Key_Up){ - mainItem.highlightFollowsCurrentItem = true - var item = itemAtIndex(--mainItem.currentIndex) - mainItem.selectedContact = item.searchResultItem - item.forceActiveFocus() - event.accepted = true; - }else if(event.key == Qt.Key_Down){ - mainItem.highlightFollowsCurrentItem = true - var item = itemAtIndex(++mainItem.currentIndex) - mainItem.selectedContact = item.searchResultItem - item.forceActiveFocus() - event.accepted = true; } } } @@ -178,307 +111,95 @@ ListView { magicSearchProxy.forceUpdate() } } - - Control.ScrollBar.vertical: ScrollBar { - id: scrollbar - rightPadding: 8 * DefaultStyle.dp - topPadding: mainItem.haveFavorites ? 24 * DefaultStyle.dp : 0 // Avoid to be on top of collapse button - - active: true - interactive: true - policy: mainItem.contentHeight > mainItem.height ? Control.ScrollBar.AlwaysOn : Control.ScrollBar.AlwaysOff - } - model: MagicSearchProxy { - id: magicSearchProxy - searchText: mainItem.searchText - aggregationFlag: LinphoneEnums.MagicSearchAggregation.Friend - sourceFlags: mainItem.sourceFlags - - hideSuggestions: mainItem.hideSuggestions - showLdapContacts: mainItem.searchText != '*' && mainItem.searchText != '' || SettingsCpp.syncLdapContacts - initialDisplayItems: 20 - onLocalFriendCreated: (index) => { - var item = itemAtIndex(index) - if(item){ - mainItem.currentIndex = index - mainItem.selectedContact = item.searchResultItem - item.forceActiveFocus() - } - } - onInitialized: { - mainItem.loading = true - magicSearchProxy.forceUpdate() - } - onModelReset: mainItem.resultsReceived() - } - - section.property: "isStored" - //section.criteria: ViewSection.FirstCharacter - section.delegate: Item{ - width: mainItem.width - height: textItem.implicitHeight + sectionsSpacing * 2 - required property bool section - Text { - id: textItem - anchors.fill: parent - text: section ? qsTr("Contacts") : qsTr("Suggestions") - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - font { - pixelSize: sectionsPixelSize - weight: sectionsWeight - } - } - - } header: FocusScope{ - id: headerItem - width: mainItem.width - height: favoritesContents.implicitHeight - property alias list: favoriteList + id: headerItem + width: mainItem.width + height: headerContents.implicitHeight + onActiveFocusChanged: { + if(activeFocus) mainItem.updatePosition() + } - // Hack because changing currentindex change focus. - Timer{ - id: focusDelay - interval: 10 - onTriggered: { - mainItem.highlightFollowsCurrentItem = !headerItem.activeFocus + ColumnLayout { + id: headerContents + width: parent.width + spacing: mainItem.count > 0 ? sectionsSpacing : 0 + Item{// Do not use directly RowLayout : there is an issue where the layout doesn't update on visible + Layout.fillWidth: true + Layout.preferredHeight: mainItem.count > 0 ? headerTitleLayout.implicitHeight : 0 + RowLayout { + id: headerTitleLayout + anchors.fill: parent + spacing: 0 + // Need this because it can stay at 0 on display without manual relayouting (moving position, resize) + visible: mainItem.count > 0 + Text { + text: mainItem.title + font { + pixelSize: sectionsPixelSize + weight: sectionsWeight + } } - } - onActiveFocusChanged:focusDelay.restart() - //--------------------------------------------------- - - function updatePosition(){ - var item = favoriteList.itemAtIndex(favoriteList.currentIndex) - if( item){ - // For debugging just in case - //var listPosition = item.mapToItem(favoriteList, item.x, item.y) - //var newPosition = favoriteList.mapToItem(mainItem, listPosition.x, listPosition.y) - //console.log("item pos: " +item.x + " / " +item.y) - //console.log("fav pos: " +favoriteList.x + " / " +favoriteList.y) - //console.log("fav content: " +favoriteList.contentX + " / " +favoriteList.contentY) - //console.log("main pos: " +mainItem.x + " / " +mainItem.y) - //console.log("main content: " +mainItem.contentX + " / " +mainItem.contentY) - //console.log("list pos: " +listPosition.x + " / " +listPosition.y) - //console.log("new pos: " +newPosition.x + " / " +newPosition.y) - //console.log("header pos: " +headerItem.x + " / " +headerItem.y) - //console.log("Moving to " + (headerItem.y+item.y)) - mainItem.contentY = headerItem.y+item.y - } - - } - - ColumnLayout { - id: favoritesContents - width: parent.width - spacing: mainItem.haveFavorites ? sectionsSpacing : 0 - BusyIndicator { - Layout.alignment: Qt.AlignCenter - Layout.preferredHeight: visible ? 60 * DefaultStyle.dp : 0 - Layout.preferredWidth: 60 * DefaultStyle.dp - indicatorHeight: 60 * DefaultStyle.dp - indicatorWidth: 60 * DefaultStyle.dp - visible: mainItem.loading - indicatorColor: DefaultStyle.main1_500_main - - } - Item{// Do not use directly RowLayout : there is an issue where the layout doesn't update on visible + Item { Layout.fillWidth: true - Layout.preferredHeight: mainItem.haveFavorites ? favoriteTitle.implicitHeight : 0 - RowLayout { - id: favoriteTitle - anchors.fill: parent - spacing: 0 - - // Need this because it can stay at 0 on display without manual relayouting (moving position, resize) - visible: mainItem.haveFavorites - onVisibleChanged: if(visible) { - Qt.callLater(mainItem.positionViewAtBeginning)// If not later, the view will not move to favoris at startup - } - Text { - //Layout.fillHeight: true - text: qsTr("Favoris") - font { - pixelSize: sectionsPixelSize - weight: sectionsWeight - } - } - Item { - Layout.fillWidth: true - } - Button { - id: favoriteExpandButton - background: Item{} - icon.source: favoriteList.visible ? AppIcons.upArrow : AppIcons.downArrow - Layout.fillHeight: true - Layout.preferredWidth: height - //Layout.preferredWidth: 24 * DefaultStyle.dp - //Layout.preferredHeight: 24 * DefaultStyle.dp - Layout.rightMargin: 23 * DefaultStyle.dp - icon.width: 24 * DefaultStyle.dp - icon.height: 24 * DefaultStyle.dp - focus: true - onClicked: favoriteList.visible = !favoriteList.visible - KeyNavigation.down: favoriteList - } - } } - ListView{ - id: favoriteList - Layout.fillWidth: true - Layout.preferredHeight: count > 0 ? contentHeight : 0// Show full and avoid scrolling - - - - onCountChanged: mainItem.haveFavorites = count > 0 - Keys.onPressed: (event)=> { - if(event.key == Qt.Key_Up || event.key == Qt.Key_Down) { - if (favoriteList.currentIndex == 0 && event.key == Qt.Key_Up) { - if( mainItem.count > 0) { - mainItem.highlightFollowsCurrentItem = true - favoriteList.currentIndex = -1 - mainItem.currentIndex = mainItem.count-1 - var item = mainItem.itemAtIndex(mainItem.currentIndex) - mainItem.selectedContact = item.searchResultItem - item.forceActiveFocus() - event.accepted = true; - }else{ - favoriteList.currentIndex = favoriteList.count - 1 - var item = itemAtIndex(favoriteList.currentIndex) - mainItem.selectedContact = item.searchResultItem - item.forceActiveFocus() - event.accepted = true; - } - }else if(currentIndex >= favoriteList.count -1 && event.key == Qt.Key_Down) { - if( mainItem.count > 0) { - mainItem.highlightFollowsCurrentItem = true - favoriteList.currentIndex = -1 - mainItem.currentIndex = 0 - var item = mainItem.itemAtIndex(mainItem.currentIndex) - mainItem.selectedContact = item.searchResultItem - item.forceActiveFocus() - event.accepted = true; - }else{ - favoriteList.currentIndex = 0 - var item = itemAtIndex(favoriteList.currentIndex) - mainItem.selectedContact = item.searchResultItem - item.forceActiveFocus() - event.accepted = true; - } - }else if(event.key == Qt.Key_Up){ - mainItem.highlightFollowsCurrentItem = false - var item = itemAtIndex(--favoriteList.currentIndex) - mainItem.selectedContact = item.searchResultItem - item.forceActiveFocus() - headerItem.updatePosition() - event.accepted = true; - }else if(event.key == Qt.Key_Down){ - mainItem.highlightFollowsCurrentItem = false - var item = itemAtIndex(++favoriteList.currentIndex) - mainItem.selectedContact = item.searchResultItem - item.forceActiveFocus() - headerItem.updatePosition() - event.accepted = true; - } - } - } - property MagicSearchProxy proxy: MagicSearchProxy{ - parentProxy: mainItem.model - showFavoritesOnly: true - hideSuggestions: mainItem.hideSuggestions - } - model : showFavorites && mainItem.searchBarText == '' ? proxy : [] - delegate: ContactListItem{ - width: favoriteList.width - focus: true - - searchResultItem: $modelData - showInitials: mainItem.showInitials - showDefaultAddress: mainItem.showDefaultAddress - showActions: mainItem.showActions - showContactMenu: mainItem.showContactMenu - highlightText: mainItem.highlightText - - displayNameCapitalization: mainItem.displayNameCapitalization - itemsRightMargin: mainItem.itemsRightMargin - selectionEnabled: mainItem.selectionEnabled - multiSelectionEnabled: mainItem.multiSelectionEnabled - selectedContacts: mainItem.selectedContacts - isSelected: mainItem.selectedContact && mainItem.selectedContact.core == searchResultItem.core - previousInitial: ''//favoriteList.count > 0 ? favoriteList.itemAtIndex(index-1)?.initial : '' // Binding on count - initial: '' // Hide initials but keep space - - onIsSelectedChanged: if(isSelected) favoriteList.currentIndex = index - onContactStarredChanged: mainItem.contactStarredChanged() - onContactDeletionRequested: (contact) => mainItem.contactDeletionRequested(contact) - onClicked: (mouse) => { - mainItem.highlightFollowsCurrentItem = false - favoriteList.currentIndex = index - mainItem.selectedContact = searchResultItem - forceActiveFocus() - headerItem.updatePosition() - if (mainItem.multiSelectionEnabled) { - var indexInSelection = mainItem.selectedContacts.indexOf(searchResultItem.core.defaultAddress) - if (indexInSelection == -1) { - mainItem.addContactToSelection(searchResultItem.core.defaultAddress) - } - else { - mainItem.removeContactFromSelection(indexInSelection, 1) - } - } - mainItem.contactClicked(searchResultItem) - } - } + Button { + id: headerExpandButton + background: Item{} + icon.source: mainItem.expanded ? AppIcons.upArrow : AppIcons.downArrow + Layout.fillHeight: true + Layout.preferredWidth: height + Layout.rightMargin: 23 * DefaultStyle.dp + icon.width: 24 * DefaultStyle.dp + icon.height: 24 * DefaultStyle.dp + focus: true + onClicked: mainItem.expanded = !mainItem.expanded } } } + } + } delegate: ContactListItem{ id: contactItem width: mainItem.width focus: true - + visible: mainItem.expanded searchResultItem: $modelData showInitials: mainItem.showInitials && isStored showDefaultAddress: mainItem.showDefaultAddress showActions: mainItem.showActions showContactMenu: searchResultItem.core.isStored - highlightText: mainItem.highlightText - displayNameCapitalization: mainItem.displayNameCapitalization - itemsRightMargin: mainItem.itemsRightMargin selectionEnabled: mainItem.selectionEnabled multiSelectionEnabled: mainItem.multiSelectionEnabled selectedContacts: mainItem.selectedContacts - isSelected: mainItem.selectedContact && mainItem.selectedContact.core == searchResultItem.core + isSelected: mainItem.highlightedContact && mainItem.highlightedContact.core == searchResultItem.core previousInitial: mainItem.itemAtIndex(index-1)?.initial + itemsRightMargin: mainItem.itemsRightMargin onIsSelectedChanged: if(isSelected) mainItem.currentIndex = index - onContactStarredChanged: mainItem.contactStarredChanged() onContactDeletionRequested: (contact) => mainItem.contactDeletionRequested(contact) + onClicked: (mouse) => { - mainItem.highlightFollowsCurrentItem = true if (mouse && mouse.button == Qt.RightButton) { friendPopup.open() } else { - forceActiveFocus() - if(mainItem.selectedContact && mainItem.selectedContact.core != contactItem.searchResultItem.core) - headerItem.list.currentIndex = -1 - mainItem.selectedContact = contactItem.searchResultItem - if (mainItem.multiSelectionEnabled) { - var indexInSelection = mainItem.selectedContacts.indexOf(searchResultItem.core.defaultAddress) - if (indexInSelection == -1) { - mainItem.addContactToSelection(searchResultItem.core.defaultAddress) - } - else { - mainItem.removeContactFromSelection(indexInSelection, 1) - } - } - mainItem.contactClicked(searchResultItem) - } + forceActiveFocus() + mainItem.highlightedContact = contactItem.searchResultItem + if (mainItem.multiSelectionEnabled) { + var indexInSelection = mainItem.selectedContacts.indexOf(searchResultItem.core.defaultAddress) + if (indexInSelection == -1) { + mainItem.addContactToSelection(searchResultItem.core.defaultAddress) + } + else { + mainItem.removeContactFromSelection(indexInSelection) + } + } + mainItem.contactSelected(searchResultItem) + } } } } diff --git a/Linphone/view/Page/Form/Call/NewCallForm.qml b/Linphone/view/Page/Form/Call/NewCallForm.qml index efa1609c2..e57346008 100644 --- a/Linphone/view/Page/Form/Call/NewCallForm.qml +++ b/Linphone/view/Page/Form/Call/NewCallForm.qml @@ -88,7 +88,7 @@ FocusScope { Layout.preferredHeight: 44 * DefaultStyle.dp padding: 0 KeyNavigation.up: searchBar - KeyNavigation.down: contactLoader.item + KeyNavigation.down: contactList onClicked: mainItem.groupCallCreationRequested() background: Rectangle { anchors.fill: parent @@ -127,23 +127,13 @@ FocusScope { } } } - Loader{ - // This is a hack for an incomprehensible behavior on sections title where they doesn't match with their delegate and can be unordered after resetting models. - id: contactLoader + AllContactListView{ + id: contactList Layout.fillWidth: true Layout.fillHeight: true - property string t: searchBar.text - onTChanged: { - contactLoader.active = false - Qt.callLater(function(){contactLoader.active=true}) - } - //------------------------------------------------------------- - sourceComponent: ContactListView{ - id: contactList - searchBarText: searchBar.text - onContactClicked: (contact) => { - mainItem.contactClicked(contact) - } + searchBarText: searchBar.text + onContactSelected: (contact) => { + mainItem.contactClicked(contact) } } } diff --git a/Linphone/view/Page/Form/Meeting/AddParticipantsForm.qml b/Linphone/view/Page/Form/Meeting/AddParticipantsForm.qml index d9739aeda..aa68be658 100644 --- a/Linphone/view/Page/Form/Meeting/AddParticipantsForm.qml +++ b/Linphone/view/Page/Form/Meeting/AddParticipantsForm.qml @@ -10,7 +10,7 @@ FocusScope{ id: mainItem property string placeHolderText: qsTr("Rechercher des contacts") - property list selectedParticipants//: contactLoader.item ? contactLoader.item.selectedContacts + property list selectedParticipants property int selectedParticipantsCount: selectedParticipants.length property ConferenceInfoGui conferenceInfoGui property color searchBarColor: DefaultStyle.grey_100 @@ -32,7 +32,6 @@ FocusScope{ width: mainItem.width model: mainItem.selectedParticipants clip: true - focus: participantList.count > 0 Keys.onPressed: (event) => { if(currentIndex <=0 && event.key == Qt.Key_Up){ nextItemInFocusChain(false).forceActiveFocus() @@ -67,7 +66,7 @@ FocusScope{ icon.height: 24 * DefaultStyle.dp focus: true contentImageColor: DefaultStyle.main1_500_main - onClicked: if(contactLoader.item) contactLoader.item.removeSelectedContactByAddress(modelData) + onClicked: contactList.removeSelectedContactByAddress(modelData) } } } @@ -95,13 +94,13 @@ FocusScope{ KeyNavigation.up: participantList.count > 0 ? participantList : nextItemInFocusChain(false) - KeyNavigation.down: contactLoader.item + KeyNavigation.down: contactList } ColumnLayout { id: content spacing: 15 * DefaultStyle.dp Text { - visible: !contactLoader.item?.loading && contactLoader.item?.count === 0 + visible: !contactList.loading && contactList.count === 0 Layout.alignment: Qt.AlignHCenter Layout.topMargin: 137 * DefaultStyle.dp text: qsTr("Aucun contact%1").arg(searchBar.text.length !== 0 ? " correspondant" : "") @@ -110,33 +109,21 @@ FocusScope{ weight: 800 * DefaultStyle.dp } } - Loader{ - // This is a hack for an incomprehensible behavior on sections title where they doesn't match with their delegate and can be unordered after resetting models. - id: contactLoader + AllContactListView{ + id: contactList Layout.fillWidth: true Layout.fillHeight: true - property string t: searchBar.text - onTChanged: { - contactLoader.active = false - Qt.callLater(function(){contactLoader.active=true}) - } - //------------------------------------------------------------- - sourceComponent: ContactListView{ - id: contactList - Layout.fillWidth: true - Layout.fillHeight: true - itemsRightMargin: 28 * DefaultStyle.dp - multiSelectionEnabled: true - showContactMenu: false - confInfoGui: mainItem.conferenceInfoGui - selectedContacts: mainItem.selectedParticipants - onSelectedContactsChanged: Qt.callLater(function(){mainItem.selectedParticipants = selectedContacts}) - searchBarText: searchBar.text - onContactAddedToSelection: (address) => { - contactList.addContactToSelection(address) - } - onContactRemovedFromSelection: (address) => contactList.removeSelectedContactByAddress(address) + itemsRightMargin: 28 * DefaultStyle.dp + multiSelectionEnabled: true + showContactMenu: false + confInfoGui: mainItem.conferenceInfoGui + selectedContacts: mainItem.selectedParticipants + onSelectedContactsChanged: Qt.callLater(function(){mainItem.selectedParticipants = selectedContacts}) + searchBarText: searchBar.text + onContactAddedToSelection: (address) => { + contactList.addContactToSelection(address) } + onContactRemovedFromSelection: (address) => contactList.removeSelectedContactByAddress(address) } } } diff --git a/Linphone/view/Page/Layout/Main/MainLayout.qml b/Linphone/view/Page/Layout/Main/MainLayout.qml index a3ed8f463..f11bc1993 100644 --- a/Linphone/view/Page/Layout/Main/MainLayout.qml +++ b/Linphone/view/Page/Layout/Main/MainLayout.qml @@ -172,8 +172,8 @@ Item { if (text.length != 0) listPopup.open() else listPopup.close() } - KeyNavigation.down: contactLoader.item?.count > 0 || !contactLoader.item?.footerItem? contactLoader.item : contactLoader.item?.footerItem - KeyNavigation.up: contactLoader.item?.footerItem ? contactLoader.item?.footerItem : contactLoader.item + KeyNavigation.down: contactList //contactLoader.item?.count > 0 || !contactLoader.item?.footerItem? contactLoader.item : contactLoader.item?.footerItem + KeyNavigation.up: contactList//contactLoader.item?.footerItem ? contactLoader.item?.footerItem : contactLoader.item component MagicSearchButton: Button { id: button @@ -197,8 +197,8 @@ Item { id: listPopup width: magicSearchBar.width property int maxHeight: 400 * DefaultStyle.dp - property bool displayScrollbar: contactLoader.item?.contentHeight + topPadding + bottomPadding> maxHeight - height: Math.min(contactLoader.item?.contentHeight + topPadding + bottomPadding, maxHeight) + property bool displayScrollbar: contactList.contentHeight + topPadding + bottomPadding> maxHeight + height: Math.min(contactList.contentHeight + topPadding + bottomPadding, maxHeight) y: magicSearchBar.height // closePolicy: Popup.NoAutoClose topPadding: 20 * DefaultStyle.dp @@ -214,7 +214,7 @@ Item { color: DefaultStyle.grey_0 anchors.fill: parent border.color: DefaultStyle.main1_500_main - border.width: contactLoader.item?.activeFocus ? 2 : 0 + border.width: contactList.activeFocus ? 2 : 0 } MultiEffect { @@ -238,23 +238,10 @@ Item { } } - contentItem: Loader{ - // This is a hack for an incomprehensible behavior on sections title where they doesn't match with their delegate and can be unordered after resetting models. - id: contactLoader - Layout.fillWidth: true - Layout.fillHeight: true - property bool deactivate: false - active: !deactivate && magicSearchBar.text != '' - property string t: magicSearchBar.text - onTChanged: { - contactLoader.deactivate = true - Qt.callLater(function(){contactLoader.deactivate=false}) - } - //------------------------------------------------------------- - sourceComponent: ContactListView { + contentItem: AllContactListView { id: contactList visible: magicSearchBar.text.length != 0 - Layout.preferredHeight: item?.contentHeight + Layout.preferredHeight: contentHeight Layout.fillWidth: true itemsRightMargin: 5 * DefaultStyle.dp //(Actions have already 10 of margin) showInitials: false @@ -263,6 +250,7 @@ Item { showFavorites: false selectionEnabled: false showDefaultAddress: true + searchOnEmpty: false sectionsPixelSize: 13 * DefaultStyle.dp sectionsWeight: 700 * DefaultStyle.dp @@ -270,24 +258,7 @@ Item { Control.ScrollBar.vertical: scrollbar searchBarText: magicSearchBar.text - - Keys.onPressed: (event) => { - if(event.key == Qt.Key_Down){ - if(contactList.currentIndex == contactList.count -1) { - contactList.currentIndex = -1 - contactList.footerItem.forceActiveFocus() - event.accepted = true - } - } else if(event.key == Qt.Key_Up){ - if(contactList.currentIndex <= 0) { - contactList.currentIndex = -1 - contactList.footerItem.forceActiveFocus() - event.accepted = true - } - } - } } - } } } RowLayout { diff --git a/Linphone/view/Page/Main/Contact/ContactPage.qml b/Linphone/view/Page/Main/Contact/ContactPage.qml index cbc685f59..e5beea57c 100644 --- a/Linphone/view/Page/Main/Contact/ContactPage.qml +++ b/Linphone/view/Page/Main/Contact/ContactPage.qml @@ -239,22 +239,21 @@ AbstractMainPage { Qt.callLater(function(){contactLoader.active=true}) } //------------------------------------------------------------- - sourceComponent: ContactListView{ + sourceComponent: AllContactListView{ id: contactList searchBarText: searchBar.text hideSuggestions: true showDefaultAddress: false sourceFlags: LinphoneEnums.MagicSearchSource.Friends | LinphoneEnums.MagicSearchSource.FavoriteFriends | LinphoneEnums.MagicSearchSource.LdapServers - - onSelectedContactChanged: { - mainItem.selectedContact = selectedContact - } + onHighlightedContactChanged: mainItem.selectedContact = highlightedContact onContactDeletionRequested: (contact) => { mainItem.deleteContact(contact) } - onCountChanged: { - if (initialFriendToDisplay.length !== 0) { - if (selectContact(initialFriendToDisplay) != -1) initialFriendToDisplay = "" + onLoadingChanged: { + if(!loading && initialFriendToDisplay.length !== 0) { + Qt.callLater(function(){ + if (selectContact(initialFriendToDisplay) != -1) initialFriendToDisplay = "" + }) } } } diff --git a/external/linphone-sdk b/external/linphone-sdk index 08d52ccdc..4e2af5b43 160000 --- a/external/linphone-sdk +++ b/external/linphone-sdk @@ -1 +1 @@ -Subproject commit 08d52ccdc63c859f14ffa160b954d1f954a5a272 +Subproject commit 4e2af5b435cb7fd6aadc9cd2b7eb45ba547cf5e8