asynchronous actions buttons to improve performances

This commit is contained in:
gaelle 2025-02-25 15:55:34 +01:00
parent 586dca5cd0
commit 3629732fde
5 changed files with 1298 additions and 1061 deletions

View file

@ -2,162 +2,191 @@ import QtQuick
import QtQuick.Controls.Basic as Control import QtQuick.Controls.Basic as Control
import QtQuick.Effects import QtQuick.Effects
import Linphone import Linphone
import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle import "qrc:/qt/qml/Linphone/view/Style/buttonStyle.js" as ButtonStyle
Button { Button {
id: mainItem id: mainItem
property alias popup: popup property alias popup: popup
property bool shadowEnabled: mainItem.activeFocus || hovered property bool shadowEnabled: mainItem.activeFocus || hovered
property alias popupBackgroundColor: popupBackground.color property alias popupBackgroundColor: popupBackground.color
property color backgroundColor: checked property color backgroundColor: checked ? pressedColor : hovered ? hoveredColor : color
? pressedColor style: ButtonStyle.popupButton
: hovered checked: popup.visible
? hoveredColor implicitWidth: 24 * DefaultStyle.dp
: color implicitHeight: 24 * DefaultStyle.dp
style: ButtonStyle.popupButton width: 24 * DefaultStyle.dp
checked: popup.visible height: 24 * DefaultStyle.dp
implicitWidth: 24 * DefaultStyle.dp leftPadding: 0
implicitHeight: 24 * DefaultStyle.dp rightPadding: 0
width: 24 * DefaultStyle.dp topPadding: 0
height: 24 * DefaultStyle.dp bottomPadding: 0
leftPadding: 0 icon.source: AppIcons.verticalDots
rightPadding: 0 icon.width: 24 * DefaultStyle.dp
topPadding: 0 icon.height: 24 * DefaultStyle.dp
bottomPadding: 0 function close() {
icon.source: AppIcons.verticalDots popup.close()
icon.width: 24 * DefaultStyle.dp }
icon.height: 24 * DefaultStyle.dp function open() {
function close() { popup.open()
popup.close() }
}
function open() {
popup.open()
}
function isFocusable(item){
return item.activeFocusOnTab
}
function getPreviousItem(index){
return _getPreviousItem(popup.contentItem instanceof FocusScope ? popup.contentItem.children[0] : popup.contentItem, index)
}
function getNextItem(index){
return _getNextItem(popup.contentItem instanceof FocusScope ? popup.contentItem.children[0] : popup.contentItem, index)
}
function _getPreviousItem(content, index){
if(content.visibleChildren.length == 0) return null
--index
while(index >= 0){
if( isFocusable(content.children[index]) && content.children[index].visible) return content.children[index]
--index
}
return _getPreviousItem(content, content.children.length)
}
function _getNextItem(content, index){
++index
while(index < content.children.length){
if( isFocusable(content.children[index]) && content.children[index].visible) return content.children[index]
++index
}
return _getNextItem(content, -1)
}
Keys.onPressed: (event) => { function isFocusable(item) {
if(mainItem.checked){ return item.activeFocusOnTab
if( event.key == Qt.Key_Escape || event.key == Qt.Key_Left || event.key == Qt.Key_Space){ }
mainItem.close() function getPreviousItem(index) {
mainItem.forceActiveFocus() return _getPreviousItem(
event.accepted = true popup.contentItem
}else if(event.key == Qt.Key_Up){ instanceof FocusScope ? popup.contentItem.children[0] : popup.contentItem,
getPreviousItem(0).forceActiveFocus() index)
event.accepted = true }
}else if(event.key == Qt.Key_Tab || event.key == Qt.Key_Down){ function getNextItem(index) {
getNextItem(-1).forceActiveFocus() return _getNextItem(
event.accepted = true popup.contentItem
} instanceof FocusScope ? popup.contentItem.children[0] : popup.contentItem,
}else if(event.key == Qt.Key_Space){ index)
mainItem.open() }
event.accepted = true
}
}
background: Item {
anchors.fill: mainItem
Rectangle {
id: buttonBackground
anchors.fill: parent
color: mainItem.backgroundColor
radius: 40 * DefaultStyle.dp
}
MultiEffect {
enabled: mainItem.shadowEnabled
anchors.fill: buttonBackground
source: buttonBackground
visible: mainItem.shadowEnabled
// Crash : https://bugreports.qt.io/browse/QTBUG-124730
shadowEnabled: true //mainItem.shadowEnabled
shadowColor: DefaultStyle.grey_1000
shadowBlur: 0.1
shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0
}
}
contentItem: EffectImage {
imageSource: mainItem.icon.source
imageWidth: mainItem.icon.width
imageHeight: mainItem.icon.height
colorizationColor: mainItem.contentImageColor
}
onPressed: {
if (popup.visible) popup.close()
else popup.open()
}
Control.Popup {
id: popup
x: 0
y: mainItem.height
closePolicy: Popup.CloseOnPressOutsideParent | Popup.CloseOnPressOutside | Popup.CloseOnEscape
padding: 10 * DefaultStyle.dp
parent: mainItem // Explicit define for coordinates references.
function updatePosition(){
if (!visible) return
var popupHeight = popup.height + popup.padding
var popupWidth = popup.width + popup.padding
var winPosition = mainItem.Window.contentItem ? mainItem.Window.contentItem.mapToItem(mainItem,0 , 0) : {x:0,y:0}
// Stay inside main window
y = Math.max( Math.min( winPosition.y + mainItem.Window.height - popupHeight, mainItem.height), winPosition.y)
x = Math.max( Math.min( winPosition.x + mainItem.Window.width - popupWidth, 0), winPosition.x)
// Avoid overlapping with popup button by going to the right (todo: check if left is better?)
if( y < mainItem.height && y + popupHeight > 0){
x += mainItem.width
}
}
onHeightChanged: Qt.callLater(updatePosition)
onWidthChanged: Qt.callLater(updatePosition)
onVisibleChanged: Qt.callLater(updatePosition)
Connections{
target: mainItem.Window
function onHeightChanged(){ Qt.callLater(popup.updatePosition)}
function onWidthChanged(){ Qt.callLater(popup.updatePosition)}
}
background: Item { function _getPreviousItem(content, index) {
anchors.fill: parent if (content.visibleChildren.length == 0)
Rectangle { return null
id: popupBackground --index
anchors.fill: parent while (index >= 0) {
color: DefaultStyle.grey_0 if (isFocusable(content.children[index])
radius: 16 * DefaultStyle.dp && content.children[index].visible)
} return content.children[index]
MultiEffect { --index
source: popupBackground }
anchors.fill: popupBackground return _getPreviousItem(content, content.children.length)
shadowEnabled: true }
shadowBlur: 0.1 function _getNextItem(content, index) {
shadowColor: DefaultStyle.grey_1000 ++index
shadowOpacity: 0.4 while (index < content.children.length) {
} if (isFocusable(content.children[index])
} && content.children[index].visible)
} return content.children[index]
++index
}
return _getNextItem(content, -1)
}
Keys.onPressed: event => {
if (mainItem.checked) {
if (event.key == Qt.Key_Escape
|| event.key == Qt.Key_Left
|| event.key == Qt.Key_Space) {
mainItem.close()
mainItem.forceActiveFocus()
event.accepted = true
} else if (event.key == Qt.Key_Up) {
getPreviousItem(0).forceActiveFocus()
event.accepted = true
} else if (event.key == Qt.Key_Tab
|| event.key == Qt.Key_Down) {
getNextItem(-1).forceActiveFocus()
event.accepted = true
}
} else if (event.key == Qt.Key_Space) {
mainItem.open()
event.accepted = true
}
}
background: Item {
anchors.fill: mainItem
Rectangle {
id: buttonBackground
anchors.fill: parent
color: mainItem.backgroundColor
radius: 40 * DefaultStyle.dp
}
MultiEffect {
enabled: mainItem.shadowEnabled
anchors.fill: buttonBackground
source: buttonBackground
visible: mainItem.shadowEnabled
// Crash : https://bugreports.qt.io/browse/QTBUG-124730
shadowEnabled: true //mainItem.shadowEnabled
shadowColor: DefaultStyle.grey_1000
shadowBlur: 0.1
shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0
}
}
contentItem: EffectImage {
imageSource: mainItem.icon.source
imageWidth: mainItem.icon.width
imageHeight: mainItem.icon.height
colorizationColor: mainItem.contentImageColor
}
onPressed: {
if (popup.visible)
popup.close()
else
popup.open()
}
Control.Popup {
id: popup
x: 0
y: mainItem.height
visible: false
closePolicy: Popup.CloseOnPressOutsideParent | Popup.CloseOnPressOutside
| Popup.CloseOnEscape
padding: 10 * DefaultStyle.dp
parent: mainItem // Explicit define for coordinates references.
function updatePosition() {
if (!visible)
return
var popupHeight = popup.height + popup.padding
var popupWidth = popup.width + popup.padding
var winPosition = mainItem.Window.contentItem ? mainItem.Window.contentItem.mapToItem(
mainItem, 0,
0) : {
"x": 0,
"y": 0
}
// Stay inside main window
y = Math.max(Math.min(
winPosition.y + mainItem.Window.height - popupHeight,
mainItem.height), winPosition.y)
x = Math.max(
Math.min(
winPosition.x + mainItem.Window.width - popupWidth,
0), winPosition.x)
// Avoid overlapping with popup button by going to the right (todo: check if left is better?)
if (y < mainItem.height && y + popupHeight > 0) {
x += mainItem.width
}
}
onHeightChanged: Qt.callLater(updatePosition)
onWidthChanged: Qt.callLater(updatePosition)
onVisibleChanged: Qt.callLater(updatePosition)
Connections {
target: mainItem.Window
function onHeightChanged() {
Qt.callLater(popup.updatePosition)
}
function onWidthChanged() {
Qt.callLater(popup.updatePosition)
}
}
background: Item {
anchors.fill: parent
Rectangle {
id: popupBackground
anchors.fill: parent
color: DefaultStyle.grey_0
radius: 16 * DefaultStyle.dp
}
MultiEffect {
source: popupBackground
anchors.fill: popupBackground
shadowEnabled: true
shadowBlur: 0.1
shadowColor: DefaultStyle.grey_1000
shadowOpacity: 0.4
}
}
}
} }

