copy message text to clipboard + improve chat message and sending area ui

This commit is contained in:
Gaelle Braud 2025-05-07 11:02:01 +02:00
parent ecd9373df9
commit 73b83771be
9 changed files with 197 additions and 113 deletions

View file

@ -97,10 +97,12 @@ void ChatList::setSelf(QSharedPointer<ChatList> me) {
std::find_if(list.begin(), list.end(), [chatRoom](const QSharedPointer<ChatCore> &item) {
return (item && item->getModel()->getMonitor() == chatRoom);
});
if (found != list.end()) {
qDebug() << "chat room created, add it to the list";
if (found == list.end()) {
auto model = createChatCore(chatRoom);
mModelConnection->invokeToCore([this, model]() { add(model); });
mModelConnection->invokeToCore([this, model]() {
add(model);
emit chatAdded();
});
}
}
}

View file

@ -47,6 +47,7 @@ signals:
void lUpdate();
void filterChanged(QString filter);
void chatRemoved(ChatGui *chat);
void chatAdded();
private:
QString mFilter;

View file

@ -28,6 +28,7 @@ DEFINE_ABSTRACT_OBJECT(ChatProxy)
ChatProxy::ChatProxy(QObject *parent) : LimitProxy(parent) {
mList = ChatList::create();
setSourceModel(mList.get());
setDynamicSortFilter(true);
}
ChatProxy::~ChatProxy() {
@ -43,8 +44,10 @@ void ChatProxy::setSourceModel(QAbstractItemModel *model) {
connect(this, &ChatProxy::filterTextChanged, newChatList,
[this, newChatList] { emit newChatList->filterChanged(getFilterText()); });
connect(newChatList, &ChatList::chatRemoved, this, &ChatProxy::chatRemoved);
connect(newChatList, &ChatList::chatAdded, this, [this] { invalidate(); });
}
setSourceModels(new SortFilterList(model));
auto firstList = new SortFilterList(model, Qt::AscendingOrder);
setSourceModels(firstList);
sort(0);
}

View file

