magic search bar (todo: finish it when mockup ready)

This commit is contained in:
Gaelle Braud 2024-02-06 16:26:45 +01:00
parent a2154a6c7b
commit 63f1328576
12 changed files with 261 additions and 102 deletions

View file

@ -309,6 +309,7 @@ void FriendCore::removeAddress(int index) {
}
void FriendCore::appendAddress(const QString &addr) {
if (addr.isEmpty()) return;
mAddressList.append(createFriendAddressVariant(addressLabel, addr));
if (mDefaultAddress.isEmpty()) mDefaultAddress = addr;
emit addressChanged();

View file

@ -74,6 +74,7 @@ list(APPEND _LINPHONEAPP_RC_FILES data/assistant/use-app-sip-account.rc
"data/image/media_encryption_zrtp_pq.svg"
"data/image/question.svg"
"data/image/settings.svg"
"data/image/user-plus.svg"
data/shaders/roundEffect.vert.qsb
data/shaders/roundEffect.frag.qsb

View file

@ -270,6 +270,17 @@ QString Utils::formatDateElapsedTime(const QDateTime &date) {
return QString::number(s) + " s";
}
QString Utils::generateLinphoneSipAddress(const QString &uri) {
QString ret = uri;
if (!ret.startsWith("sip:")) {
ret.prepend("sip:");
}
if (!ret.endsWith("@sip.linhpone.org")) {
ret.append("@sip.linhpone.org");
}
return ret;
}
QString Utils::generateSavedFilename(const QString &from, const QString &to) {
auto escape = [](const QString &str) {
constexpr char ReservedCharacters[] = "[<|>|:|\"|/|\\\\|\\?|\\*|\\+|\\||_|-]+";

View file

@ -74,8 +74,9 @@ public:
Q_INVOKABLE static QString formatDateElapsedTime(const QDateTime &date);
Q_INVOKABLE static QStringList generateSecurityLettersArray(int arraySize, int correctIndex, QString correctCode);
Q_INVOKABLE static int getRandomIndex(int size);
Q_INVOKABLE static void copyToClipboard(const QString &text);
Q_INVOKABLE static void copyToClipboard(const QString &text);
static QString generateSavedFilename(const QString &from, const QString &to);
Q_INVOKABLE static QString generateLinphoneSipAddress(const QString &uri);
static inline QString coreStringToAppString(const std::string &str) {
if (Constants::LinphoneLocaleEncoding == QString("UTF-8")) return QString::fromStdString(str);

View file

@ -121,9 +121,7 @@ ColumnLayout {
label: qsTr("Appel")
property var callObj
button.onClicked: {
var addr = mainItem.contact.core.defaultAddress
var addressEnd = "@sip.linphone.org"
if (!addr.endsWith(addressEnd)) addr += addressEnd
var addr = UtilsCpp.generateLinphoneSipAddress(mainItem.contact.core.defaultAddress)
callObj = UtilsCpp.createCall(addr)
}
}
@ -144,11 +142,9 @@ ColumnLayout {
label: qsTr("Appel Video")
property var callObj
button.onClicked: {
var addr = mainItem.contact.core.defaultAddress
var addressEnd = "@sip.linphone.org"
if(!addr.endsWith(addressEnd)) addr += addressEnd
callObj = UtilsCpp.createCall(addr)
console.log("[CallPage.qml] TODO : enable video")
var addr = UtilsCpp.generateLinphoneSipAddress(mainItem.contact.core.defaultAddress)
callObj = UtilsCpp.createCall(addr)
console.log("[CallPage.qml] TODO : enable video")
}
}
// Item {Layout.fillWidth: true}
@ -162,4 +158,4 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 30 * DefaultStyle.dp
}
}
}

View file

@ -103,8 +103,140 @@ Item {
Layout.leftMargin: 25 * DefaultStyle.dp
Layout.rightMargin: 41 * DefaultStyle.dp
SearchBar {
id: magicSearchBar
Layout.fillWidth: true
placeholderText: qsTr("Rechercher un contact, appeler ou envoyer un message...")
onTextChanged: {
if (text.length != 0) listPopup.open()
else listPopup.close()
}
Popup {
id: listPopup
width: magicSearchList.width + listPopup.rightPadding + listPopup.leftPadding
height: Math.min(magicSearchList.contentHeight, 300 * DefaultStyle.dp + topPadding + bottomPadding)
y: magicSearchBar.height
closePolicy: Popup.NoAutoClose
topPadding: 10 * DefaultStyle.dp
bottomPadding: 10 * DefaultStyle.dp
rightPadding: 10 * DefaultStyle.dp
leftPadding: 10 * DefaultStyle.dp
background: Item {
anchors.fill: parent
Rectangle {
id: popupBg
radius: 15 * DefaultStyle.dp
anchors.fill: parent
// width: magicSearchList.width + listPopup.rightPadding + listPopup.leftPadding
}
MultiEffect {
source: popupBg
anchors.fill: popupBg
shadowEnabled: true
shadowBlur: 1
shadowColor: DefaultStyle.grey_1000
shadowOpacity: 0.1
}
}
Control.ScrollBar {
id: scrollbar
height: parent.height
anchors.right: parent.right
}
ContactsList {
id: magicSearchList
visible: magicSearchBar.text.length != 0
height: Math.min(contentHeight, listPopup.height)
width: magicSearchBar.width
initialHeadersVisible: false
contactMenuVisible: false
model: MagicSearchProxy {
searchText: magicSearchBar.text.length === 0 ? "*" : magicSearchBar.text
aggregationFlag: LinphoneEnums.MagicSearchAggregation.Friend
}
Control.ScrollBar.vertical: scrollbar
header: ColumnLayout {
width: magicSearchList.width
RowLayout {
Layout.fillWidth: true
spacing: 10 * DefaultStyle.dp
Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
address: sipAddr.text
}
ColumnLayout {
Text {
text: magicSearchBar.text
font {
pixelSize: 14 * DefaultStyle.dp
weight: 700 * DefaultStyle.dp
}
}
Text {
id: sipAddr
text: UtilsCpp.generateLinphoneSipAddress(magicSearchBar.text)
}
}
Item {
Layout.fillWidth: true
}
Button {
background: Item{}
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
contentItem: Image {
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
source: AppIcons.phone
}
onClicked: {
var callObj = UtilsCpp.createCall(sipAddr)
}
}
}
Button {
Layout.fillWidth: true
color: DefaultStyle.main2_200
pressedColor: DefaultStyle.main2_400
background: Rectangle {
anchors.fill: parent
color: DefaultStyle.main2_200
}
contentItem: RowLayout {
spacing: 10 * DefaultStyle.dp
Image {
source: AppIcons.userPlus
}
Text {
text: qsTr("Ajouter ce contact")
font {
pixelSize: 14 * DefaultStyle.dp
weight: 700 * DefaultStyle.dp
capitalization: Font.AllUppercase
}
}
Item {Layout.fillWidth: true}
}
onClicked: {
var currentItem = mainStackLayout.children[mainStackLayout.currentIndex]
listPopup.close()
currentItem.createContact(magicSearchBar.text, sipAddr.text)
}
}
}
delegateButtons: Button {
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
background: Item{}
contentItem: Image {
anchors.fill: parent
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
source: AppIcons.phone
}
}
}
}
}
Control.Button {
id: avatarButton
@ -190,7 +322,7 @@ Item {
StackLayout {
// width: parent.width
// height: parent.height
id: mainStackLayout
currentIndex: tabbar.currentIndex
CallPage {

View file

@ -257,6 +257,8 @@ Item {
Layout.fillHeight: true
initialHeadersVisible: false
displayNameCapitalization: false
// Align the suggestions list to the friends ones (which contains intial column)
delegateLeftMargin: 20 * DefaultStyle.dp
model: MagicSearchProxy {
searchText: searchBar.text.length === 0 ? "*" : searchBar.text
sourceFlags: LinphoneEnums.MagicSearchSource.All

View file

@ -9,6 +9,7 @@ ListView {
Layout.preferredHeight: contentHeight
height: contentHeight
visible: count > 0
clip: true
property string searchBarText
@ -17,6 +18,10 @@ ListView {
property bool initialHeadersVisible: true
property bool displayNameCapitalization: true
property int delegateLeftMargin: 0
currentIndex: -1
property var delegateButtons
property FriendGui selectedContact: model.getAt(currentIndex) || null
@ -48,10 +53,11 @@ ListView {
}
Text {
id: initial
visible: mainItem.model.sourceFlags != LinphoneEnums.MagicSearchSource.All
anchors.left: parent.left
visible: mainItem.initialHeadersVisible && mainItem.model.sourceFlags != LinphoneEnums.MagicSearchSource.All
anchors.verticalCenter: parent.verticalCenter
verticalAlignment: Text.AlignVCenter
Layout.preferredWidth: 20 * DefaultStyle.dp
width: 20 * DefaultStyle.dp
opacity: (!previousItem || !previousDisplayName.startsWith(displayName[0])) ? 1 : 0
text: displayName[0]
color: DefaultStyle.main2_400
@ -63,10 +69,11 @@ ListView {
}
RowLayout {
id: contactDelegate
anchors.left: initial.right
anchors.leftMargin: 10 * DefaultStyle.dp
anchors.left: initial.visible ? initial.right : parent.left
anchors.leftMargin: 10 * DefaultStyle.dp + mainItem.delegateLeftMargin
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 10 * DefaultStyle.dp
z: 1
Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp
@ -83,11 +90,20 @@ ListView {
}
}
RowLayout {
z: 1
height: parent.height
anchors.right: parent.right
anchors.rightMargin: 5 * DefaultStyle.dp
anchors.verticalCenter: parent.verticalCenter
children: mainItem.delegateButtons || []
}
PopupButton {
id: friendPopup
z: 1
hoverEnabled: mainItem.hoverEnabled
visible: mainItem.contactMenuVisible && (contactArea.containsMouse || hovered || popup.opened)
visible: mainItem.contactMenuVisible && (contactArea.containsMouse || hovered || popup.opened) && (!delegateButtons || delegateButtons.children.length === 0)
popup.x: 0
popup.padding: 10 * DefaultStyle.dp
anchors.right: parent.right
@ -151,7 +167,7 @@ ListView {
MouseArea {
id: contactArea
hoverEnabled: mainItem.hoverEnabled
anchors.fill: contactDelegate
anchors.fill: initial.visible ? contactDelegate : parent
height: mainItem.height
Rectangle {
anchors.fill: contactArea
@ -165,4 +181,4 @@ ListView {
}
}
}
}
}

View file

@ -7,7 +7,7 @@ import QtQuick.Layouts 1.3
import QtQuick.Controls as Control
import Linphone
import UtilsCpp
Item {
id: mainItem
@ -15,9 +15,25 @@ Item {
property string newItemIconSource
property string emptyListText
property alias leftPanelContent: leftPanel.children
property alias rightPanelContent: rightPanelItem.children
property alias rightPanelStackView: rightPanelStackView
property alias contactEditionComp: contactEditionComp
property bool showDefaultItem: true
signal noItemButtonPressed()
signal contactEditionClosed()
function createContact(name, address) {
var friendGui = Qt.createQmlObject('import Linphone
FriendGui{
}', mainItem)
friendGui.core.givenName = UtilsCpp.getGivenNameFromFullName(name)
friendGui.core.familyName = UtilsCpp.getFamilyNameFromFullName(name)
friendGui.core.defaultAddress = address
rightPanelStackView.push(contactEditionComp, {"contact": friendGui, "title": qsTr("Nouveau contact"), "saveButtonText": qsTr("Créer")})
}
function editContact(friendGui) {
rightPanelStackView.push(contactEditionComp, {"contact": friendGui, "title": qsTr("Modifier contact"), "saveButtonText": qsTr("Enregistrer")})
}
// Control.SplitView {
// id: splitView
@ -206,11 +222,21 @@ Item {
}
}
ColumnLayout {
id: rightPanelItem
Control.StackView {
id: rightPanelStackView
Layout.fillWidth: true
Layout.fillHeight: true
}
Component {
id: contactEditionComp
ContactEdition {
property string objectName: "contactEdition"
onCloseEdition: {
rightPanelStackView.pop(Control.StackView.Immediate)
mainItem.contactEditionClosed()
}
}
}
}
}
}

View file

@ -15,6 +15,12 @@ AbstractMainPage {
signal listViewUpdated()
onSelectedRowHistoryGuiChanged: {
if (selectedRowHistoryGui) rightPanelStackView.replace(contactDetailComp, Control.StackView.Immediate)
else rightPanelStackView.replace(emptySelection, Control.StackView.Immediate)
}
rightPanelStackView.initialItem: emptySelection
onNoItemButtonPressed: goToNewCall()
showDefaultItem: listStackView.currentItem.listView && listStackView.currentItem.listView.count === 0 && listStackView.currentItem.listView.model.sourceModel.count === 0 || false
@ -161,7 +167,7 @@ AbstractMainPage {
}
currentIndex: -1
flickDeceleration: 10000
spacing: 10 * DefaultStyle.dp
delegate: Item {
@ -259,9 +265,7 @@ AbstractMainPage {
fillMode: Image.PreserveAspectFit
}
onClicked: {
var addr = modelData.core.remoteAddress
var addressEnd = "@sip.linphone.org"
if (!addr.endsWith(addressEnd)) addr += addressEnd
var addr = UtilsCpp.generateLinphoneSipAddress(modelData.core.remoteAddress)
callObj = UtilsCpp.createCall(addr)
}
}
@ -351,9 +355,7 @@ AbstractMainPage {
property var callObj
onCallButtonPressed: (address) => {
var addressEnd = "@sip.linphone.org"
if (!address.endsWith(addressEnd)) address += addressEnd
callObj = UtilsCpp.createCall(address)
callObj = UtilsCpp.createCall(UtilsCpp.generateLinphoneSipAddress(address))
// var window = UtilsCpp.getCallsWindow()
}
}
@ -361,21 +363,6 @@ AbstractMainPage {
}
}
rightPanelContent: Control.StackView {
id: rightPanelStackView
Layout.fillWidth: true
Layout.fillHeight: true
initialItem: emptySelection
Connections {
target: mainItem
onSelectedRowHistoryGuiChanged: {
if (mainItem.selectedRowHistoryGui) rightPanelStackView.replace(contactDetailComp, Control.StackView.Immediate)
else rightPanelStackView.replace(emptySelection, Control.StackView.Immediate)
}
}
onCurrentItemChanged: {
}
}
Component{
id: emptySelection
Item{}
@ -406,15 +393,8 @@ AbstractMainPage {
iconSource: AppIcons.plusCircle
}
onClicked: {
// console.debug("[CallPage.qml] TODO : add to contact")
var friendGui = Qt.createQmlObject('import Linphone
FriendGui{
}', contactDetail)
detailOptions.close()
friendGui.core.givenName = UtilsCpp.getGivenNameFromFullName(contactDetail.contactName)
friendGui.core.familyName = UtilsCpp.getFamilyNameFromFullName(contactDetail.contactName)
friendGui.core.defaultAddress = contactDetail.contactAddress
rightPanelStackView.push(editContact, {"contact": friendGui, "title": qsTr("Ajouter contact"), "saveButtonText": qsTr("Créer")})
mainItem.createContact(contactDetail.contactName, contactDetail.contactAddress)
}
}
Button {
@ -558,12 +538,6 @@ AbstractMainPage {
}
}
}
Component {
id: editContact
ContactEdition {
onCloseEdition: rightPanelStackView.pop(Control.StackView.Immediate)
}
}
component LabelButton: ColumnLayout {
id: labelButton

View file

@ -14,16 +14,29 @@ AbstractMainPage {
// disable left panel contact list interaction while a contact is being edited
property bool leftPanelEnabled: true
property FriendGui selectedContact
onSelectedContactChanged: {
if (selectedContact) {
if (!rightPanelStackView.currentItem || rightPanelStackView.currentItem.objectName != "contactDetail") rightPanelStackView.push(contactDetail)
} else {
if (rightPanelStackView.currentItem && rightPanelStackView.currentItem.objectName === "contactDetail") rightPanelStackView.clear()
}
}
signal forceListsUpdate()
onNoItemButtonPressed: createNewContact()
onNoItemButtonPressed: createContact("", "")
function createNewContact() {
console.debug("[ContactPage]User: create new contact")
var friendGui = Qt.createQmlObject('import Linphone
FriendGui{
}', contactDetail)
rightPanelStackView.replace(editContact, {"contact": friendGui, "title": qsTr("Nouveau contact"), "saveButtonText": qsTr("Créer")})
// rightPanelStackView.initialItem: contactDetail
Binding {
mainItem.showDefaultItem: false
when: rightPanelStackView.currentItem && rightPanelStackView.currentItem.objectName == "contactEdition"
restoreMode: Binding.RestoreBinding
}
Connections {
target: mainItem
onContactEditionClosed: {
mainItem.forceListsUpdate()
// mainItem.rightPanelStackView.replace(contactDetail, Control.StackView.Immediate)
}
}
showDefaultItem: contactList.model.sourceModel.count === 0
@ -71,7 +84,7 @@ AbstractMainPage {
fillMode: Image.PreserveAspectFit
}
onClicked: {
mainItem.createNewContact()
mainItem.createContact("", "")
}
}
}
@ -80,6 +93,12 @@ AbstractMainPage {
Layout.topMargin: 30 * DefaultStyle.dp
Layout.leftMargin: leftPanel.sideMargin
enabled: mainItem.leftPanelEnabled
Button {
onClicked: {
favoriteList.currentIndex = -1
contactList.currentIndex = -1
}
}
SearchBar {
id: searchBar
Layout.rightMargin: leftPanel.sideMargin
@ -227,20 +246,11 @@ AbstractMainPage {
}
}
}
rightPanelContent: Control.StackView {
id: rightPanelStackView
Layout.fillWidth: true
Layout.fillHeight: true
initialItem: contactDetail
Binding {
mainItem.showDefaultItem: false
when: rightPanelStackView.currentItem.objectName == "contactEdition"
restoreMode: Binding.RestoreBinding
}
}
Component {
id: contactDetail
RowLayout {
property string objectName: "contactDetail"
visible: mainItem.selectedContact != undefined
Layout.fillWidth: true
Layout.fillHeight: true
@ -263,7 +273,7 @@ AbstractMainPage {
anchors.fill: parent
source: AppIcons.pencil
}
onClicked: rightPanelStackView.replace(editContact, Control.StackView.Immediate)
onClicked: mainItem.editContact(mainItem.selectedContact)
}
detailContent: ColumnLayout {
Layout.fillWidth: false
@ -475,24 +485,24 @@ AbstractMainPage {
IconLabelButton {
Layout.fillWidth: true
Layout.leftMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.preferredHeight: 50 * DefaultStyle.dp
iconSize: 24 * DefaultStyle.dp
iconSource: AppIcons.pencil
text: qsTr("Edit")
onClicked: rightPanelStackView.replace(editContact, Control.StackView.Immediate)
onClicked: mainItem.editContact(mainItem.selectedContact)
}
Rectangle {
Layout.fillWidth: true
Layout.leftMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.preferredHeight: 1 * DefaultStyle.dp
color: DefaultStyle.main2_200
}
IconLabelButton {
Layout.fillWidth: true
Layout.leftMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.preferredHeight: 50 * DefaultStyle.dp
iconSize: 24 * DefaultStyle.dp
iconSource: mainItem.selectedContact && mainItem.selectedContact.core.starred ? AppIcons.heartFill : AppIcons.heart
@ -502,14 +512,14 @@ AbstractMainPage {
Rectangle {
Layout.fillWidth: true
Layout.leftMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.preferredHeight: 1 * DefaultStyle.dp
color: DefaultStyle.main2_200
}
IconLabelButton {
Layout.fillWidth: true
Layout.leftMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.preferredHeight: 50 * DefaultStyle.dp
iconSize: 24 * DefaultStyle.dp
iconSource: AppIcons.shareNetwork
@ -519,14 +529,14 @@ AbstractMainPage {
Rectangle {
Layout.fillWidth: true
Layout.leftMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.preferredHeight: 1 * DefaultStyle.dp
color: DefaultStyle.main2_200
}
IconLabelButton {
Layout.fillWidth: true
Layout.leftMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.preferredHeight: 50 * DefaultStyle.dp
iconSize: 24 * DefaultStyle.dp
iconSource: AppIcons.bellSlash
@ -536,14 +546,14 @@ AbstractMainPage {
Rectangle {
Layout.fillWidth: true
Layout.leftMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.preferredHeight: 1 * DefaultStyle.dp
color: DefaultStyle.main2_200
}
IconLabelButton {
Layout.fillWidth: true
Layout.leftMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.preferredHeight: 50 * DefaultStyle.dp
iconSize: 24 * DefaultStyle.dp
iconSource: AppIcons.empty
@ -553,14 +563,14 @@ AbstractMainPage {
Rectangle {
Layout.fillWidth: true
Layout.leftMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.preferredHeight: 1 * DefaultStyle.dp
color: DefaultStyle.main2_200
}
IconLabelButton {
Layout.fillWidth: true
Layout.leftMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.rightMargin: 15 * DefaultStyle.dp
Layout.preferredHeight: 50 * DefaultStyle.dp
iconSize: 24 * DefaultStyle.dp
iconSource: AppIcons.trashCan
@ -579,16 +589,4 @@ AbstractMainPage {
}
}
}
Component {
id: editContact
ContactEdition {
id: contactEdition
property string objectName: "contactEdition"
contact: mainItem.selectedContact
onCloseEdition: {
mainItem.forceListsUpdate()
rightPanelStackView.replace(contactDetail, Control.StackView.Immediate)
}
}
}
}

View file

@ -34,6 +34,7 @@ QtObject {
property string chatTeardropTextSelected: "image://internal/chat-teardrop-text-selected.svg"
property string usersThree: "image://internal/users-three.svg"
property string usersThreeSelected: "image://internal/users-three-selected.svg"
property string userPlus: "image://internal/user-plus.svg"
property string noItemImage: "image://internal/noItemImage.svg"
property string verticalDots: "image://internal/dots-three-vertical.svg"
property string more: "image://internal/more.svg"