View file

@ -5,8 +5,8 @@ import QtQuick.Controls.Basic as Control
import Linphone import Linphone
import UtilsCpp import UtilsCpp
import SettingsCpp import SettingsCpp
import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle import "qrc:/qt/qml/Linphone/view/Style/buttonStyle.js" as ButtonStyle
import 'qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js' as Utils import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils
ListView { ListView {
id: mainItem id: mainItem
@ -17,7 +17,7 @@ ListView {
property string searchText: searchBar?.text property string searchText: searchBar?.text
property double busyIndicatorSize: 60 * DefaultStyle.dp property double busyIndicatorSize: 60 * DefaultStyle.dp
signal resultsReceived() signal resultsReceived
onResultsReceived: { onResultsReceived: {
loading = false loading = false
@ -29,7 +29,9 @@ ListView {
id: callHistoryProxy id: callHistoryProxy
filterText: mainItem.searchText filterText: mainItem.searchText
onFilterTextChanged: maxDisplayItems = initialDisplayItems onFilterTextChanged: maxDisplayItems = initialDisplayItems
initialDisplayItems: Math.max(20, 2 * mainItem.height / (56 * DefaultStyle.dp)) initialDisplayItems: Math.max(
20,
2 * mainItem.height / (56 * DefaultStyle.dp))
displayItemsStep: 3 * initialDisplayItems / 2 displayItemsStep: 3 * initialDisplayItems / 2
onModelReset: { onModelReset: {
mainItem.resultsReceived() mainItem.resultsReceived()
@ -37,28 +39,32 @@ ListView {
} }
flickDeceleration: 10000 flickDeceleration: 10000
spacing: 10 * DefaultStyle.dp spacing: 10 * DefaultStyle.dp
Keys.onPressed: (event) => {
if(event.key == Qt.Key_Escape){
console.log("Back")
searchBar.forceActiveFocus()
event.accepted = true
}
}
Component.onCompleted: cacheBuffer = Math.max(contentHeight,0)//contentHeight>0 ? contentHeight : 0// cache all items Keys.onPressed: event => {
if (event.key == Qt.Key_Escape) {
console.log("Back")
searchBar.forceActiveFocus()
event.accepted = true
}
}
Component.onCompleted: cacheBuffer = Math.max(
contentHeight,
0) //contentHeight>0 ? contentHeight : 0// cache all items
// remove binding loop // remove binding loop
onContentHeightChanged: Qt.callLater(function(){ onContentHeightChanged: Qt.callLater(function () {
if (mainItem) mainItem.cacheBuffer = Math?.max(contentHeight,0) || 0 if (mainItem)
mainItem.cacheBuffer = Math?.max(contentHeight, 0) || 0
}) })
onActiveFocusChanged: if(activeFocus && currentIndex < 0 && count > 0) currentIndex = 0 onActiveFocusChanged: if (activeFocus && currentIndex < 0 && count > 0)
currentIndex = 0
onCountChanged: { onCountChanged: {
if(currentIndex < 0 && count > 0){ if (currentIndex < 0 && count > 0) {
mainItem.currentIndex = 0 // Select first item after loading model mainItem.currentIndex = 0 // Select first item after loading model
} }
if(atYBeginning) if (atYBeginning)
positionViewAtBeginning()// Stay at beginning positionViewAtBeginning() // Stay at beginning
} }
Connections { Connections {
target: deleteHistoryPopup target: deleteHistoryPopup
@ -68,13 +74,13 @@ ListView {
} }
onAtYEndChanged: { onAtYEndChanged: {
if(atYEnd && count > 0){ if (atYEnd && count > 0) {
callHistoryProxy.displayMore() callHistoryProxy.displayMore()
} }
} }
//---------------------------------------------------------------- //----------------------------------------------------------------
function moveToCurrentItem(){ function moveToCurrentItem() {
if( mainItem.currentIndex >= 0) if (mainItem.currentIndex >= 0)
Utils.updatePosition(mainItem, mainItem) Utils.updatePosition(mainItem, mainItem)
} }
onCurrentItemChanged: { onCurrentItemChanged: {
@ -82,32 +88,32 @@ ListView {
} }
// Update position only if we are moving to current item and its position is changing. // Update position only if we are moving to current item and its position is changing.
property var _currentItemY: currentItem?.y property var _currentItemY: currentItem?.y
on_CurrentItemYChanged: if(_currentItemY && moveAnimation.running){ on_CurrentItemYChanged: if (_currentItemY && moveAnimation.running) {
moveToCurrentItem() moveToCurrentItem()
} }
Behavior on contentY{ Behavior on contentY {
NumberAnimation { NumberAnimation {
id: moveAnimation id: moveAnimation
duration: 500 duration: 500
easing.type: Easing.OutExpo easing.type: Easing.OutExpo
alwaysRunToEnd: true alwaysRunToEnd: true
} }
} }
//---------------------------------------------------------------- //----------------------------------------------------------------
onVisibleChanged: { onVisibleChanged: {
if (!visible) currentIndex = -1 if (!visible)
currentIndex = -1
} }
// Qt bug: sometimes, containsMouse may not be send and update on each MouseArea. // 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. // So we need to use this variable to switch off all hovered items.
property int lastMouseContainsIndex: -1 property int lastMouseContainsIndex: -1
delegate: FocusScope { delegate: FocusScope {
width:mainItem.width width: mainItem.width
height: 56 * DefaultStyle.dp height: 56 * DefaultStyle.dp
visible: !!modelData visible: !!modelData
RowLayout { RowLayout {
z: 1 z: 1
anchors.fill: parent anchors.fill: parent
@ -115,7 +121,8 @@ ListView {
spacing: 10 * DefaultStyle.dp spacing: 10 * DefaultStyle.dp
Avatar { Avatar {
id: historyAvatar id: historyAvatar
property var contactObj: UtilsCpp.findFriendByAddress(modelData.core.remoteAddress) property var contactObj: UtilsCpp.findFriendByAddress(
modelData.core.remoteAddress)
contact: contactObj?.value || null contact: contactObj?.value || null
displayNameVal: modelData.core.displayName displayNameVal: modelData.core.displayName
secured: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified secured: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified
@ -144,32 +151,32 @@ ListView {
EffectImage { EffectImage {
id: statusIcon id: statusIcon
imageSource: modelData.core.status === LinphoneEnums.CallStatus.Declined imageSource: modelData.core.status === LinphoneEnums.CallStatus.Declined
|| modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere || modelData.core.status
|| modelData.core.status === LinphoneEnums.CallStatus.Aborted === LinphoneEnums.CallStatus.DeclinedElsewhere
|| modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted || modelData.core.status === LinphoneEnums.CallStatus.Aborted
? AppIcons.arrowElbow || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted ? AppIcons.arrowElbow : modelData.core.isOutgoing ? AppIcons.arrowUpRight : AppIcons.arrowDownLeft
: modelData.core.isOutgoing colorizationColor: modelData.core.status
? AppIcons.arrowUpRight === LinphoneEnums.CallStatus.Declined
: AppIcons.arrowDownLeft || modelData.core.status
colorizationColor: modelData.core.status === LinphoneEnums.CallStatus.Declined === LinphoneEnums.CallStatus.DeclinedElsewhere
|| modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere || modelData.core.status
|| modelData.core.status === LinphoneEnums.CallStatus.Aborted === LinphoneEnums.CallStatus.Aborted
|| modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted || modelData.core.status
|| modelData.core.status === LinphoneEnums.CallStatus.Missed === LinphoneEnums.CallStatus.EarlyAborted
? DefaultStyle.danger_500main || modelData.core.status === LinphoneEnums.CallStatus.Missed ? DefaultStyle.danger_500main : modelData.core.isOutgoing ? DefaultStyle.info_500_main : DefaultStyle.success_500main
: modelData.core.isOutgoing
? DefaultStyle.info_500_main
: DefaultStyle.success_500main
Layout.preferredWidth: 12 * DefaultStyle.dp Layout.preferredWidth: 12 * DefaultStyle.dp
Layout.preferredHeight: 12 * DefaultStyle.dp Layout.preferredHeight: 12 * DefaultStyle.dp
transform: Rotation { transform: Rotation {
angle: modelData.core.isOutgoing && (modelData.core.status === LinphoneEnums.CallStatus.Declined angle: modelData.core.isOutgoing
|| modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere && (modelData.core.status === LinphoneEnums.CallStatus.Declined
|| modelData.core.status === LinphoneEnums.CallStatus.Aborted || modelData.core.status
|| modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted) ? 180 : 0 === LinphoneEnums.CallStatus.DeclinedElsewhere
|| modelData.core.status === LinphoneEnums.CallStatus.Aborted
|| modelData.core.status
=== LinphoneEnums.CallStatus.EarlyAborted) ? 180 : 0
origin { origin {
x: statusIcon.width/2 x: statusIcon.width / 2
y: statusIcon.height/2 y: statusIcon.height / 2
} }
} }
} }
@ -191,10 +198,10 @@ ListView {
onClicked: { onClicked: {
if (modelData.core.isConference) { if (modelData.core.isConference) {
var callsWindow = UtilsCpp.getCallsWindow() var callsWindow = UtilsCpp.getCallsWindow()
callsWindow.setupConference(modelData.core.conferenceInfo) callsWindow.setupConference(
modelData.core.conferenceInfo)
UtilsCpp.smartShowWindow(callsWindow) UtilsCpp.smartShowWindow(callsWindow)
} } else {
else {
UtilsCpp.createCall(modelData.core.remoteAddress) UtilsCpp.createCall(modelData.core.remoteAddress)
} }
} }
@ -205,17 +212,19 @@ ListView {
anchors.fill: parent anchors.fill: parent
focus: true focus: true
onContainsMouseChanged: { onContainsMouseChanged: {
if(containsMouse) if (containsMouse)
mainItem.lastMouseContainsIndex = index mainItem.lastMouseContainsIndex = index
else if( mainItem.lastMouseContainsIndex == index) else if (mainItem.lastMouseContainsIndex == index)
mainItem.lastMouseContainsIndex = -1 mainItem.lastMouseContainsIndex = -1
} }
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
opacity: 0.7 opacity: 0.7
radius: 8 * DefaultStyle.dp radius: 8 * DefaultStyle.dp
color: mainItem.currentIndex === index ? DefaultStyle.main2_200 : DefaultStyle.main2_100 color: mainItem.currentIndex
visible: mainItem.lastMouseContainsIndex === index || mainItem.currentIndex === index === index ? DefaultStyle.main2_200 : DefaultStyle.main2_100
visible: mainItem.lastMouseContainsIndex === index
|| mainItem.currentIndex === index
} }
onPressed: { onPressed: {
mainItem.currentIndex = model.index mainItem.currentIndex = model.index

View file

@ -6,27 +6,28 @@ import Linphone
import UtilsCpp 1.0 import UtilsCpp 1.0
import ConstantsCpp 1.0 import ConstantsCpp 1.0
import SettingsCpp import SettingsCpp
import 'qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js' as Utils import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils
Flickable { Flickable {
id: mainItem id: mainItem
flickableDirection: Flickable.VerticalFlick flickableDirection: Flickable.VerticalFlick
property bool showInitials: true // Display Initials of Display name. property bool showInitials: true // Display Initials of Display name.
property bool showDefaultAddress: true // Display address below display name. property bool showDefaultAddress: true // Display address below display name.
property bool showActions: false // Display actions layout (call buttons) property bool showActions: false // Display actions layout (call buttons)
property bool showContactMenu: true // Display the dot menu for contacts. property bool showContactMenu: true // Display the dot menu for contacts.
property bool showFavorites: true // Display the favorites in the header property bool showFavorites: true // Display the favorites in the header
property bool hideSuggestions: false // Hide not stored contacts (not suggestions) property bool hideSuggestions: false // Hide not stored contacts (not suggestions)
property string highlightText: searchText // Bold characters in Display name. property string highlightText: searchText // Bold characters in Display name.
property var sourceFlags: LinphoneEnums.MagicSearchSource.All property var sourceFlags: LinphoneEnums.MagicSearchSource.All
property bool displayNameCapitalization: true // Capitalize display name. property bool displayNameCapitalization: true // Capitalize display name.
property bool selectionEnabled: true // Contact can be selected property bool selectionEnabled: true // Contact can be selected
property bool multiSelectionEnabled: false //Multiple items can be selected. property bool multiSelectionEnabled: false //Multiple items can be selected.
property list<string> selectedContacts // List of default address on selected contacts. property list<string> selectedContacts
// List of default address on selected contacts.
//property FriendGui selectedContact//: model.getAt(currentIndex) || null //property FriendGui selectedContact//: model.getAt(currentIndex) || null
property FriendGui highlightedContact property FriendGui highlightedContact
@ -38,7 +39,8 @@ Flickable {
// set searchBarText without specifying a model to bold // set searchBarText without specifying a model to bold
// matching names // matching names
property string searchBarText property string searchBarText
property string searchText// Binding is done on searchBarTextChanged property string searchText
// Binding is done on searchBarTextChanged
property ConferenceInfoGui confInfoGui property ConferenceInfoGui confInfoGui
property bool haveFavorites: false property bool haveFavorites: false
@ -54,19 +56,19 @@ Flickable {
contentHeight: contentsLayout.height contentHeight: contentsLayout.height
rightMargin: itemsRightMargin rightMargin: itemsRightMargin
signal contactStarredChanged() signal contactStarredChanged
signal contactDeletionRequested(FriendGui contact) signal contactDeletionRequested(FriendGui contact)
signal contactAddedToSelection(string address) signal contactAddedToSelection(string address)
signal contactRemovedFromSelection(string address) signal contactRemovedFromSelection(string address)
signal contactSelected(FriendGui contact) signal contactSelected(FriendGui contact)
function selectContact(address) { function selectContact(address) {
var index = contactsProxy.loadUntil(address)// Be sure to have this address in proxy if it exists var index = contactsProxy.loadUntil(
address) // Be sure to have this address in proxy if it exists
if (index != -1) { if (index != -1) {
contactsList.selectIndex(index) contactsList.selectIndex(index)
} }
return index return index
} }
function addContactToSelection(address) { function addContactToSelection(address) {
if (multiSelectionEnabled) { if (multiSelectionEnabled) {
@ -91,49 +93,59 @@ Flickable {
contactRemovedFromSelection(address) contactRemovedFromSelection(address)
} }
} }
function haveAddress(address){ function haveAddress(address) {
var index = magicSearchProxy.findFriendIndexByAddress(address) var index = magicSearchProxy.findFriendIndexByAddress(address)
return index != -1 return index != -1
} }
function resetSelections(){ function resetSelections() {
mainItem.highlightedContact = null mainItem.highlightedContact = null
favoritesList.currentIndex = -1 favoritesList.currentIndex = -1
contactsList.currentIndex = -1 contactsList.currentIndex = -1
suggestionsList.currentIndex = -1 suggestionsList.currentIndex = -1
} }
function findNextList(item, count, direction){ function findNextList(item, count, direction) {
if(count == 3) return null if (count == 3)
return null
var nextItem var nextItem
switch(item){ switch (item) {
case suggestionsList:nextItem=(direction > 0 ? favoritesList : contactsList);break; case suggestionsList:
case contactsList:nextItem=(direction > 0 ? suggestionsList : favoritesList);break; nextItem = (direction > 0 ? favoritesList : contactsList)
case favoritesList:nextItem=(direction > 0 ? contactsList : suggestionsList);break; break
default: return null 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 if (nextItem.model.count > 0)
else return findNextList(nextItem, count+1, direction) return nextItem
else
return findNextList(nextItem, count + 1, direction)
} }
function updatePosition(list){ function updatePosition(list) {
Utils.updatePosition(mainItem, list) Utils.updatePosition(mainItem, list)
} }
onHighlightedContactChanged:{ onHighlightedContactChanged: {
favoritesList.highlightedContact = highlightedContact favoritesList.highlightedContact = highlightedContact
contactsList.highlightedContact = highlightedContact contactsList.highlightedContact = highlightedContact
suggestionsList.highlightedContact = highlightedContact suggestionsList.highlightedContact = highlightedContact
} }
onSearchBarTextChanged: { onSearchBarTextChanged: {
if(!pauseSearch && (mainItem.searchOnEmpty || searchBarText != '')) { if (!pauseSearch && (mainItem.searchOnEmpty || searchBarText != '')) {
console.log("change search text") console.log("change search text")
searchText = searchBarText.length === 0 ? "*" : searchBarText searchText = searchBarText.length === 0 ? "*" : searchBarText
} }
} }
onPauseSearchChanged: { onPauseSearchChanged: {
if(!pauseSearch && (mainItem.searchOnEmpty || searchBarText != '')){ if (!pauseSearch && (mainItem.searchOnEmpty || searchBarText != '')) {
searchText = searchBarText.length === 0 ? "*" : searchBarText searchText = searchBarText.length === 0 ? "*" : searchBarText
} }
} }
@ -142,26 +154,37 @@ Flickable {
loading = true loading = true
} }
Keys.onPressed: (event)=> { Keys.onPressed: event => {
if(!event.accepted){ if (!event.accepted) {
if(event.key == Qt.Key_Up || event.key == Qt.Key_Down){ if (event.key == Qt.Key_Up
var newItem || event.key == Qt.Key_Down) {
var direction = (event.key == Qt.Key_Up ? -1 : 1) var newItem
if(suggestionsList.activeFocus) newItem = findNextList(suggestionsList, 0, direction) var direction = (event.key == Qt.Key_Up ? -1 : 1)
else if(contactsList.activeFocus) newItem = findNextList(contactsList, 0, direction) if (suggestionsList.activeFocus)
else if(favoritesList.activeFocus) newItem = findNextList(favoritesList, 0, direction) newItem = findNextList(suggestionsList, 0,
else newItem = findNextList(suggestionsList, 0, direction) direction)
if(newItem){ else if (contactsList.activeFocus)
newItem.selectIndex(direction > 0 ? -1 : newItem.model.count - 1) newItem = findNextList(contactsList, 0,
event.accepted = true 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: { Component.onCompleted: {
if (confInfoGui) { if (confInfoGui) {
for(var i = 0; i < confInfoGui.core.participants.length; ++i) { for (var i = 0; i < confInfoGui.core.participants.length; ++i) {
selectedContacts.push(confInfoGui.core.getParticipantAddressAt(i)); selectedContacts.push(
confInfoGui.core.getParticipantAddressAt(i))
} }
} }
} }
@ -181,7 +204,6 @@ Flickable {
sourceFlags: mainItem.sourceFlags sourceFlags: mainItem.sourceFlags
onModelReset: { onModelReset: {
mainItem.resetSelections() mainItem.resetSelections()
} }
onResultsProcessed: { onResultsProcessed: {
mainItem.loading = false mainItem.loading = false
@ -189,18 +211,21 @@ Flickable {
} }
onInitialized: { onInitialized: {
if(mainItem.searchOnEmpty || searchText != '' ) { if (mainItem.searchOnEmpty || searchText != '') {
mainItem.loading = true mainItem.loading = true
forceUpdate() forceUpdate()
} }
} }
} }
onAtYEndChanged: if(atYEnd) { onAtYEndChanged: if (atYEnd) {
if( (contactsProxy.haveMore && contactList.expanded ) || mainItem.hideSuggestions) contactsProxy.displayMore() if ((contactsProxy.haveMore && contactList.expanded)
else suggestionsProxy.displayMore() || mainItem.hideSuggestions)
} contactsProxy.displayMore()
Behavior on contentY{ else
suggestionsProxy.displayMore()
}
Behavior on contentY {
NumberAnimation { NumberAnimation {
duration: 500 duration: 500
easing.type: Easing.OutExpo easing.type: Easing.OutExpo
@ -210,7 +235,7 @@ Flickable {
Control.ScrollBar.vertical: ScrollBar { Control.ScrollBar.vertical: ScrollBar {
id: scrollbar id: scrollbar
z: 1 z: 1
topPadding: 24 * DefaultStyle.dp // Avoid to be on top of collapse button topPadding: 24 * DefaultStyle.dp // Avoid to be on top of collapse button
active: true active: true
interactive: true interactive: true
visible: mainItem.contentHeight > mainItem.height visible: mainItem.contentHeight > mainItem.height
@ -220,7 +245,7 @@ Flickable {
ColumnLayout { ColumnLayout {
id: contentsLayout id: contentsLayout
width: mainItem.width width: mainItem.width
spacing: 0//20 * DefaultStyle.dp spacing: 0 //20 * DefaultStyle.dp
BusyIndicator { BusyIndicator {
id: busyIndicator id: busyIndicator
@ -232,7 +257,7 @@ Flickable {
Layout.alignment: Qt.AlignCenter | Qt.AlignVCenter Layout.alignment: Qt.AlignCenter | Qt.AlignVCenter
} }
ContactListView{ ContactListView {
id: favoritesList id: favoritesList
visible: contentHeight > 0 visible: contentHeight > 0
Layout.fillWidth: true Layout.fillWidth: true
@ -252,22 +277,32 @@ Flickable {
itemsRightMargin: mainItem.itemsRightMargin itemsRightMargin: mainItem.itemsRightMargin
onHighlightedContactChanged: mainItem.highlightedContact = highlightedContact onHighlightedContactChanged: mainItem.highlightedContact = highlightedContact
onContactSelected: (contactGui) => { onContactSelected: contactGui => {
mainItem.contactSelected(contactGui) mainItem.contactSelected(contactGui)
} }
onUpdatePosition: mainItem.updatePosition(favoritesList) onUpdatePosition: mainItem.updatePosition(favoritesList)
onContactDeletionRequested: (contact) => {mainItem.contactDeletionRequested(contact)} onContactDeletionRequested: contact => {
onAddContactToSelection: (address) => {mainItem.addContactToSelection(address)} mainItem.contactDeletionRequested(
onRemoveContactFromSelection: (index) => {mainItem.removeContactFromSelection(index)} contact)
}
onAddContactToSelection: address => {
mainItem.addContactToSelection(address)
}
onRemoveContactFromSelection: index => {
mainItem.removeContactFromSelection(
index)
}
property MagicSearchProxy proxy: MagicSearchProxy { property MagicSearchProxy proxy: MagicSearchProxy {
parentProxy: mainItem.mainModel parentProxy: mainItem.mainModel
filterType: MagicSearchProxy.FilteringTypes.Favorites filterType: MagicSearchProxy.FilteringTypes.Favorites
} }
model : mainItem.showFavorites && (mainItem.searchBarText == ''|| mainItem.searchBarText == '*')? proxy : [] model: mainItem.showFavorites
&& (mainItem.searchBarText == ''
|| mainItem.searchBarText == '*') ? proxy : []
} }
ContactListView{ ContactListView {
id: contactsList id: contactsList
visible: contentHeight > 0 visible: contentHeight > 0
Layout.fillWidth: true Layout.fillWidth: true
@ -286,32 +321,45 @@ Flickable {
title: qsTr('Contacts') title: qsTr('Contacts')
onHighlightedContactChanged: mainItem.highlightedContact = highlightedContact onHighlightedContactChanged: mainItem.highlightedContact = highlightedContact
onContactSelected: (contactGui) => { onContactSelected: contactGui => {
mainItem.contactSelected(contactGui) mainItem.contactSelected(contactGui)
} }
onUpdatePosition: mainItem.updatePosition(contactsList) onUpdatePosition: mainItem.updatePosition(contactsList)
onContactDeletionRequested: (contact) => {mainItem.contactDeletionRequested(contact)} onContactDeletionRequested: contact => {
onAddContactToSelection: (address) => {mainItem.addContactToSelection(address)} mainItem.contactDeletionRequested(
onRemoveContactFromSelection: (index) => {mainItem.removeContactFromSelection(index)} contact)
}
onAddContactToSelection: address => {
mainItem.addContactToSelection(address)
}
onRemoveContactFromSelection: index => {
mainItem.removeContactFromSelection(
index)
}
model:MagicSearchProxy { model: MagicSearchProxy {
id: contactsProxy id: contactsProxy
parentProxy: mainItem.mainModel parentProxy: mainItem.mainModel
filterType: MagicSearchProxy.FilteringTypes.App filterType: MagicSearchProxy.FilteringTypes.App
| (mainItem.searchText != '*' && mainItem.searchText != '' || SettingsCpp.syncLdapContacts ? MagicSearchProxy.FilteringTypes.Ldap | MagicSearchProxy.FilteringTypes.CardDAV: 0) | (mainItem.searchText != '*'
initialDisplayItems: Math.max(20, 2 * mainItem.height / (63 * DefaultStyle.dp)) && mainItem.searchText != ''
|| SettingsCpp.syncLdapContacts ? MagicSearchProxy.FilteringTypes.Ldap | MagicSearchProxy.FilteringTypes.CardDAV : 0)
initialDisplayItems: Math.max(
20,
2 * mainItem.height / (63 * DefaultStyle.dp))
displayItemsStep: 3 * initialDisplayItems / 2 displayItemsStep: 3 * initialDisplayItems / 2
onLocalFriendCreated: (index) => { onLocalFriendCreated: index => {
contactsList.selectIndex(index) contactsList.selectIndex(index)
} }
} }
} }
ContactListView{ ContactListView {
id: suggestionsList id: suggestionsList
visible: contentHeight > 0 visible: contentHeight > 0
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: implicitHeight Layout.preferredHeight: implicitHeight
Layout.topMargin: contactsList.height + favoritesList.height > 0 ? 4 * DefaultStyle.dp : 0 Layout.topMargin: contactsList.height + favoritesList.height
> 0 ? 4 * DefaultStyle.dp : 0
interactive: false interactive: false
showInitials: false showInitials: false
highlightText: mainItem.highlightText highlightText: mainItem.highlightText
@ -325,18 +373,30 @@ Flickable {
itemsRightMargin: mainItem.itemsRightMargin itemsRightMargin: mainItem.itemsRightMargin
onHighlightedContactChanged: mainItem.highlightedContact = highlightedContact onHighlightedContactChanged: mainItem.highlightedContact = highlightedContact
onContactSelected: (contactGui) => { onContactSelected: contactGui => {
mainItem.contactSelected(contactGui) mainItem.contactSelected(contactGui)
} }
onUpdatePosition: mainItem.updatePosition(suggestionsList) onUpdatePosition: mainItem.updatePosition(suggestionsList)
onContactDeletionRequested: (contact) => {mainItem.contactDeletionRequested(contact)} onContactDeletionRequested: contact => {
onAddContactToSelection: (address) => {mainItem.addContactToSelection(address)} mainItem.contactDeletionRequested(
onRemoveContactFromSelection: (index) => {mainItem.removeContactFromSelection(index)} contact)
model:MagicSearchProxy { }
onAddContactToSelection: address => {
mainItem.addContactToSelection(address)
}
onRemoveContactFromSelection: index => {
mainItem.removeContactFromSelection(
index)
}
model: MagicSearchProxy {
id: suggestionsProxy id: suggestionsProxy
parentProxy: mainItem.mainModel parentProxy: mainItem.mainModel
filterType: mainItem.hideSuggestions ? MagicSearchProxy.FilteringTypes.None : MagicSearchProxy.FilteringTypes.Other filterType: mainItem.hideSuggestions ? MagicSearchProxy.FilteringTypes.None : MagicSearchProxy.FilteringTypes.Other
initialDisplayItems: contactsProxy.haveMore && contactsList.expanded ? 0 : Math.max(20, 2 * mainItem.height / (63 * DefaultStyle.dp)) initialDisplayItems: contactsProxy.haveMore
&& contactsList.expanded ? 0 : Math.max(
20,
2 * mainItem.height
/ (63 * DefaultStyle.dp))
onInitialDisplayItemsChanged: maxDisplayItems = initialDisplayItems onInitialDisplayItemsChanged: maxDisplayItems = initialDisplayItems
displayItemsStep: 3 * initialDisplayItems / 2 displayItemsStep: 3 * initialDisplayItems / 2
onModelReset: maxDisplayItems = initialDisplayItems onModelReset: maxDisplayItems = initialDisplayItems

View file

@ -6,243 +6,291 @@ import Linphone
import UtilsCpp 1.0 import UtilsCpp 1.0
import ConstantsCpp 1.0 import ConstantsCpp 1.0
import SettingsCpp import SettingsCpp
import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle import "qrc:/qt/qml/Linphone/view/Style/buttonStyle.js" as ButtonStyle
FocusScope { FocusScope {
id: mainItem id: mainItem
implicitHeight: visible ? 56 * DefaultStyle.dp : 0 implicitHeight: visible ? 56 * DefaultStyle.dp : 0
property var searchResultItem property var searchResultItem
property bool showInitials: true // Display Initials of Display name. property bool showInitials: true // Display Initials of Display name.
property bool showDefaultAddress: true // Display address below display name. property bool showDefaultAddress: true // Display address below display name.
property bool showActions: false // Display actions layout (call buttons) property bool showActions: false // Display actions layout (call buttons)
property bool showContactMenu: true // Display the dot menu for contacts. property bool showContactMenu: true // Display the dot menu for contacts.
property string highlightText // Bold characters in Display name. property string highlightText
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 bool isSelected: false // selected in list => currentIndex == index
property bool isLastHovered: false
property var previousInitial // Use directly previous initial
property int itemsRightMargin: 39 * DefaultStyle.dp
property var displayName: searchResultItem.core.fullName
property string initial: displayName ? displayName[0].toLocaleLowerCase(ConstantsCpp.DefaultLocale) : ''
signal clicked(var mouse)
signal contactDeletionRequested(FriendGui contact)
signal containsMouseChanged(bool containsMouse)
Text { // Bold characters in Display name.
id: initial property bool displayNameCapitalization: true // Capitalize display name.
anchors.left: parent.left
visible: mainItem.showInitials
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: 15 * DefaultStyle.dp
verticalAlignment: Text.AlignVCenter
width: 20 * DefaultStyle.dp
opacity: previousInitial != mainItem.initial ? 1 : 0
text: mainItem.initial
color: DefaultStyle.main2_400
font {
pixelSize: 20 * DefaultStyle.dp
weight: 500 * DefaultStyle.dp
capitalization: Font.AllUppercase
}
}
RowLayout {
id: contactDelegate
anchors.left: initial.visible ? initial.right : parent.left
anchors.right: parent.right
anchors.rightMargin: mainItem.itemsRightMargin
anchors.top: parent.top
anchors.bottom: parent.bottom
spacing: 16 * DefaultStyle.dp
z: 1
Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
Layout.leftMargin: 5 * DefaultStyle.dp
contact: searchResultItem
shadowEnabled: false
}
ColumnLayout {
spacing: 0
Text {
text: UtilsCpp.boldTextPart(mainItem.displayName, mainItem.highlightText)
font{
pixelSize: mainItem.showDefaultAddress ? 16 * DefaultStyle.dp : 14 * DefaultStyle.dp
capitalization: mainItem.displayNameCapitalization ? Font.Capitalize : Font.MixedCase
weight: mainItem.showDefaultAddress ? 800 * DefaultStyle.dp : 400 * DefaultStyle.dp
}
maximumLineCount: 1
Layout.fillWidth: true
}
Text {
Layout.topMargin: 2 * DefaultStyle.dp
Layout.fillWidth: true
visible: mainItem.showDefaultAddress
property string address: SettingsCpp.onlyDisplaySipUriUsername ? UtilsCpp.getUsername(searchResultItem.core.defaultAddress) : searchResultItem.core.defaultAddress
text: UtilsCpp.boldTextPart(address, mainItem.highlightText)
maximumLineCount: 1
elide: Text.ElideRight
font {
weight: 300 * DefaultStyle.dp
pixelSize: 12 * DefaultStyle.dp
}
}
}
Item{Layout.fillWidth: true}
RowLayout {
id: actionsRow
z: 1
visible: actionButtons || friendPopup.visible || mainItem.multiSelectionEnabled
spacing: visible ? 16 * DefaultStyle.dp : 0
Layout.rightMargin: visible ? 10 * DefaultStyle.dp : 0
EffectImage {
id: isSelectedCheck
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
}
RowLayout{
id: actionButtons
visible: mainItem.showActions
spacing: visible ? 10 * DefaultStyle.dp : 0
IconButton {
id: callButton
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
icon.source: AppIcons.phone
focus: visible
radius: 40 * DefaultStyle.dp
style: ButtonStyle.grey
onClicked: UtilsCpp.createCall(searchResultItem.core.defaultFullAddress)
KeyNavigation.left: chatButton
KeyNavigation.right: videoCallButton
}
IconButton {
id: videoCallButton
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
icon.source: AppIcons.videoCamera
focus: visible && !callButton.visible
radius: 40 * DefaultStyle.dp
style: ButtonStyle.grey
onClicked: UtilsCpp.createCall(searchResultItem.core.defaultFullAddress, {'localVideoEnabled': true})
KeyNavigation.left: callButton
KeyNavigation.right: chatButton
}
IconButton {
id: chatButton
visible: actionButtons.visible && !SettingsCpp.disableChatFeature
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
icon.source: AppIcons.chatTeardropText
focus: visible && !callButton.visible && !videoCallButton.visible
radius: 40 * DefaultStyle.dp
style: ButtonStyle.grey
KeyNavigation.left: videoCallButton
KeyNavigation.right: callButton
}
}
PopupButton {
id: friendPopup
z: 1
popup.x: 0
popup.padding: 10 * DefaultStyle.dp
visible: mainItem.showContactMenu && (contactArea.containsMouse || hovered || popup.opened)
popup.contentItem: ColumnLayout {
IconLabelButton {
visible: searchResultItem.core.isStored && !searchResultItem.core.readOnly
text: searchResultItem.core.starred ? qsTr("Enlever des favoris") : qsTr("Mettre en favori")
icon.source: searchResultItem.core.starred ? AppIcons.heartFill : AppIcons.heart
spacing: 10 * DefaultStyle.dp
textColor: DefaultStyle.main2_500main
hoveredImageColor: searchResultItem.core.starred ? DefaultStyle.main1_700 : DefaultStyle.danger_700
contentImageColor: searchResultItem.core.starred ? DefaultStyle.danger_500main : DefaultStyle.main2_600
onClicked: {
searchResultItem.core.lSetStarred(!searchResultItem.core.starred)
friendPopup.close()
}
style: ButtonStyle.noBackground
}
IconLabelButton {
text: qsTr("Partager")
icon.source: AppIcons.shareNetwork
spacing: 10 * DefaultStyle.dp
textColor: DefaultStyle.main2_500main
onClicked: {
var vcard = searchResultItem.core.getVCard()
var username = searchResultItem.core.givenName + searchResultItem.core.familyName
var filepath = UtilsCpp.createVCardFile(username, vcard)
if (filepath == "") UtilsCpp.showInformationPopup(qsTr("Erreur"), qsTr("La création du fichier vcard a échoué"), false)
else mainWindow.showInformationPopup(qsTr("VCard créée"), qsTr("VCard du contact enregistrée dans %1").arg(filepath))
UtilsCpp.shareByEmail(qsTr("Partage de contact"), vcard, filepath)
}
style: ButtonStyle.noBackground
} property bool selectionEnabled: true // Contact can be selected
IconLabelButton { property bool multiSelectionEnabled: false //Multiple items can be selected.
text: qsTr("Supprimer") property list<string> selectedContacts
icon.source: AppIcons.trashCan // List of default address on selected contacts.
spacing: 10 * DefaultStyle.dp property bool isSelected: false // selected in list => currentIndex == index
visible: !searchResultItem.core.readOnly property bool isLastHovered: false
onClicked: {
mainItem.contactDeletionRequested(searchResultItem) property var previousInitial
friendPopup.close() // Use directly previous initial
} property int itemsRightMargin: 39 * DefaultStyle.dp
style: ButtonStyle.noBackgroundRed
} property var displayName: searchResultItem.core.fullName
} property string initial: displayName ? displayName[0].toLocaleLowerCase(
} ConstantsCpp.DefaultLocale) : ''
}
} signal clicked(var mouse)
signal contactDeletionRequested(FriendGui contact)
MouseArea { signal containsMouseChanged(bool containsMouse)
id: contactArea
enabled: mainItem.selectionEnabled Text {
anchors.fill: contactDelegate id: initial
//height: mainItem.height anchors.left: parent.left
hoverEnabled: true visible: mainItem.showInitials
acceptedButtons: Qt.AllButtons anchors.verticalCenter: parent.verticalCenter
z: -1 anchors.rightMargin: 15 * DefaultStyle.dp
focus: !actionButtons.visible verticalAlignment: Text.AlignVCenter
onContainsMouseChanged: { width: 20 * DefaultStyle.dp
mainItem.containsMouseChanged(containsMouse) opacity: previousInitial != mainItem.initial ? 1 : 0
} text: mainItem.initial
Rectangle { color: DefaultStyle.main2_400
anchors.fill: contactArea font {
radius: 8 * DefaultStyle.dp pixelSize: 20 * DefaultStyle.dp
opacity: 0.7 weight: 500 * DefaultStyle.dp
color: mainItem.isSelected ? DefaultStyle.main2_200 : DefaultStyle.main2_100 capitalization: Font.AllUppercase
visible: mainItem.isLastHovered || friendPopup.hovered || mainItem.isSelected || friendPopup.visible }
} }
Keys.onPressed: (event)=> { RowLayout {
if (event.key == Qt.Key_Space || event.key == Qt.Key_Enter || event.key == Qt.Key_Return) { id: contactDelegate
contactArea.clicked(undefined) anchors.left: initial.visible ? initial.right : parent.left
event.accepted = true; anchors.right: parent.right
} anchors.rightMargin: mainItem.itemsRightMargin
} anchors.top: parent.top
onClicked: (mouse) => { anchors.bottom: parent.bottom
forceActiveFocus() spacing: 16 * DefaultStyle.dp
if (mouse && mouse.button == Qt.RightButton && mainItem.showContactMenu) { z: 1
friendPopup.open() Avatar {
} else { Layout.preferredWidth: 45 * DefaultStyle.dp
mainItem.clicked(mouse) Layout.preferredHeight: 45 * DefaultStyle.dp
} Layout.leftMargin: 5 * DefaultStyle.dp
} contact: searchResultItem
} shadowEnabled: false
}
ColumnLayout {
spacing: 0
Text {
text: UtilsCpp.boldTextPart(mainItem.displayName,
mainItem.highlightText)
font {
pixelSize: mainItem.showDefaultAddress ? 16 * DefaultStyle.dp : 14
* DefaultStyle.dp
capitalization: mainItem.displayNameCapitalization ? Font.Capitalize : Font.MixedCase
weight: mainItem.showDefaultAddress ? 800 * DefaultStyle.dp : 400
* DefaultStyle.dp
}
maximumLineCount: 1
Layout.fillWidth: true
}
Text {
Layout.topMargin: 2 * DefaultStyle.dp
Layout.fillWidth: true
visible: mainItem.showDefaultAddress
property string address: SettingsCpp.onlyDisplaySipUriUsername ? UtilsCpp.getUsername(searchResultItem.core.defaultAddress) : searchResultItem.core.defaultAddress
text: UtilsCpp.boldTextPart(address, mainItem.highlightText)
maximumLineCount: 1
elide: Text.ElideRight
font {
weight: 300 * DefaultStyle.dp
pixelSize: 12 * DefaultStyle.dp
}
}
}
Item {
Layout.fillWidth: true
}
Loader {
id: buttonsLayoutLoader
asynchronous: true
active: mainItem.showActions || mainItem.showContactMenu
|| mainItem.multiSelectionEnabled
Layout.rightMargin: active ? 10 * DefaultStyle.dp : 0
sourceComponent: RowLayout {
id: actionsRow
z: 1
visible: actionButtons.visible || friendPopup.visible
|| mainItem.multiSelectionEnabled
spacing: visible ? 16 * DefaultStyle.dp : 0
enabled: visible
EffectImage {
id: isSelectedCheck
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
}
RowLayout {
id: actionButtons
visible: mainItem.showActions
spacing: visible ? 10 * DefaultStyle.dp : 0
IconButton {
id: callButton
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
icon.source: AppIcons.phone
focus: visible
radius: 40 * DefaultStyle.dp
style: ButtonStyle.grey
onClicked: UtilsCpp.createCall(
searchResultItem.core.defaultFullAddress)
KeyNavigation.left: chatButton
KeyNavigation.right: videoCallButton
}
IconButton {
id: videoCallButton
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
icon.source: AppIcons.videoCamera
focus: visible && !callButton.visible
radius: 40 * DefaultStyle.dp
style: ButtonStyle.grey
onClicked: UtilsCpp.createCall(
searchResultItem.core.defaultFullAddress,
{
"localVideoEnabled": true
})
KeyNavigation.left: callButton
KeyNavigation.right: chatButton
}
IconButton {
id: chatButton
visible: actionButtons.visible
&& !SettingsCpp.disableChatFeature
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
icon.width: 24 * DefaultStyle.dp
icon.height: 24 * DefaultStyle.dp
icon.source: AppIcons.chatTeardropText
focus: visible && !callButton.visible
&& !videoCallButton.visible
radius: 40 * DefaultStyle.dp
style: ButtonStyle.grey
KeyNavigation.left: videoCallButton
KeyNavigation.right: callButton
}
}
PopupButton {
id: friendPopup
z: 1
popup.x: 0
popup.padding: 10 * DefaultStyle.dp
visible: mainItem.showContactMenu
&& (contactArea.containsMouse || hovered
|| popup.opened)
enabled: visible
popup.contentItem: ColumnLayout {
IconLabelButton {
visible: searchResultItem.core.isStored
&& !searchResultItem.core.readOnly
text: searchResultItem.core.starred ? qsTr(
"Enlever des favoris") : qsTr(
"Mettre en favori")
icon.source: searchResultItem.core.starred ? AppIcons.heartFill : AppIcons.heart
spacing: 10 * DefaultStyle.dp
textColor: DefaultStyle.main2_500main
hoveredImageColor: searchResultItem.core.starred ? DefaultStyle.main1_700 : DefaultStyle.danger_700
contentImageColor: searchResultItem.core.starred ? DefaultStyle.danger_500main : DefaultStyle.main2_600
onClicked: {
searchResultItem.core.lSetStarred(
!searchResultItem.core.starred)
friendPopup.close()
}
style: ButtonStyle.noBackground
}
IconLabelButton {
text: qsTr("Partager")
icon.source: AppIcons.shareNetwork
spacing: 10 * DefaultStyle.dp
textColor: DefaultStyle.main2_500main
onClicked: {
var vcard = searchResultItem.core.getVCard()
var username = searchResultItem.core.givenName
+ searchResultItem.core.familyName
var filepath = UtilsCpp.createVCardFile(
username, vcard)
if (filepath == "")
UtilsCpp.showInformationPopup(
qsTr("Erreur"), qsTr(
"La création du fichier vcard a échoué"),
false)
else
mainWindow.showInformationPopup(
qsTr("VCard créée"), qsTr(
"VCard du contact enregistrée dans %1").arg(
filepath))
UtilsCpp.shareByEmail(
qsTr("Partage de contact"),
vcard, filepath)
}
style: ButtonStyle.noBackground
}
IconLabelButton {
text: qsTr("Supprimer")
icon.source: AppIcons.trashCan
spacing: 10 * DefaultStyle.dp
visible: !searchResultItem.core.readOnly
onClicked: {
mainItem.contactDeletionRequested(
searchResultItem)
friendPopup.close()
}
style: ButtonStyle.noBackgroundRed
}
}
}
}
}
}
MouseArea {
id: contactArea
enabled: mainItem.selectionEnabled
anchors.fill: contactDelegate
//height: mainItem.height
hoverEnabled: true
acceptedButtons: Qt.AllButtons
z: -1
focus: !buttonsLayoutLoader.active
onContainsMouseChanged: {
mainItem.containsMouseChanged(containsMouse)
}
Rectangle {
anchors.fill: contactArea
radius: 8 * DefaultStyle.dp
opacity: 0.7
color: mainItem.isSelected ? DefaultStyle.main2_200 : DefaultStyle.main2_100
visible: mainItem.isLastHovered || mainItem.isSelected
}
Keys.onPressed: event => {
if (event.key == Qt.Key_Space
|| event.key == Qt.Key_Enter
|| event.key == Qt.Key_Return) {
contactArea.clicked(undefined)
event.accepted = true
}
}
onClicked: mouse => {
forceActiveFocus()
if (mouse && mouse.button == Qt.RightButton
&& mainItem.showContactMenu) {
friendPopup.open()
} else {
mainItem.clicked(mouse)
}
}
}
} }

File diff suppressed because it is too large Load diff