484 lines
17 KiB
QML
484 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: 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<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 bool haveContacts: count > 0 || (showFavorites && headerItem.list.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
|
|
|
|
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
|
|
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
|
|
|
|
// 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 && 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)
|
|
}
|
|
}
|
|
}
|
|
}
|