@ -13,9 +13,11 @@ Button {
radius: Math.round(5 * DefaultStyle.dp)
shadowEnabled: mainItem.activeFocus || hovered
style: ButtonStyle.hoveredBackground
property bool inverseLayout: false
contentItem: RowLayout {
spacing: Math.round(5 * DefaultStyle.dp)
layoutDirection: mainItem.inverseLayout ? Qt.RightToLeft: Qt.LeftToRight
EffectImage {
imageSource: mainItem.icon.source
imageWidth: mainItem.icon.width
@ -33,6 +35,7 @@ Button {
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
Layout.preferredWidth: textMetrics.advanceWidth
Layout.fillWidth: true
wrapMode: Text.WrapAnywhere
text: mainItem.text
maximumLineCount: 1

View file

@ -8,137 +8,177 @@ 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
RowLayout {
Control.Control {
id: mainItem
property color backgroundColor
property bool isFirstMessage
property string imgUrl
spacing: 0
property ChatMessageGui chatMessage
property string fromAddress: chatMessage? chatMessage.core.fromAddress : ""
property bool isRemoteMessage: chatMessage? chatMessage.core.isRemoteMessage : false
property bool isFromChatGroup: chatMessage? chatMessage.core.isFromChatGroup : false
hoverEnabled: true
signal messageDeletionRequested()
Avatar {
id: avatar
visible: mainItem.isFromChatGroup
opacity: mainItem.isRemoteMessage && mainItem.isFirstMessage ? 1 : 0
Layout.preferredWidth: 26 * DefaultStyle.dp
Layout.preferredHeight: 26 * DefaultStyle.dp
Layout.alignment: Qt.AlignTop
Layout.topMargin: isFirstMessage ? 16 * DefaultStyle.dp : 0
_address: chatMessage ? chatMessage.core.fromAddress : ""
}
Control.Control {
Layout.topMargin: isFirstMessage ? 16 * DefaultStyle.dp : 0
Layout.leftMargin: mainItem.isFromChatGroup ? Math.round(9 * DefaultStyle.dp) : 0
Layout.preferredWidth: Math.min(implicitWidth, mainItem.maxWidth - avatar.implicitWidth)
// Layout.topMargin: name.visible ? Math.round(7 * DefaultStyle.dp) : 0
topPadding: Math.round(12 * DefaultStyle.dp)
bottomPadding: Math.round(12 * DefaultStyle.dp)
leftPadding: Math.round(18 * DefaultStyle.dp)
rightPadding: Math.round(18 * DefaultStyle.dp)
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: (mouse) => {
console.log("message clicked")
if (mouse.button === Qt.RightButton) {
optionsMenu.x = mouse.x
optionsMenu.open()
}
background: Item {
anchors.fill: parent
Text {
id: fromNameText
visible: mainItem.isFromChatGroup && mainItem.isRemoteMessage && mainItem.isFirstMessage
anchors.top: parent.top
maximumLineCount: 1
width: implicitWidth
x: chatBubble.x
text: mainItem.chatMessage.core.fromName
color: DefaultStyle.main2_500main
font {
pixelSize: Typography.p4.pixelSize
weight: Typography.p4.weight
}
}
Popup {
id: optionsMenu
}
contentItem: RowLayout {
spacing: 0
layoutDirection: mainItem.isRemoteMessage ? Qt.LeftToRight : Qt.RightToLeft
Avatar {
id: avatar
visible: mainItem.isFromChatGroup
opacity: mainItem.isRemoteMessage && mainItem.isFirstMessage ? 1 : 0
Layout.preferredWidth: 26 * DefaultStyle.dp
Layout.preferredHeight: 26 * DefaultStyle.dp
Layout.alignment: Qt.AlignTop
Layout.topMargin: isFirstMessage ? 16 * DefaultStyle.dp : 0
_address: chatMessage ? chatMessage.core.fromAddress : ""
}
Control.Control {
id: chatBubble
Layout.topMargin: isFirstMessage ? 16 * DefaultStyle.dp : 0
Layout.leftMargin: mainItem.isFromChatGroup ? Math.round(9 * DefaultStyle.dp) : 0
Layout.preferredWidth: Math.min(implicitWidth, mainItem.maxWidth - avatar.implicitWidth)
spacing: Math.round(2 * DefaultStyle.dp)
topPadding: Math.round(12 * DefaultStyle.dp)
bottomPadding: Math.round(12 * DefaultStyle.dp)
leftPadding: Math.round(18 * DefaultStyle.dp)
rightPadding: Math.round(18 * DefaultStyle.dp)
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: (mouse) => {
if (mouse.button === Qt.RightButton) {
optionsMenu.open()
}
}
}
background: Item {
anchors.fill: parent
Rectangle {
id: popupBackground
anchors.fill: parent
color: DefaultStyle.grey_0
color: mainItem.backgroundColor
radius: Math.round(16 * DefaultStyle.dp)
}
MultiEffect {
source: popupBackground
anchors.fill: popupBackground
shadowEnabled: true
shadowBlur: 0.1
shadowColor: DefaultStyle.grey_1000
shadowOpacity: 0.4
Rectangle {
visible: mainItem.isFirstMessage && mainItem.isRemoteMessage
anchors.top: parent.top
anchors.left: parent.left
width: Math.round(parent.width / 4)
height: Math.round(parent.height / 4)
color: mainItem.backgroundColor
}
Rectangle {
visible: mainItem.isFirstMessage && !mainItem.isRemoteMessage
anchors.bottom: parent.bottom
anchors.right: parent.right
width: Math.round(parent.width / 4)
height: Math.round(parent.height / 4)
color: mainItem.backgroundColor
}
}
contentItem: ColumnLayout {
IconLabelButton {
//: "Supprimer"
text: qsTr("chat_message_delete")
icon.source: AppIcons.trashCan
spacing: Math.round(10 * DefaultStyle.dp)
id: contentLayout
Image {
visible: mainItem.imgUrl != undefined
id: contentimage
}
Text {
visible: modelData.core.text != undefined
text: modelData.core.text
Layout.fillWidth: true
onClicked: {
mainItem.messageDeletionRequested()
optionsMenu.close()
color: DefaultStyle.main2_700
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight
}
}
Text {
Layout.alignment: Qt.AlignRight
text: UtilsCpp.formatDate(modelData.core.timestamp, true, false)
color: DefaultStyle.main2_500main
font {
pixelSize: Typography.p3.pixelSize
weight: Typography.p3.weight
}
style: ButtonStyle.noBackgroundRed
}
}
}
background: Item {
anchors.fill: parent
Rectangle {
anchors.fill: parent
color: mainItem.backgroundColor
radius: Math.round(16 * DefaultStyle.dp)
}
Rectangle {
visible: mainItem.isFirstMessage && mainItem.isRemoteMessage
anchors.top: parent.top
anchors.left: parent.left
width: Math.round(parent.width / 4)
height: Math.round(parent.height / 4)
color: mainItem.backgroundColor
}
Rectangle {
visible: mainItem.isFirstMessage && !mainItem.isRemoteMessage
anchors.bottom: parent.bottom
anchors.right: parent.right
width: Math.round(parent.width / 4)
height: Math.round(parent.height / 4)
color: mainItem.backgroundColor
}
}
contentItem: ColumnLayout {
id: contentLayout
Image {
visible: mainItem.imgUrl != undefined
id: contentimage
}
Text {
visible: modelData.core.text != undefined
text: modelData.core.text
Layout.fillWidth: true
color: DefaultStyle.main2_700
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight
RowLayout {
id: actionsLayout
visible: mainItem.hovered || optionsMenu.hovered || optionsMenu.popup.opened || emojiButton.hovered
Layout.leftMargin: Math.round(8 * DefaultStyle.dp)
Layout.rightMargin: Math.round(8 * DefaultStyle.dp)
Layout.alignment: Qt.AlignVCenter
// Layout.fillWidth: true
spacing: Math.round(7 * DefaultStyle.dp)
layoutDirection: mainItem.isRemoteMessage ? Qt.LeftToRight : Qt.RightToLeft
PopupButton {
id: optionsMenu
popup.padding: 0
popup.contentItem: ColumnLayout {
spacing: 0
IconLabelButton {
inverseLayout: true
//: "Copy"
text: qsTr("chat_message_copy")
icon.source: AppIcons.copy
// spacing: Math.round(10 * DefaultStyle.dp)
Layout.fillWidth: true
Layout.preferredHeight: 45 * DefaultStyle.dp
onClicked: {
var success = UtilsCpp.copyToClipboard(modelData.core.text)
//: Copied
if (success) UtilsCpp.showInformationPopup(qsTr("chat_message_copied_to_clipboard_title"),
//: "in clipboard"
qsTr("chat_message_copied_to_clipboard_toast"))
}
}
IconLabelButton {
inverseLayout: true
//: "Delete"
text: qsTr("chat_message_delete")
icon.source: AppIcons.trashCan
// spacing: Math.round(10 * DefaultStyle.dp)
Layout.fillWidth: true
Layout.preferredHeight: 45 * DefaultStyle.dp
onClicked: {
mainItem.messageDeletionRequested()
optionsMenu.close()
}
style: ButtonStyle.hoveredBackgroundRed
}
}
}
Text {
Layout.alignment: Qt.AlignRight
text: UtilsCpp.formatDate(modelData.core.timestamp, true, false)
color: DefaultStyle.main2_500main
font {
pixelSize: Typography.p3.pixelSize
weight: Typography.p3.weight
}
BigButton {
id: emojiButton
style: ButtonStyle.noBackground
icon.source: AppIcons.smiley
}
}
Item{Layout.fillWidth: true}
}
}

View file

@ -14,13 +14,41 @@ ListView {
property color backgroundColor
spacing: Math.round(4 * DefaultStyle.dp)
Component.onCompleted: positionViewAtEnd()
// Component.onCompleted: positionViewAtIndex(chatMessageProxy.findFirstUnreadIndex(), ListView.Visible)
onCountChanged: positionViewAtEnd()
onAtYEndChanged: if (atYEnd) chat.core.lMarkAsRead();
onChatChanged: if (visible) {
var index = chatMessageProxy.findFirstUnreadIndex()
console.log("visible, first unread at index", index)
mainItem.positionViewAtIndex(index, ListView.Visible)
}
RoundButton {
icon.source: AppIcons.downArrow
// Layout.preferredWidth: 40 * DefaultStyle.dp
// Layout.preferredHeight: 40 * DefaultStyle.dp
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.bottomMargin: Math.round(18 * DefaultStyle.dp)
anchors.rightMargin: Math.round(18 * DefaultStyle.dp)
onClicked: {
var index = chatMessageProxy.findFirstUnreadIndex()
console.log("clicked, first unread at index", index)
mainItem.positionViewAtIndex(index, ListView.Visible)
// var chatMessage = chatMessageProxy.getChatMessageAtIndex(index)
// if (chatMessage && !chatMessage.core.isRead) chatMessage.core.lMarkAsRead()
}
}
model: ChatMessageProxy {
id: chatMessageProxy
chatGui: mainItem.chat
onCountChanged: {
var indexToSelect = mainItem.currentIndex
mainItem.currentIndex = -1
mainItem.currentIndex = indexToSelect
}
}
header: Item {
@ -31,7 +59,7 @@ ListView {
chatMessage: modelData
property real maxWidth: Math.round(mainItem.width * (3/4))
// height: childrenRect.height
// width: childrenRect.width
width: mainItem.width
property var previousIndex: index - 1
property var previousFromAddress: chatMessageProxy.getChatMessageAtIndex(index-1)?.core.fromAddress
backgroundColor: isRemoteMessage ? DefaultStyle.main2_100 : DefaultStyle.main1_100
@ -52,7 +80,7 @@ ListView {
bottomPadding: Math.round(5 * DefaultStyle.dp)
background: Rectangle {
anchors.fill: parent
color: mainItem.panelColor
color: mainItem.backgroundColor
}
contentItem: RowLayout {
id: composeLayout

View file

@ -80,7 +80,7 @@ RowLayout {
ChatMessagesListView {
id: chatMessagesListView
height: contentHeight
backgroundColor: panelColor
backgroundColor: splitPanel.panelColor
width: parent.width - anchors.leftMargin - anchors.rightMargin
chat: mainItem.chat
anchors.top: parent.top
@ -146,8 +146,8 @@ RowLayout {
Layout.fillWidth: true
leftPadding: Math.round(15 * DefaultStyle.dp)
rightPadding: Math.round(15 * DefaultStyle.dp)
topPadding: Math.round(16 * DefaultStyle.dp)
bottomPadding: Math.round(16 * DefaultStyle.dp)
topPadding: Math.round(15 * DefaultStyle.dp)
bottomPadding: Math.round(15 * DefaultStyle.dp)
background: Rectangle {
id: inputBackground
anchors.fill: parent
@ -188,9 +188,15 @@ RowLayout {
height: sendingAreaFlickable.height
anchors.left: parent.left
anchors.right: parent.right
wrapMode: TextEdit.WordWrap
//: Say something : placeholder text for sending message text area
placeholderText: qsTr("Dites quelque chose…")
placeholderTextColor: DefaultStyle.main2_400
color: DefaultStyle.main2_700
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight
}
onCursorRectangleChanged: sendingAreaFlickable.ensureVisible(cursorRectangle)
property string previousText
Component.onCompleted: previousText = text

View file

@ -684,8 +684,8 @@ AbstractMainPage {
KeyNavigation.up: deletePopup
KeyNavigation.down: joinButton
onClicked: {
UtilsCpp.copyToClipboard(mainItem.selectedConference.core.uri)
UtilsCpp.showInformationPopup(qsTr("saved"),
var success = UtilsCpp.copyToClipboard(mainItem.selectedConference.core.uri)
if (success) UtilsCpp.showInformationPopup(qsTr("saved"),
//: "Adresse de la réunion copiée"
qsTr("meeting_address_copied_to_clipboard_toast"))
}

View file

@ -113,7 +113,8 @@
color: {
normal: Linphone.DefaultStyle.grey_500,
hovered: Linphone.DefaultStyle.grey_600,
pressed: Linphone.DefaultStyle.main2_400
pressed: Linphone.DefaultStyle.main2_400,
hovered: Linphone.DefaultStyle.main2_400
},
text: {
normal: Linphone.DefaultStyle.grey_0,