diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index 0371f5523..d3f271ab9 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -45,6 +45,7 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Control/Display/Text.qml view/Control/Display/ToolTip.qml view/Control/Display/Call/CallListView.qml + view/Control/Display/Call/CallHistoryListView.qml view/Control/Display/Call/CallStatistics.qml view/Control/Display/Contact/Avatar.qml view/Control/Display/Contact/Contact.qml diff --git a/Linphone/view/Control/Display/Call/CallHistoryListView.qml b/Linphone/view/Control/Display/Call/CallHistoryListView.qml new file mode 100644 index 000000000..5ab54db89 --- /dev/null +++ b/Linphone/view/Control/Display/Call/CallHistoryListView.qml @@ -0,0 +1,227 @@ +import QtQuick +import QtQuick.Effects +import QtQuick.Layouts +import QtQuick.Controls.Basic as Control +import Linphone +import UtilsCpp +import SettingsCpp +import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle +import 'qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js' as Utils + +ListView { + id: mainItem + clip: true + Layout.fillWidth: true + Layout.fillHeight: true + property SearchBar searchBar + property bool loading: false + property string searchText: searchBar.text + + signal resultsReceived() + + onResultsReceived: { + loading = false + // contentY = 0 + } + onSearchTextChanged: loading = true + + model: CallHistoryProxy { + id: callHistoryProxy + filterText: searchBar.text + onFilterTextChanged: maxDisplayItems = initialDisplayItems + initialDisplayItems: Math.max(20, 2 * mainItem.height / (56 * DefaultStyle.dp)) + displayItemsStep: 3 * initialDisplayItems / 2 + onModelReset: { + console.log("model reset================") + mainItem.resultsReceived() + } + } + cacheBuffer: contentHeight>0 ? contentHeight : 0// cache all items + flickDeceleration: 10000 + spacing: 10 * DefaultStyle.dp + + Keys.onPressed: (event) => { + if(event.key == Qt.Key_Escape){ + console.log("Back") + searchBar.forceActiveFocus() + event.accepted = true + } + } + // remove binding loop + onContentHeightChanged: Qt.callLater(function(){ + mainItem.cacheBuffer = Math.max(contentHeight,0) + }) + onActiveFocusChanged: if(activeFocus && currentIndex < 0 && count > 0) currentIndex = 0 + onCountChanged: { + if(currentIndex < 0 && count > 0){ + mainItem.currentIndex = 0 // Select first item after loading model + } + if(atYBeginning) + positionViewAtBeginning()// Stay at beginning + } + Connections { + target: deleteHistoryPopup + function onAccepted() { + mainItem.model.removeAllEntries() + } + } + + onAtYEndChanged: { + if(atYEnd && count > 0){ + callHistoryProxy.displayMore() + } + } + //---------------------------------------------------------------- + function moveToCurrentItem(){ + if( mainItem.currentIndex >= 0) + Utils.updatePosition(mainItem, mainItem) + } + onCurrentItemChanged: { + moveToCurrentItem() + } + // Update position only if we are moving to current item and its position is changing. + property var _currentItemY: currentItem?.y + on_CurrentItemYChanged: if(_currentItemY && moveAnimation.running){ + moveToCurrentItem() + } + Behavior on contentY{ + NumberAnimation { + id: moveAnimation + duration: 500 + easing.type: Easing.OutExpo + alwaysRunToEnd: true + } + } + //---------------------------------------------------------------- + + + onVisibleChanged: { + if (!visible) currentIndex = -1 + } + // 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 + delegate: FocusScope { + width:mainItem.width + height: 56 * DefaultStyle.dp + visible: !!modelData + + RowLayout { + z: 1 + anchors.fill: parent + anchors.leftMargin: 10 * DefaultStyle.dp + spacing: 10 * DefaultStyle.dp + Avatar { + id: historyAvatar + property var contactObj: UtilsCpp.findFriendByAddress(modelData.core.remoteAddress) + contact: contactObj?.value || null + _address: modelData.core.conferenceInfo + ? modelData.core.conferenceInfo.core.subject + : modelData.core.remoteAddress + secured: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified + width: 45 * DefaultStyle.dp + height: 45 * DefaultStyle.dp + isConference: modelData.core.isConference + } + ColumnLayout { + Layout.fillHeight: true + Layout.fillWidth: true + spacing: 5 * DefaultStyle.dp + Text { + id: friendAddress + Layout.fillWidth: true + maximumLineCount: 1 + text: historyAvatar.displayNameVal + font { + pixelSize: 14 * DefaultStyle.dp + weight: 400 * DefaultStyle.dp + capitalization: Font.Capitalize + } + } + RowLayout { + spacing: 6 * DefaultStyle.dp + EffectImage { + id: statusIcon + imageSource: modelData.core.status === LinphoneEnums.CallStatus.Declined + || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere + || modelData.core.status === LinphoneEnums.CallStatus.Aborted + || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted + ? AppIcons.arrowElbow + : modelData.core.isOutgoing + ? AppIcons.arrowUpRight + : AppIcons.arrowDownLeft + colorizationColor: modelData.core.status === LinphoneEnums.CallStatus.Declined + || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere + || modelData.core.status === LinphoneEnums.CallStatus.Aborted + || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted + || modelData.core.status === LinphoneEnums.CallStatus.Missed + ? DefaultStyle.danger_500main + : modelData.core.isOutgoing + ? DefaultStyle.info_500_main + : DefaultStyle.success_500main + Layout.preferredWidth: 12 * DefaultStyle.dp + Layout.preferredHeight: 12 * DefaultStyle.dp + transform: Rotation { + angle: modelData.core.isOutgoing && (modelData.core.status === LinphoneEnums.CallStatus.Declined + || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere + || modelData.core.status === LinphoneEnums.CallStatus.Aborted + || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted) ? 180 : 0 + origin { + x: statusIcon.width/2 + y: statusIcon.height/2 + } + } + } + Text { + // text: modelData.core.date + text: UtilsCpp.formatDate(modelData.core.date) + font { + pixelSize: 12 * DefaultStyle.dp + weight: 300 * DefaultStyle.dp + } + } + } + } + BigButton { + style: ButtonStyle.noBackground + icon.source: AppIcons.phone + focus: true + activeFocusOnTab: false + onClicked: { + if (modelData.core.isConference) { + var callsWindow = UtilsCpp.getCallsWindow() + callsWindow.setupConference(modelData.core.conferenceInfo) + UtilsCpp.smartShowWindow(callsWindow) + } + else { + UtilsCpp.createCall(modelData.core.remoteAddress) + } + } + } + } + MouseArea { + hoverEnabled: true + anchors.fill: parent + focus: true + onContainsMouseChanged: { + if(containsMouse) + mainItem.lastMouseContainsIndex = index + else if( mainItem.lastMouseContainsIndex == index) + mainItem.lastMouseContainsIndex = -1 + } + Rectangle { + anchors.fill: parent + opacity: 0.7 + radius: 8 * DefaultStyle.dp + color: mainItem.currentIndex === index ? DefaultStyle.main2_200 : DefaultStyle.main2_100 + visible: mainItem.lastMouseContainsIndex === index || mainItem.currentIndex === index + } + onPressed: { + mainItem.currentIndex = model.index + mainItem.forceActiveFocus() + } + } + } + + Control.ScrollBar.vertical: scrollbar +} diff --git a/Linphone/view/Page/Form/Login/SIPLoginPage.qml b/Linphone/view/Page/Form/Login/SIPLoginPage.qml index e2752504c..068fce593 100644 --- a/Linphone/view/Page/Form/Login/SIPLoginPage.qml +++ b/Linphone/view/Page/Form/Login/SIPLoginPage.qml @@ -32,7 +32,7 @@ LoginLayout { } EffectImage { fillMode: Image.PreserveAspectFit - source: AppIcons.profile + imageSource: AppIcons.profile Layout.preferredHeight: 34 * DefaultStyle.dp Layout.preferredWidth: 34 * DefaultStyle.dp colorizationColor: DefaultStyle.main2_600 diff --git a/Linphone/view/Page/Main/Call/CallPage.qml b/Linphone/view/Page/Main/Call/CallPage.qml index 27734be07..fdba8ad2d 100644 --- a/Linphone/view/Page/Main/Call/CallPage.qml +++ b/Linphone/view/Page/Main/Call/CallPage.qml @@ -214,213 +214,21 @@ AbstractMainPage { weight: 800 * DefaultStyle.dp } } - ListView { + CallHistoryListView { id: historyListView - clip: true - Layout.fillWidth: true - Layout.fillHeight: true - model: CallHistoryProxy { - id: callHistoryProxy - filterText: searchBar.text - onFilterTextChanged: maxDisplayItems = initialDisplayItems - initialDisplayItems: Math.max(20, 2 * historyListView.height / (56 * DefaultStyle.dp)) - displayItemsStep: 3 * initialDisplayItems / 2 - } - cacheBuffer: contentHeight>0 ? contentHeight : 0// cache all items - flickDeceleration: 10000 - spacing: 10 * DefaultStyle.dp - - Keys.onPressed: (event) => { - if(event.key == Qt.Key_Escape){ - console.log("Back") - searchBar.forceActiveFocus() - event.accepted = true - } - } - // remove binding loop - onContentHeightChanged: Qt.callLater(function(){ - historyListView.cacheBuffer = Math.max(contentHeight,0) - }) - onActiveFocusChanged: if(activeFocus && currentIndex < 0 && count > 0) currentIndex = 0 - onCountChanged: { - if(currentIndex < 0 && count > 0){ - historyListView.currentIndex = 0 // Select first item after loading model - } - if(atYBeginning) - positionViewAtBeginning()// Stay at beginning - } - Connections { - target: deleteHistoryPopup - function onAccepted() { - historyListView.model.removeAllEntries() - } - } + searchBar: searchBar Connections{ target: mainItem function onListViewUpdated(){ - callHistoryProxy.reload() + historyListView.model.reload() } } - onAtYEndChanged: { - if(atYEnd && count > 0){ - callHistoryProxy.displayMore() - } - } - //---------------------------------------------------------------- - function moveToCurrentItem(){ - if( historyListView.currentIndex >= 0) - Utils.updatePosition(historyListView, historyListView) - } - onCurrentItemChanged: { - moveToCurrentItem() - } - // Update position only if we are moving to current item and its position is changing. - property var _currentItemY: currentItem?.y - on_CurrentItemYChanged: if(_currentItemY && moveAnimation.running){ - moveToCurrentItem() - } - Behavior on contentY{ - NumberAnimation { - id: moveAnimation - duration: 500 - easing.type: Easing.OutExpo - alwaysRunToEnd: true - } - } - //---------------------------------------------------------------- - onCurrentIndexChanged: { mainItem.selectedRowHistoryGui = model.getAt(currentIndex) } - onVisibleChanged: { - if (!visible) currentIndex = -1 + onCountChanged: { + mainItem.selectedRowHistoryGui = model.getAt(currentIndex) } - // 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 - delegate: FocusScope { - width:historyListView.width - height: 56 * DefaultStyle.dp - visible: !!modelData - - RowLayout { - z: 1 - anchors.fill: parent - anchors.leftMargin: 10 * DefaultStyle.dp - spacing: 10 * DefaultStyle.dp - Avatar { - id: historyAvatar - property var contactObj: UtilsCpp.findFriendByAddress(modelData.core.remoteAddress) - contact: contactObj?.value || null - _address: modelData.core.conferenceInfo - ? modelData.core.conferenceInfo.core.subject - : modelData.core.remoteAddress - secured: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified - width: 45 * DefaultStyle.dp - height: 45 * DefaultStyle.dp - isConference: modelData.core.isConference - } - ColumnLayout { - Layout.fillHeight: true - Layout.fillWidth: true - spacing: 5 * DefaultStyle.dp - Text { - id: friendAddress - Layout.fillWidth: true - maximumLineCount: 1 - text: historyAvatar.displayNameVal - font { - pixelSize: 14 * DefaultStyle.dp - weight: 400 * DefaultStyle.dp - capitalization: Font.Capitalize - } - } - RowLayout { - spacing: 6 * DefaultStyle.dp - EffectImage { - id: statusIcon - imageSource: modelData.core.status === LinphoneEnums.CallStatus.Declined - || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere - || modelData.core.status === LinphoneEnums.CallStatus.Aborted - || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted - ? AppIcons.arrowElbow - : modelData.core.isOutgoing - ? AppIcons.arrowUpRight - : AppIcons.arrowDownLeft - colorizationColor: modelData.core.status === LinphoneEnums.CallStatus.Declined - || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere - || modelData.core.status === LinphoneEnums.CallStatus.Aborted - || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted - || modelData.core.status === LinphoneEnums.CallStatus.Missed - ? DefaultStyle.danger_500main - : modelData.core.isOutgoing - ? DefaultStyle.info_500_main - : DefaultStyle.success_500main - Layout.preferredWidth: 12 * DefaultStyle.dp - Layout.preferredHeight: 12 * DefaultStyle.dp - transform: Rotation { - angle: modelData.core.isOutgoing && (modelData.core.status === LinphoneEnums.CallStatus.Declined - || modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere - || modelData.core.status === LinphoneEnums.CallStatus.Aborted - || modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted) ? 180 : 0 - origin { - x: statusIcon.width/2 - y: statusIcon.height/2 - } - } - } - Text { - // text: modelData.core.date - text: UtilsCpp.formatDate(modelData.core.date) - font { - pixelSize: 12 * DefaultStyle.dp - weight: 300 * DefaultStyle.dp - } - } - } - } - BigButton { - style: ButtonStyle.noBackground - icon.source: AppIcons.phone - focus: true - activeFocusOnTab: false - onClicked: { - if (modelData.core.isConference) { - var callsWindow = UtilsCpp.getCallsWindow() - callsWindow.setupConference(modelData.core.conferenceInfo) - UtilsCpp.smartShowWindow(callsWindow) - } - else { - UtilsCpp.createCall(modelData.core.remoteAddress) - } - } - } - } - MouseArea { - hoverEnabled: true - anchors.fill: parent - focus: true - onContainsMouseChanged: { - if(containsMouse) - historyListView.lastMouseContainsIndex = index - else if( historyListView.lastMouseContainsIndex == index) - historyListView.lastMouseContainsIndex = -1 - } - Rectangle { - anchors.fill: parent - opacity: 0.7 - radius: 8 * DefaultStyle.dp - color: historyListView.currentIndex === index ? DefaultStyle.main2_200 : DefaultStyle.main2_100 - visible: historyListView.lastMouseContainsIndex === index || historyListView.currentIndex === index - } - onPressed: { - historyListView.currentIndex = model.index - historyListView.forceActiveFocus() - } - } - } - - Control.ScrollBar.vertical: scrollbar } } }