linphone-desktop/Linphone/view/Control/Display/Contact/AllContactListView.qml
Gaelle Braud c4db4d132d only load 20 elements each time in chat messages list to improve perf
fix image error ui
black rectangle on unstarted video

fix text edit loses focus when message sent (#LINQT-2009)

try to fix wrong message spinner icon #LINQT-2010

hide security parameters #LINQT-2019

fix open contextual menu component to open settings/help pages #LINQT-2021/2022

fix muted status not visible on local preview #LINQT-2023
2025-10-07 16:05:44 +02:00

409 lines
16 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
import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils
Flickable {
id: mainItem
flickableDirection: Flickable.VerticalFlick
property bool showInitials: true // Display Initials of 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 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 real sectionsPixelSize: Typography.h4.pixelSize
property real sectionsWeight: Typography.h4.weight
property real sectionsSpacing: Math.round(18 * DefaultStyle.dp)
property real busyIndicatorSize: Math.round(60 * DefaultStyle.dp)
property real itemsRightMargin: Math.round(39 * DefaultStyle.dp)
property int count: contactsList.count + suggestionsList.count + favoritesList.count
contentHeight: contentsLayout.height
rightMargin: itemsRightMargin
signal contactStarredChanged
signal contactDeletionRequested(FriendGui contact)
signal contactAddedToSelection(string address)
signal contactRemovedFromSelection(string address)
signal contactSelected(FriendGui contact)
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
}
function resetSelections() {
mainItem.highlightedContact = null
favoritesList.currentIndex = -1
contactsList.currentIndex = -1
suggestionsList.currentIndex = -1
}
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) {
Utils.updatePosition(mainItem, list)
}
onHighlightedContactChanged: {
favoritesList.highlightedContact = highlightedContact
contactsList.highlightedContact = highlightedContact
suggestionsList.highlightedContact = highlightedContact
}
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
}
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)
else if (contactsList.activeFocus)
newItem = findNextList(contactsList, 0,
direction)
else if (favoritesList.activeFocus)
newItem = findNextList(favoritesList, 0,
direction)
else
newItem = findNextList(suggestionsList, 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.resetSelections()
}
onResultsProcessed: {
mainItem.loading = false
mainItem.contentY = 0
}
onInitialized: {
if (mainItem.searchOnEmpty || searchText != '') {
mainItem.loading = true
forceUpdate()
}
}
}
onAtYEndChanged: if (atYEnd) {
if (favoritesProxy.haveMore && favoritesList.expanded && mainItem.showFavorites)
favoritesProxy.displayMore()
else if (contactsProxy.haveMore && contactsList.expanded) {
contactsProxy.displayMore()
}
else
suggestionsProxy.displayMore()
}
Behavior on contentY {
NumberAnimation {
duration: 500
easing.type: Easing.OutExpo
}
}
Control.ScrollBar.vertical: ScrollBar {
id: scrollbar
z: 1
topPadding: Math.round(24 * DefaultStyle.dp) // Avoid to be on top of collapse button
active: true
interactive: true
visible: mainItem.contentHeight > mainItem.height
policy: Control.ScrollBar.AsNeeded
}
ColumnLayout {
id: contentsLayout
width: mainItem.width
spacing: 0 //Math.round(20 * DefaultStyle.dp)
BusyIndicator {
id: busyIndicator
visible: mainItem.loading
width: mainItem.busyIndicatorSize
height: mainItem.busyIndicatorSize
Layout.preferredWidth: mainItem.busyIndicatorSize
Layout.preferredHeight: mainItem.busyIndicatorSize
Layout.alignment: Qt.AlignCenter | Qt.AlignVCenter
}
ContactListView {
id: favoritesList
visible: contentHeight > 0
Layout.fillWidth: true
Layout.preferredHeight: implicitHeight
sectionsWeight: mainItem.sectionsWeight
sectionsPixelSize: mainItem.sectionsPixelSize
interactive: false
highlightText: mainItem.highlightText
showActions: mainItem.showActions
showInitials: mainItem.showInitials
showContactMenu: mainItem.showContactMenu
showDefaultAddress: false
selectionEnabled: mainItem.selectionEnabled
multiSelectionEnabled: mainItem.multiSelectionEnabled
selectedContacts: mainItem.selectedContacts
//: "Favoris"
title: qsTr("car_favorites_contacts_title")
itemsRightMargin: mainItem.itemsRightMargin
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 {
id: favoritesProxy
parentProxy: mainItem.mainModel
filterType: MagicSearchProxy.FilteringTypes.Favorites
}
model: mainItem.showFavorites
&& (mainItem.searchBarText == ''
|| mainItem.searchBarText == '*') ? proxy : []
}
ContactListView {
id: contactsList
visible: contentHeight > 0
Layout.fillWidth: true
Layout.preferredHeight: implicitHeight
Layout.topMargin: favoritesList.height > 0 ? Math.round(4 * DefaultStyle.dp) : 0
interactive: false
highlightText: mainItem.highlightText
showActions: mainItem.showActions
showInitials: mainItem.showInitials
showContactMenu: mainItem.showContactMenu
showDefaultAddress: false
selectionEnabled: mainItem.selectionEnabled
multiSelectionEnabled: mainItem.multiSelectionEnabled
selectedContacts: mainItem.selectedContacts
itemsRightMargin: mainItem.itemsRightMargin
//: 'Contacts'
title: qsTr("generic_address_picker_contacts_list_title")
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 | MagicSearchProxy.FilteringTypes.CardDAV : 0)
initialDisplayItems: Math.max(
20,
2 * mainItem.height / (Math.round(63 * DefaultStyle.dp)))
displayItemsStep: 3 * initialDisplayItems / 2
onLocalFriendCreated: index => {
contactsList.selectIndex(index)
}
}
}
ContactListView {
id: suggestionsList
visible: contentHeight > 0
Layout.fillWidth: true
Layout.preferredHeight: implicitHeight
Layout.topMargin: contactsList.height + favoritesList.height
> 0 ? Math.round(4 * DefaultStyle.dp) : 0
interactive: false
showInitials: false
highlightText: mainItem.highlightText
showActions: mainItem.showActions
showContactMenu: mainItem.showContactMenu
showDefaultAddress: true
showDisplayName: false
selectionEnabled: mainItem.selectionEnabled
multiSelectionEnabled: mainItem.multiSelectionEnabled
selectedContacts: mainItem.selectedContacts
//: "Suggestions"
title: qsTr("generic_address_picker_suggestions_list_title")
itemsRightMargin: mainItem.itemsRightMargin
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: contactsProxy.haveMore
&& contactsList.expanded ? 0 : Math.max(
20,
2 * mainItem.height
/ (Math.round(63 * DefaultStyle.dp)))
onInitialDisplayItemsChanged: maxDisplayItems = initialDisplayItems
displayItemsStep: 3 * initialDisplayItems / 2
onModelReset: maxDisplayItems = initialDisplayItems
}
}
}
}