From 924224abc5ad2993cf82e186b71b8f847ed71a8e Mon Sep 17 00:00:00 2001 From: Gaelle Braud Date: Wed, 1 Oct 2025 10:34:03 +0200 Subject: [PATCH] enlarge image size when single in message #LINQT-2008 --- .../content/ChatMessageContentProxy.cpp | 25 ++++----- .../content/ChatMessageContentProxy.hpp | 2 + Linphone/tool/Constants.hpp | 4 +- Linphone/view/CMakeLists.txt | 2 + .../Container/Chat/ChatFilesGridLayout.qml | 12 ++-- .../Display/Chat/AnimatedImageFileView.qml | 56 +++++++++++++++++++ .../Display/Chat/ChatMessageContent.qml | 42 ++++++++++++-- .../view/Control/Display/Chat/FileView.qml | 16 ++---- .../Control/Display/Chat/ImageFileView.qml | 56 +++++++++++++++++++ 9 files changed, 175 insertions(+), 40 deletions(-) create mode 100644 Linphone/view/Control/Display/Chat/AnimatedImageFileView.qml create mode 100644 Linphone/view/Control/Display/Chat/ImageFileView.qml diff --git a/Linphone/core/chat/message/content/ChatMessageContentProxy.cpp b/Linphone/core/chat/message/content/ChatMessageContentProxy.cpp index 3e721fb91..f0f6fae9f 100644 --- a/Linphone/core/chat/message/content/ChatMessageContentProxy.cpp +++ b/Linphone/core/chat/message/content/ChatMessageContentProxy.cpp @@ -57,16 +57,16 @@ void ChatMessageContentProxy::setChatMessageGui(ChatMessageGui *chat) { getListModel()->setChatMessageGui(chat); } -// ChatMessageGui *ChatMessageContentProxy::getChatMessageAtIndex(int i) { -// auto model = getListModel(); -// auto sourceIndex = mapToSource(index(i, 0)).row(); -// if (model) { -// auto chat = model->getAt(sourceIndex); -// if (chat) return new ChatMessageGui(chat); -// else return nullptr; -// } -// return nullptr; -// } +ChatMessageContentGui *ChatMessageContentProxy::getChatMessageContentAtIndex(int i) { + auto model = getListModel(); + auto sourceIndex = mapToSource(index(i, 0)).row(); + if (model) { + auto chat = model->getAt(sourceIndex); + if (chat) return new ChatMessageContentGui(chat); + else return nullptr; + } + return nullptr; +} void ChatMessageContentProxy::addFiles(const QStringList &paths) { auto model = getListModel(); @@ -99,8 +99,5 @@ bool ChatMessageContentProxy::SortFilterList::filterAcceptsRow(int sourceRow, co bool ChatMessageContentProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const { - auto l = getItemAtSource(sourceLeft.row()); - auto r = getItemAtSource(sourceRight.row()); - if (l && r) return l->getTimestamp() <= r->getTimestamp(); - else return true; + return true; } diff --git a/Linphone/core/chat/message/content/ChatMessageContentProxy.hpp b/Linphone/core/chat/message/content/ChatMessageContentProxy.hpp index 34c7ca933..fe1f9fdec 100644 --- a/Linphone/core/chat/message/content/ChatMessageContentProxy.hpp +++ b/Linphone/core/chat/message/content/ChatMessageContentProxy.hpp @@ -45,6 +45,8 @@ public: ChatMessageGui *getChatMessageGui(); void setChatMessageGui(ChatMessageGui *chat); + Q_INVOKABLE ChatMessageContentGui *getChatMessageContentAtIndex(int i); + void setSourceModel(QAbstractItemModel *sourceModel) override; Q_INVOKABLE void addFiles(const QStringList &paths); diff --git a/Linphone/tool/Constants.hpp b/Linphone/tool/Constants.hpp index c88536abf..3d1f7a2ca 100644 --- a/Linphone/tool/Constants.hpp +++ b/Linphone/tool/Constants.hpp @@ -114,8 +114,8 @@ public: // Max image size in bytes. (1Mb) static constexpr qint64 MaxImageSize = 1024000; // In Bytes. static constexpr qint64 FileSizeLimit = 524288000; // In Bytes. - static constexpr int ThumbnailImageFileWidth = 100; - static constexpr int ThumbnailImageFileHeight = 100; + static constexpr int ThumbnailImageFileWidth = 285; + static constexpr int ThumbnailImageFileHeight = 345; //-------------------------------------------------------------------------------- // LINPHONE diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index 76a170815..9e7d1f520 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -67,6 +67,8 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Control/Display/Chat/Event.qml view/Control/Display/Chat/EphemeralEvent.qml view/Control/Display/Chat/FileView.qml + view/Control/Display/Chat/ImageFileView.qml + view/Control/Display/Chat/AnimatedImageFileView.qml view/Control/Display/Contact/Avatar.qml view/Control/Display/Contact/Contact.qml view/Control/Display/Contact/Presence.qml diff --git a/Linphone/view/Control/Container/Chat/ChatFilesGridLayout.qml b/Linphone/view/Control/Container/Chat/ChatFilesGridLayout.qml index 21606d897..b2c26d5cb 100644 --- a/Linphone/view/Control/Container/Chat/ChatFilesGridLayout.qml +++ b/Linphone/view/Control/Container/Chat/ChatFilesGridLayout.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Layouts import QtQml.Models +import QtQuick.Controls.Basic as Control import Linphone import UtilsCpp @@ -8,7 +9,7 @@ import UtilsCpp // ============================================================================= GridLayout { id: mainItem - property ChatMessageGui chatMessageGui: null + property ChatMessageContentProxy proxyModel property bool isHoveringFile: false property int itemCount: delModel.count property int itemWidth: Math.round(95 * DefaultStyle.dp) @@ -40,15 +41,10 @@ GridLayout { return bestCols; } - + Repeater { id: delModel - model: ChatMessageContentProxy { - id: contentProxy - filterType: ChatMessageContentProxy.FilterContentType.File - chatMessageGui: mainItem.chatMessageGui - } - + model: mainItem.proxyModel delegate: FileView { id: avatarCell contentGui: modelData diff --git a/Linphone/view/Control/Display/Chat/AnimatedImageFileView.qml b/Linphone/view/Control/Display/Chat/AnimatedImageFileView.qml new file mode 100644 index 000000000..161da0e23 --- /dev/null +++ b/Linphone/view/Control/Display/Chat/AnimatedImageFileView.qml @@ -0,0 +1,56 @@ +import QtQuick +import QtQuick.Controls as Control +import QtQuick.Layouts +import QtMultimedia + +import Linphone +import UtilsCpp +import 'qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js' as Utils + +// ============================================================================= + + +// --------------------------------------------------------------------- +// Separated file to show a single image bigger in chat message +// The FileView file does not allow that as it is a Loader and the image +// is reloaded everytime the message becomes visible again. It causes the +// chat message not to be able to adapt its size according to the painted +// size of the image +// --------------------------------------------------------------------- +AnimatedImage { + id: mainItem + property ChatMessageContentGui contentGui + + mipmap: false//SettingsModel.mipmapEnabled + autoTransform: true + fillMode: Image.PreserveAspectFit + source: contentGui && UtilsCpp.isAnimatedImage(contentGui.core.filePath) ? ('file:/'+ contentGui.core.filePath) : "" + + states: State { + name: 'hovered' + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + propagateComposedEvents: true + cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor + // Changing cursor in MouseArea seems not to work with the Loader + // Use override cursor for this case + onContainsMouseChanged: { + if (containsMouse) UtilsCpp.setGlobalCursor(Qt.PointingHandCursor) + else UtilsCpp.restoreGlobalCursor() + mainItem.state = containsMouse ? 'hovered' : '' + } + onPressed: (mouse) => { + mouse.accepted = true + // if(SettingsModel.isVfsEncrypted){ + // window.attachVirtualWindow(Utils.buildCommonDialogUri('FileViewDialog'), { + // contentGui: mainItem.contentGui, + // }, function (status) { + // }) + // }else + mainItem.contentGui.core.lOpenFile() + } + } +} \ No newline at end of file diff --git a/Linphone/view/Control/Display/Chat/ChatMessageContent.qml b/Linphone/view/Control/Display/Chat/ChatMessageContent.qml index e1ecfe7ea..80f3f38bf 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessageContent.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessageContent.qml @@ -3,6 +3,7 @@ import QtQuick.Effects import QtQuick.Layouts import QtQuick.Controls.Basic as Control import Linphone +import UtilsCpp // ============================================================================= // Simple content display without reply and forward. These modules need to be splitted because of cyclic dependencies. @@ -29,12 +30,17 @@ ColumnLayout { spacing: Math.round(5 * DefaultStyle.dp) property int padding: Math.round(10 * DefaultStyle.dp) + + property ChatMessageContentProxy filescontentProxy: ChatMessageContentProxy { + filterType: ChatMessageContentProxy.FilterContentType.File + chatMessageGui: mainItem.chatMessageGui + } // VOICE MESSAGES Repeater { id: messagesVoicesList visible: count > 0 - model: ChatMessageContentProxy{ + model: ChatMessageContentProxy { filterType: ChatMessageContentProxy.FilterContentType.Voice chatMessageGui: mainItem.chatMessageGui } @@ -70,15 +76,39 @@ ColumnLayout { onMouseEvent: (event) => mainItem.mouseEvent(event) } } + // SINGLE FILE + ImageFileView { + id: singleImageFile + visible: mainItem.filescontentProxy.count === 1 && source !== "" && UtilsCpp.isImage(contentGui.core.filePath) + contentGui: mainItem.filescontentProxy.count === 1 + ? mainItem.filescontentProxy.getChatMessageContentAtIndex(0) + : null + width: Math.round(285 * DefaultStyle.dp) + Layout.alignment: Qt.AlignHCenter + fillMode: Image.PreserveAspectFit + } + AnimatedImageFileView { + id: singleAnimatedImageFile + visible: mainItem.filescontentProxy.count === 1 && source !== "" && UtilsCpp.isAnimatedImage(contentGui.core.filePath) + contentGui: mainItem.filescontentProxy.count === 1 + ? mainItem.filescontentProxy.getChatMessageContentAtIndex(0) + : null + Layout.preferredWidth: Math.round(285 * DefaultStyle.dp) + Layout.preferredHeight: paintedHeight + Layout.alignment: Qt.AlignHCenter + fillMode: Image.PreserveAspectFit + } // FILES ChatFilesGridLayout { id: messageFilesList - visible: itemCount > 0 - Layout.fillWidth: true + visible: mainItem.filescontentProxy.count > 0 + && !singleImageFile.visible + && !singleAnimatedImageFile.visible + Layout.fillWidth: visible + Layout.fillHeight: visible maxWidth: Math.round(115*3 * DefaultStyle.dp) - Layout.fillHeight: true - // Layout.preferredHeight: contentHeight - chatMessageGui: mainItem.chatMessageGui + // Layout.fillHeight: true + proxyModel: visible ? mainItem.filescontentProxy : null // onIsHoveringFileChanged: mainItem.isFileHoveringChanged(isHoveringFile) // borderWidth: mainItem.fileBorderWidth // property int availableSection: mainItem.availableWidth / mainItem.filesBestWidth diff --git a/Linphone/view/Control/Display/Chat/FileView.qml b/Linphone/view/Control/Display/Chat/FileView.qml index 9a6153e4d..649aa8945 100644 --- a/Linphone/view/Control/Display/Chat/FileView.qml +++ b/Linphone/view/Control/Display/Chat/FileView.qml @@ -26,8 +26,6 @@ Item { property bool isImage: UtilsCpp.isImage(filePath) property bool isPdf: UtilsCpp.isPdf(filePath) property bool isThumbnail: isVideo || isImage || isPdf - property int overriddenWidth - property int overriddenHeight // property to change default view display property bool showAsSquare: true // default image @@ -40,6 +38,7 @@ Item { ? AppIcons.fileText : AppIcons.file : '' + property var thumbnailFillMode: Image.PreserveAspectCrop Connections { enabled: contentGui @@ -60,12 +59,9 @@ Item { id: thumbnailImage Item { id: thumbnailSource - property bool isVideo: UtilsCpp.isVideo(mainItem.filePath) - property bool isImage: UtilsCpp.isImage(mainItem.filePath) - property bool isPdf: UtilsCpp.isPdf(mainItem.filePath) Image { anchors.fill: parent - visible: thumbnailSource.isPdf + visible: mainItem.isPdf source: AppIcons.filePdf sourceSize.width: mainItem.width sourceSize.height: mainItem.height @@ -82,24 +78,24 @@ Item { } Image { id: image - visible: thumbnailSource.isImage && status !== Image.Loading + visible: mainItem.isImage && status !== Image.Loading mipmap: false//SettingsModel.mipmapEnabled source: mainItem.thumbnail sourceSize.width: mainItem.width sourceSize.height: mainItem.height autoTransform: true - fillMode: Image.PreserveAspectCrop anchors.fill: parent + fillMode: mainItem.thumbnailFillMode } Rectangle { - visible: thumbnailSource.isVideo + visible: mainItem.isVideo color: DefaultStyle.grey_1000 anchors.fill: parent Video { id: videoThumbnail anchors.fill: parent position: 100 - source: "file:///" + mainItem.filePath + source: mainItem.isVideo ? "file:///" + mainItem.filePath : "" fillMode: playbackState === MediaPlayer.PlayingState ? VideoOutput.PreserveAspectFit : VideoOutput.PreserveAspectCrop MouseArea { propagateComposedEvents: false diff --git a/Linphone/view/Control/Display/Chat/ImageFileView.qml b/Linphone/view/Control/Display/Chat/ImageFileView.qml new file mode 100644 index 000000000..c7d6b93cb --- /dev/null +++ b/Linphone/view/Control/Display/Chat/ImageFileView.qml @@ -0,0 +1,56 @@ +import QtQuick +import QtQuick.Controls as Control +import QtQuick.Layouts +import QtMultimedia + +import Linphone +import UtilsCpp +import 'qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js' as Utils + +// ============================================================================= + + +// --------------------------------------------------------------------- +// Separated file to show a single image bigger in chat message +// The FileView file does not allow that as it is a Loader and the image +// is reloaded everytime the message becomes visible again. It causes the +// chat message not to be able to adapt its size according to the painted +// size of the image +// --------------------------------------------------------------------- +Image { + id: mainItem + property ChatMessageContentGui contentGui + + mipmap: false//SettingsModel.mipmapEnabled + autoTransform: true + fillMode: Image.PreserveAspectFit + source: contentGui && contentGui.core.thumbnail || "" + + states: State { + name: 'hovered' + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + propagateComposedEvents: true + cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor + // Changing cursor in MouseArea seems not to work with the Loader + // Use override cursor for this case + onContainsMouseChanged: { + if (containsMouse) UtilsCpp.setGlobalCursor(Qt.PointingHandCursor) + else UtilsCpp.restoreGlobalCursor() + mainItem.state = containsMouse ? 'hovered' : '' + } + onPressed: (mouse) => { + mouse.accepted = true + // if(SettingsModel.isVfsEncrypted){ + // window.attachVirtualWindow(Utils.buildCommonDialogUri('FileViewDialog'), { + // contentGui: mainItem.contentGui, + // }, function (status) { + // }) + // }else + mainItem.contentGui.core.lOpenFile() + } + } +} \ No newline at end of file