- store search parameters into Core. - add search limitation to avoid 300 useless items. - retrieve old parameters on proxy when changing list. - store parent proxy to avoid MOC warnings. Fix contacts search views: - add a loading state for buzy indicators. - limit results on suggestions. - avoid to create MagicSearchProxy if not needed. - add a status to know if friend is stored or not. - propagate invalidateFilter. - delay search while typing. Fix margins and participants selection. Do not search contacts when contact panel is not shown. Avoid search on empty magicbar. Avoid repeating section on object that disappeared from cache. Focus on new contact after creation. Avoid changing maxresult if not needed. Redirect only if friend is not ldap Fix empty display name on making favorite a ldap contact. Fix focus and positions on favorites.
481 lines
17 KiB
QML
481 lines
17 KiB
QML
import QtQuick
|
|
import QtQuick.Layouts
|
|
import QtQuick.Controls.Basic as Control
|
|
|
|
import Linphone
|
|
import UtilsCpp 1.0
|
|
import ConstantsCpp 1.0
|
|
import SettingsCpp
|
|
|
|
|
|
ListView {
|
|
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 // 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<string> 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
|
|
|
|
// 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 int sectionsPixelSize: 16 * DefaultStyle.dp
|
|
property int sectionsWeight: 800 * DefaultStyle.dp
|
|
property int sectionsSpacing: 18 * DefaultStyle.dp
|
|
|
|
property int itemsRightMargin: 39 * DefaultStyle.dp
|
|
|
|
signal resultsReceived()
|
|
signal contactStarredChanged()
|
|
signal contactDeletionRequested(FriendGui contact)
|
|
signal contactAddedToSelection(string address)
|
|
signal contactRemovedFromSelection(string address)
|
|
signal contactClicked(FriendGui contact)
|
|
|
|
clip: true
|
|
highlightFollowsCurrentItem: true
|
|
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
|
|
}
|
|
|
|
onResultsReceived: {
|
|
loading = false
|
|
mainItem.positionViewAtBeginning()
|
|
}
|
|
onSearchBarTextChanged: {
|
|
loading = true
|
|
if(!pauseSearch) {
|
|
searchText = searchBarText.length === 0 ? "*" : searchBarText
|
|
}
|
|
}
|
|
onPauseSearchChanged: {
|
|
if(!pauseSearch){
|
|
searchText = searchBarText.length === 0 ? "*" : searchBarText
|
|
}
|
|
}
|
|
onAtYEndChanged: if(atYEnd) magicSearchProxy.displayMore()
|
|
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()
|
|
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()
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
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()
|
|
}
|
|
}
|
|
|
|
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
|
|
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
|
|
|
|
// Hack because changing currentindex change focus.
|
|
Timer{
|
|
id: focusDelay
|
|
interval: 10
|
|
onTriggered: {
|
|
mainItem.highlightFollowsCurrentItem = !headerItem.activeFocus
|
|
}
|
|
}
|
|
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
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
delegate: ContactListItem{
|
|
id: contactItem
|
|
width: mainItem.width
|
|
focus: true
|
|
|
|
searchResultItem: $modelData
|
|
showInitials: mainItem.showInitials && searchResultItem.core.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
|
|
previousInitial: mainItem.itemAtIndex(index-1)?.initial
|
|
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
}
|