linphone-desktop/Linphone/view/Control/Display/Contact/ContactListView.qml
Alexandre Jörgensen c73eea248f Format QML files
2026-03-10 12:32:35 +01:00

255 lines
8.4 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
import "qrc:/qt/qml/Linphone/view/Style/buttonStyle.js" as ButtonStyle
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 showDisplayName: true // Display name above address.
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 highlightedContact
// Model properties
// set searchBarText without specifying a model to bold
// matching names
property string searchText
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: Utils.getSizeWithScreenRatio(18)
property real itemsRightMargin: Utils.getSizeWithScreenRatio(39)
property bool expanded: true
property real headerHeight: headerItem?.height
signal contactDeletionRequested(FriendGui contact)
signal contactSelected(FriendGui contact) // Click/Space/Enter
signal addContactToSelection(var address)
signal removeContactFromSelection(var indexInSelection)
signal updatePosition
clip: true
highlightFollowsCurrentItem: false
cacheBuffer: 400
implicitHeight: contentHeight
spacing: expanded ? Utils.getSizeWithScreenRatio(4) : 0
onVisibleChanged: if (visible && !expanded)
expanded = true
onYChanged: updatePosition()
// Qt bug: sometimes, containsMouse may not be send and update on each MouseArea.
// So we need to use this variable to switch off all hovered items.
property int lastMouseContainsIndex: -1
property bool _moveToIndex: false
function selectIndex(index, focusReason = Qt.OtherFocusReason) {
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(focusReason);
updatePosition();
_moveToIndex = false;
} else {
// Move on the next items load.
// If visible, try to wait loading
_moveToIndex = visible;
}
} else {
mainItem.currentIndex = -1;
mainItem.highlightedContact = null;
if (headerItem) {
headerItem.forceActiveFocus(focusReason);
}
_moveToIndex = false;
}
}
onContactSelected: updatePosition()
onExpandedChanged: if (!expanded)
updatePosition()
keyNavigationEnabled: false
Keys.onPressed: event => {
if (event.key == Qt.Key_Up || event.key == Qt.Key_Down) {
if (event.key == Qt.Key_Up && !headerItem.activeFocus) {
if (currentIndex >= 0) {
selectIndex(mainItem.currentIndex - 1, Qt.BacktabFocusReason);
event.accepted = true;
}
} else if (event.key == Qt.Key_Down && mainItem.expanded) {
if (currentIndex < model.count - 1) {
selectIndex(mainItem.currentIndex + 1, Qt.TabFocusReason);
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
function onLdapConfigChanged() {
if (SettingsCpp.syncLdapContacts)
magicSearchProxy.forceUpdate();
}
function onCardDAVAddressBookSynchronized() {
console.log("card dav synchro update");
magicSearchProxy.forceUpdate();
}
}
// Workaround: itemAtIndex and count are decorellated and are not enough to know when the ListView has load all its children.
// So when itemAtIndex is not available, start this timer along count changed signal.
Timer {
id: delaySelection
interval: 100
running: _moveToIndex
onTriggered: {
_moveToIndex = false;
if (count > mainItem.currentIndex)
selectIndex(mainItem.currentIndex);
else {
_moveToIndex = true;
}
}
}
header: FocusScope {
id: headerItem
width: mainItem.width
height: headerContents.implicitHeight
ColumnLayout {
id: headerContents
width: parent.width
spacing: 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
Layout.bottomMargin: Utils.getSizeWithScreenRatio(4)
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
}
}
Item {
Layout.fillWidth: true
}
RoundButton {
id: headerExpandButton
style: ButtonStyle.noBackground
icon.source: mainItem.expanded ? AppIcons.upArrow : AppIcons.downArrow
Layout.rightMargin: mainItem.itemsRightMargin
focus: true
onClicked: mainItem.expanded = !mainItem.expanded
Rectangle {
anchors.fill: headerExpandButton
radius: headerExpandButton.width / 2
visible: headerExpandButton.activeFocus
opacity: 0.5
color: DefaultStyle.main2_200
}
Accessible.name: (mainItem.expanded ?
//: Shrink %1
qsTr("shrink_accessible_name") :
//: Expand %1
qsTr("expand_accessible_name")).arg(mainItem.title)
}
}
}
}
}
delegate: ContactListItem {
id: contactItem
width: mainItem.width
focus: true
visible: mainItem.expanded
searchResultItem: $modelData
showInitials: mainItem.showInitials && isStored
showDefaultAddress: mainItem.showDefaultAddress
showDisplayName: mainItem.showDisplayName
showActions: mainItem.showActions
showContactMenu: mainItem.showContactMenu && searchResultItem.core.isStored
highlightText: mainItem.highlightText
displayNameCapitalization: mainItem.displayNameCapitalization
selectionEnabled: mainItem.selectionEnabled
multiSelectionEnabled: mainItem.multiSelectionEnabled
selectedContacts: mainItem.selectedContacts
isSelected: mainItem.highlightedContact && mainItem.highlightedContact.core == searchResultItem.core
isLastHovered: mainItem.lastMouseContainsIndex == index
previousInitial: mainItem.itemAtIndex(index - 1)?.initial
itemsRightMargin: mainItem.itemsRightMargin
onIsSelectedChanged: if (isSelected)
mainItem.currentIndex = index
onContactDeletionRequested: contact => mainItem.contactDeletionRequested(contact)
onClicked: mouse => {
if (mouse && mouse.button == Qt.RightButton) {
friendPopup.open();
} else {
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);
}
}
onContainsMouseChanged: containsMouse => {
if (containsMouse)
mainItem.lastMouseContainsIndex = index;
else if (mainItem.lastMouseContainsIndex == index)
mainItem.lastMouseContainsIndex = -1;
}
}
}