diff --git a/CHANGELOG.md b/CHANGELOG.md
index 409be8e68..f1f472690 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Primary color for links in chat.
- Replace double click on avatar by a simple click for copying address into the SmartSearchBar.
+- Bubble chat layout.
### Added
- VFS Encryption
@@ -26,6 +27,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Emojis picker.
- Text edit in chat can now understand rich texts.
- Create thumbnails into memory instead of disk.
+- Display video thumbnails.
+- Crop thumbnail and pictures if distored.
+
### Removed
- Picture zoom on mouse over.
diff --git a/linphone-app/CMakeLists.txt b/linphone-app/CMakeLists.txt
index 4c6a71f01..63b6607db 100644
--- a/linphone-app/CMakeLists.txt
+++ b/linphone-app/CMakeLists.txt
@@ -132,7 +132,7 @@ if( WIN32)
endif()
set(CMAKE_INCLUDE_CURRENT_DIR ON)#useful for config.h
-set(QT5_PACKAGES Core Gui Quick Widgets QuickControls2 Svg LinguistTools Concurrent Network Test Qml)
+set(QT5_PACKAGES Core Gui Quick Widgets QuickControls2 Svg LinguistTools Concurrent Network Test Qml Multimedia)
if(ENABLE_APP_OAUTH2)
list(APPEND QT5_PACKAGES NetworkAuth)
diff --git a/linphone-app/assets/images/thumbnail_video_custom.svg b/linphone-app/assets/images/thumbnail_video_custom.svg
new file mode 100644
index 000000000..0bb6847e9
--- /dev/null
+++ b/linphone-app/assets/images/thumbnail_video_custom.svg
@@ -0,0 +1,46 @@
+
+
diff --git a/linphone-app/resources.qrc b/linphone-app/resources.qrc
index 2af508a45..a8b75859d 100644
--- a/linphone-app/resources.qrc
+++ b/linphone-app/resources.qrc
@@ -164,6 +164,7 @@
assets/images/stop_fullscreen_custom.svg
assets/images/timer_custom.svg
assets/images/tel_keypad_voicemail_custom.svg
+ assets/images/thumbnail_video_custom.svg
assets/images/tooltip_arrow_bottom_custom.svg
assets/images/tooltip_arrow_left_custom.svg
assets/images/tooltip_arrow_right_custom.svg
@@ -412,6 +413,7 @@
ui/modules/Linphone/Styles/Dialog/OnlineInstallerDialogStyle.qml
ui/modules/Linphone/Styles/Dialog/SipAddressDialogStyle.qml
ui/modules/Linphone/Styles/Dialog/ZrtpTokenAuthenticationDialogStyle.qml
+ ui/modules/Linphone/Styles/File/FileViewStyle.qml
ui/modules/Linphone/Styles/History/HistoryStyle.qml
ui/modules/Linphone/Styles/Menus/SipAddressesMenuStyle.qml
ui/modules/Linphone/Styles/Menus/IncallMenuStyle.qml
diff --git a/linphone-app/src/app/providers/ExternalImageProvider.cpp b/linphone-app/src/app/providers/ExternalImageProvider.cpp
index 9aeb06f18..7e0bab3f9 100644
--- a/linphone-app/src/app/providers/ExternalImageProvider.cpp
+++ b/linphone-app/src/app/providers/ExternalImageProvider.cpp
@@ -35,8 +35,17 @@ ExternalImageProvider::ExternalImageProvider () : QQuickImageProvider(
) {
}
-QImage ExternalImageProvider::requestImage (const QString &id, QSize *size, const QSize &) {
- QImage image(Utils::getImage(QUrl::fromPercentEncoding(id.toUtf8())));
- *size = image.size();
- return image;
+QImage ExternalImageProvider::requestImage (const QString &id, QSize *size, const QSize &requestedSize) {
+ QImage image(Utils::getImage(QUrl::fromPercentEncoding(id.toUtf8())));
+ double requestedFactor = 1.0;
+ double factor = image.width() / (double)image.height();
+ if(requestedSize.isValid())
+ requestedFactor = requestedSize.width() / (double)requestedSize.height();
+ if(factor <0.2){// too height
+ image = image.copy(0,0, image.width(), image.width() / requestedFactor);
+ }else if( factor > 5){// too large
+ image = image.copy(0,0, image.height() * requestedFactor, image.height());
+ }
+ *size = image.size();
+ return image;
}
diff --git a/linphone-app/src/components/other/images/ImageModel.cpp b/linphone-app/src/components/other/images/ImageModel.cpp
index a9601eec4..aa4340814 100644
--- a/linphone-app/src/components/other/images/ImageModel.cpp
+++ b/linphone-app/src/components/other/images/ImageModel.cpp
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
#include "app/App.hpp"
#include "utils/Utils.hpp"
@@ -30,6 +31,75 @@
#include "components/core/CoreManager.hpp"
#include "utils/QExifImageHeader.hpp"
+#include
+
+VideoFrameGrabber::VideoFrameGrabber( QObject *parent)
+ : QAbstractVideoSurface(parent){
+}
+
+QList VideoFrameGrabber::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const {
+ Q_UNUSED(handleType);
+ return QList()
+ << QVideoFrame::Format_ARGB32
+ << QVideoFrame::Format_ARGB32_Premultiplied
+ << QVideoFrame::Format_RGB32
+ << QVideoFrame::Format_RGB24
+ << QVideoFrame::Format_RGB565
+ << QVideoFrame::Format_RGB555
+ << QVideoFrame::Format_ARGB8565_Premultiplied
+ << QVideoFrame::Format_BGRA32
+ << QVideoFrame::Format_BGRA32_Premultiplied
+ << QVideoFrame::Format_BGR32
+ << QVideoFrame::Format_BGR24
+ << QVideoFrame::Format_BGR565
+ << QVideoFrame::Format_BGR555
+ << QVideoFrame::Format_BGRA5658_Premultiplied
+ << QVideoFrame::Format_AYUV444
+ << QVideoFrame::Format_AYUV444_Premultiplied
+ << QVideoFrame::Format_YUV444
+ << QVideoFrame::Format_YUV420P
+ << QVideoFrame::Format_YV12
+ << QVideoFrame::Format_UYVY
+ << QVideoFrame::Format_YUYV
+ << QVideoFrame::Format_NV12
+ << QVideoFrame::Format_NV21
+ << QVideoFrame::Format_IMC1
+ << QVideoFrame::Format_IMC2
+ << QVideoFrame::Format_IMC3
+ << QVideoFrame::Format_IMC4
+ << QVideoFrame::Format_Y8
+ << QVideoFrame::Format_Y16
+ << QVideoFrame::Format_Jpeg
+ << QVideoFrame::Format_CameraRaw
+ << QVideoFrame::Format_AdobeDng;
+}
+
+bool VideoFrameGrabber::isFormatSupported(const QVideoSurfaceFormat &format) const {
+ const QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat());
+ const QSize size = format.frameSize();
+
+ return imageFormat != QImage::Format_Invalid
+ && !size.isEmpty()
+ && format.handleType() == QAbstractVideoBuffer::NoHandle;
+}
+
+bool VideoFrameGrabber::start(const QVideoSurfaceFormat &format){
+ return QAbstractVideoSurface::start(format);
+}
+
+void VideoFrameGrabber::stop() {
+ QAbstractVideoSurface::stop();
+}
+
+bool VideoFrameGrabber::present(const QVideoFrame &frame){
+ if (frame.isValid()) {
+ emit frameAvailable(frame.image());
+ return true;
+ }else
+ return false;
+}
+
+
// =============================================================================
ImageModel::ImageModel (const QString& id, const QString& path, const QString& description, QObject * parent) : QObject(parent) {
@@ -88,6 +158,25 @@ QImage ImageModel::createThumbnail(const QString& path){
QByteArray format = reader.format();
if(!format.isEmpty())
originalImage = QImage(path, format);
+ else if(Utils::isVideo(path)){
+ QMediaPlayer player;
+ player.setMedia(QUrl::fromLocalFile(path));
+ player.setPosition(player.duration() / 2);
+ VideoFrameGrabber grabber;
+ player.setVideoOutput(&grabber);
+ QObject * context = new QObject();
+ QObject::connect(&grabber, &VideoFrameGrabber::frameAvailable, context, [&context,&originalImage, &player](QImage frame) mutable{
+ originalImage = frame.copy();
+ player.stop();
+ context->deleteLater();// This will destroy context and initializer
+ context = nullptr;
+ }, Qt::DirectConnection);
+ player.play();
+ do{
+ qApp->processEvents();
+ }while(player.state() != QMediaPlayer::State::StoppedState);
+ if(context) context->deleteLater();
+ }
}
if (!originalImage.isNull()){
int rotation = 0;
@@ -101,26 +190,27 @@ QImage ImageModel::createThumbnail(const QString& path){
painter.drawImage(0, 0, originalImage);
//--------------------
double factor = image.width() / (double)image.height();
- if(factor < 0.2 || factor > 5){
- qInfo() << QStringLiteral("Cannot create thumbnails because size factor (%1) is too low/much of: `%2`.").arg(factor).arg(path);
- }else {
- thumbnail = image.scaled(
+ Qt::AspectRatioMode aspectRatio = Qt::KeepAspectRatio;
+ if(factor < 0.2 || factor > 5)
+ aspectRatio = Qt::KeepAspectRatioByExpanding;
+ thumbnail = image.scaled(
Constants::ThumbnailImageFileWidth, Constants::ThumbnailImageFileHeight,
- Qt::KeepAspectRatio, Qt::SmoothTransformation
+ aspectRatio , Qt::SmoothTransformation
);
+ if(aspectRatio == Qt::KeepAspectRatioByExpanding)
+ thumbnail = thumbnail.copy(0,0,Constants::ThumbnailImageFileWidth, Constants::ThumbnailImageFileHeight);
- if (rotation != 0) {
- QTransform transform;
- if (rotation == 3 || rotation == 4)
- transform.rotate(180);
- else if (rotation == 5 || rotation == 6)
- transform.rotate(90);
- else if (rotation == 7 || rotation == 8)
- transform.rotate(-90);
- thumbnail = thumbnail.transformed(transform);
- if (rotation == 2 || rotation == 4 || rotation == 5 || rotation == 7)
- thumbnail = thumbnail.mirrored(true, false);
- }
+ if (rotation != 0) {
+ QTransform transform;
+ if (rotation == 3 || rotation == 4)
+ transform.rotate(180);
+ else if (rotation == 5 || rotation == 6)
+ transform.rotate(90);
+ else if (rotation == 7 || rotation == 8)
+ transform.rotate(-90);
+ thumbnail = thumbnail.transformed(transform);
+ if (rotation == 2 || rotation == 4 || rotation == 5 || rotation == 7)
+ thumbnail = thumbnail.mirrored(true, false);
}
}
}
diff --git a/linphone-app/src/components/other/images/ImageModel.hpp b/linphone-app/src/components/other/images/ImageModel.hpp
index a16c6e00a..114f74cd3 100644
--- a/linphone-app/src/components/other/images/ImageModel.hpp
+++ b/linphone-app/src/components/other/images/ImageModel.hpp
@@ -28,6 +28,25 @@
#include
#include "utils/LinphoneEnums.hpp"
+#include
+
+
+class VideoFrameGrabber : public QAbstractVideoSurface {
+ Q_OBJECT
+ public:
+ VideoFrameGrabber(QObject *parent = 0);
+
+ QList supportedPixelFormats(
+ QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const override;
+ bool isFormatSupported(const QVideoSurfaceFormat &format) const override;
+
+ bool start(const QVideoSurfaceFormat &format) override;
+ void stop() override;
+ bool present(const QVideoFrame &frame) override;
+
+ signals:
+ void frameAvailable(QImage frame);
+};
class ImageModel : public QObject {
Q_OBJECT
diff --git a/linphone-app/src/components/participant/ParticipantDeviceListModel.cpp b/linphone-app/src/components/participant/ParticipantDeviceListModel.cpp
index ea2e9b6c8..82e1af73b 100644
--- a/linphone-app/src/components/participant/ParticipantDeviceListModel.cpp
+++ b/linphone-app/src/components/participant/ParticipantDeviceListModel.cpp
@@ -50,6 +50,9 @@ ParticipantDeviceListModel::ParticipantDeviceListModel (CallModel * callModel, Q
}
}
+ParticipantDeviceListModel::~ParticipantDeviceListModel(){
+}
+
void ParticipantDeviceListModel::initConferenceModel(){
if(!mInitialized && mCallModel){
auto conferenceModel = mCallModel->getConferenceSharedModel();
diff --git a/linphone-app/src/components/participant/ParticipantDeviceListModel.hpp b/linphone-app/src/components/participant/ParticipantDeviceListModel.hpp
index ed5edc3c1..9f0790122 100644
--- a/linphone-app/src/components/participant/ParticipantDeviceListModel.hpp
+++ b/linphone-app/src/components/participant/ParticipantDeviceListModel.hpp
@@ -38,7 +38,7 @@ class ParticipantDeviceListModel : public ProxyListModel {
public:
ParticipantDeviceListModel (std::shared_ptr participant, QObject *parent = nullptr);
ParticipantDeviceListModel (CallModel * callModel, QObject *parent = nullptr);
-
+ ~ParticipantDeviceListModel();
void initConferenceModel();
void updateDevices(std::shared_ptr participant);
void updateDevices(const std::list>& devices, const bool& isMe);
diff --git a/linphone-app/src/utils/Utils.cpp b/linphone-app/src/utils/Utils.cpp
index 8cb7b77c5..3c2a9629b 100644
--- a/linphone-app/src/utils/Utils.cpp
+++ b/linphone-app/src/utils/Utils.cpp
@@ -650,6 +650,10 @@ bool Utils::isSupportedForDisplay(const QString& path){
return !QMimeDatabase().mimeTypeForFile(path).name().contains("application");// "for pdf : "application/pdf". Note : Make an exception when supported.
}
+bool Utils::canHaveThumbnail(const QString& path){
+ return isImage(path) || isAnimatedImage(path) || isPdf(path) || isVideo(path);
+}
+
bool Utils::isPhoneNumber(const QString& txt){
auto core = CoreManager::getInstance()->getCore();
if(!core)
diff --git a/linphone-app/src/utils/Utils.hpp b/linphone-app/src/utils/Utils.hpp
index 1e6b846bd..43710e8f4 100644
--- a/linphone-app/src/utils/Utils.hpp
+++ b/linphone-app/src/utils/Utils.hpp
@@ -71,6 +71,7 @@ public:
Q_INVOKABLE static bool isVideo(const QString& path);
Q_INVOKABLE static bool isPdf(const QString& path);
Q_INVOKABLE static bool isSupportedForDisplay(const QString& path);
+ Q_INVOKABLE static bool canHaveThumbnail(const QString& path);
Q_INVOKABLE static bool isPhoneNumber(const QString& txt);
Q_INVOKABLE static bool isUsername(const QString& txt); // Check with Regex
Q_INVOKABLE QSize getImageSize(const QString& url);
diff --git a/linphone-app/ui/modules/Linphone/Chat/ChatConferenceInvitationMessage.qml b/linphone-app/ui/modules/Linphone/Chat/ChatConferenceInvitationMessage.qml
index bf7c883ee..47229ba7e 100644
--- a/linphone-app/ui/modules/Linphone/Chat/ChatConferenceInvitationMessage.qml
+++ b/linphone-app/ui/modules/Linphone/Chat/ChatConferenceInvitationMessage.qml
@@ -47,10 +47,6 @@ Loader{
property int fitWidth: layout.fitWidth + ChatCalendarMessageStyle.leftMargin+ChatCalendarMessageStyle.rightMargin
anchors.fill: parent
- anchors.leftMargin: ChatCalendarMessageStyle.widthMargin
- anchors.rightMargin: ChatCalendarMessageStyle.widthMargin
- anchors.topMargin: ChatCalendarMessageStyle.topMargin
- anchors.bottomMargin: ChatCalendarMessageStyle.bottomMargin
clip: false
diff --git a/linphone-app/ui/modules/Linphone/Chat/ChatContent.qml b/linphone-app/ui/modules/Linphone/Chat/ChatContent.qml
index 7c0b12072..1ca050026 100644
--- a/linphone-app/ui/modules/Linphone/Chat/ChatContent.qml
+++ b/linphone-app/ui/modules/Linphone/Chat/ChatContent.qml
@@ -23,10 +23,10 @@ Loader{// Use of Loader because of Repeater (items cannot be loaded dynamically)
id: mainItem
property ChatMessageModel chatMessageModel: null
property int availableWidth //const
- property int fileWidth: ChatStyle.entry.message.file.height * 4 / 3 + 2*ChatStyle.entry.message.file.margins
+ property int fileWidth: FileViewStyle.height * 4 / 3 + 2*ChatStyle.entry.message.file.margins
// Readonly
- property int bestWidth: Math.min(availableWidth, Math.max(filesBestWidth, conferencesBestWidth, textsBestWidth, voicesBestWidth))
+ property int bestWidth: Math.min(availableWidth, Math.max(filesCount*filesBestWidth, conferencesBestWidth, textsBestWidth, voicesBestWidth))
property int filesBestWidth: 0
property int filesCount: 0
property int conferencesCount: 0
@@ -49,11 +49,12 @@ Loader{// Use of Loader because of Repeater (items cannot be loaded dynamically)
property int fileBackgroundRadius: ChatStyle.entry.message.file.extension.radius
active: chatMessageModel
-
+
sourceComponent: Component{
Column{
id: mainComponent
spacing: 0
+ padding: 10
function updateFilesBestWidth(){
var newBestWidth = 0
var count = 0
@@ -63,13 +64,10 @@ Loader{// Use of Loader because of Repeater (items cannot be loaded dynamically)
var a = item.fitWidth
if(a) {
++count
- newBestWidth = Math.max(newBestWidth,a)
+ newBestWidth = Math.max(newBestWidth,a+2*ChatStyle.entry.message.file.margins)
}
}
}
- if(count > 1){
- newBestWidth = Math.max(newBestWidth, mainItem.fileWidth*count)
- }
mainItem.filesCount = count
mainItem.filesBestWidth = newBestWidth
}
@@ -87,7 +85,7 @@ Loader{// Use of Loader because of Repeater (items cannot be loaded dynamically)
}
ListView {
id: messagesVoicesList
- width: parent.width
+ width: parent.width-2*mainComponent.padding
visible: count > 0
spacing: 0
clip: false
@@ -115,7 +113,7 @@ Loader{// Use of Loader because of Repeater (items cannot be loaded dynamically)
// CONFERENCE
ListView {
id: messagesConferencesList
- width: parent.width
+ width: parent.width-2*mainComponent.padding
visible: count > 0
spacing: 0
clip: false
@@ -151,14 +149,14 @@ Loader{// Use of Loader because of Repeater (items cannot be loaded dynamically)
id: messageFilesList
property alias count: repeater.count
visible: count > 0
- clip: false
+ clip: false
+ width: parent.width-2*mainComponent.padding
- property int availableSection: mainItem.availableWidth / mainItem.fileWidth
- property int bestFitSection: mainItem.bestWidth / mainItem.fileWidth
+ property int availableSection: mainItem.availableWidth / mainItem.filesBestWidth
+ property int bestFitSection: mainItem.bestWidth / mainItem.filesBestWidth
columns: Math.max(1, Math.min(availableSection , bestFitSection))
columnSpacing: 0
- rowSpacing: 0
- width: parent.width
+ rowSpacing: ChatStyle.entry.message.file.spacing
Repeater{
id: repeater
model: ContentProxyModel{
@@ -166,6 +164,12 @@ Loader{// Use of Loader because of Repeater (items cannot be loaded dynamically)
chatMessageModel: mainItem.chatMessageModel
}
ChatFileMessage{
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ Layout.preferredHeight: fitHeight
+ Layout.preferredWidth: fitWidth
+ Layout.maximumWidth: fitWidth
+ Layout.maximumHeight: fitHeight
contentModel: $modelData
onIsHoveringChanged: mainItem.isFileHoveringChanged(isHovering)
borderWidth: mainItem.fileBorderWidth
@@ -178,7 +182,7 @@ Loader{// Use of Loader because of Repeater (items cannot be loaded dynamically)
// TEXTS
ListView {
id: messagesTextsList
- width: parent.width
+ width: parent.width-2*mainComponent.padding
visible: count > 0
spacing: 0
clip: false
@@ -192,11 +196,14 @@ Loader{// Use of Loader because of Repeater (items cannot be loaded dynamically)
function updateBestWidth(){
var newWidth = mainComponent.updateListBestWidth(messagesTextsList)
mainItem.textsCount = newWidth[0]
- mainItem.textsBestWidth = newWidth[1]
+ // Padding is takken account because it is used for the whole bubble.
+ // We add 1 pixel to avoid implicit new line computation (Guess : float computation from Qt)
+ mainItem.textsBestWidth = newWidth[1] + 2*mainComponent.padding + 1
}
Component.onCompleted: messagesTextsList.updateBestWidth()
delegate:
ChatTextMessage {
+ width: parent.width
contentModel: $modelData
onLastTextSelectedChanged: mainItem.lastTextSelectedChanged(lastTextSelected)
color: mainItem.useTextColor
diff --git a/linphone-app/ui/modules/Linphone/Chat/ChatFileMessage.qml b/linphone-app/ui/modules/Linphone/Chat/ChatFileMessage.qml
index 5bacbcede..45dcefaf4 100644
--- a/linphone-app/ui/modules/Linphone/Chat/ChatFileMessage.qml
+++ b/linphone-app/ui/modules/Linphone/Chat/ChatFileMessage.qml
@@ -13,270 +13,35 @@ import UtilsCpp 1.0
// =============================================================================
// TODO : into Loader
-Row {
+Item {
id:mainRow
property ChatMessageModel chatMessageModel: contentModel && contentModel.chatMessageModel
property ContentModel contentModel
- property bool isOutgoing : chatMessageModel && ( chatMessageModel.isOutgoing || chatMessageModel.state == LinphoneEnums.ChatMessageStateIdle);
- property int fitHeight: mainRow.isAnimatedImage ? ChatStyle.entry.message.file.heightbetter : ChatStyle.entry.message.file.height
- property int fitWidth: fitHeight * 4 / 3 + 2*ChatStyle.entry.message.file.margins
- property int borderWidth : 0
- property color backgroundColor: ChatStyle.entry.message.file.extension.background.colorModel.color
- property int backgroundRadius: ChatStyle.entry.message.file.extension.radius
- /*
- property int fitWidth: visible
- ? Math.max( Math.max((thumbnailProvider.sourceComponent == extension
- ? thumbnailProvider.item.fitWidth
- : 0)
- , thumbnailProvider.width + 3*ChatStyle.entry.message.file.margins)
- , Math.max(ChatStyle.entry.message.file.width, ChatStyle.entry.message.outgoing.areaSize))
- : 0
- property int fitHeight: visible ? rectangle.height : 0
- */
- property bool isAnimatedImage : mainRow.contentModel && mainRow.contentModel.wasDownloaded && UtilsCpp.isAnimatedImage(mainRow.contentModel.filePath)
- property bool haveThumbnail: mainRow.contentModel && mainRow.contentModel.thumbnail
- property bool isHovering: thumbnailProvider.state == 'hovered'
+ property int fitHeight: fileView.fitHeight
+ property int fitWidth: fileView.fitWidth
+ property alias borderWidth: fileView.borderWidth
+ property alias backgroundColor: fileView.backgroundColor
+ property alias backgroundRadius: fileView.backgroundRadius
+ property alias isHovering: fileView.isHovering
- signal copyAllDone()
- signal copySelectionDone()
- signal forwardClicked()
- height: fitHeight
- width: fitWidth
- visible: true
// ---------------------------------------------------------------------------
// File message.
// ---------------------------------------------------------------------------
-
- Item{
- width: mainRow.width
- height:rectangle.height
+ Rectangle {
+ id: rectangle
+ color: 'transparent'
+ anchors.fill: parent
+ radius: ChatStyle.entry.message.radius
- Rectangle {
- id: rectangle
-
- readonly property bool isError: chatMessageModel && Utils.includes([
- LinphoneEnums.ChatMessageStateFileTransferError,
- LinphoneEnums.ChatMessageStateNotDelivered,
- ], chatMessageModel.state)
- readonly property bool isUploaded: chatMessageModel && chatMessageModel.state == LinphoneEnums.ChatMessageStateDelivered
- readonly property bool isDelivered: chatMessageModel && chatMessageModel.state == LinphoneEnums.ChatMessageStateDeliveredToUser
- readonly property bool isRead: chatMessageModel && chatMessageModel.state == LinphoneEnums.ChatMessageStateDisplayed
- readonly property bool isTransferring: chatMessageModel && (chatMessageModel.state == LinphoneEnums.ChatMessageStateFileTransferInProgress || chatMessageModel.state == LinphoneEnums.ChatMessageStateInProgress )
-
- property string thumbnail : mainRow.contentModel ? mainRow.contentModel.thumbnail : ''
- color: 'transparent'
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: parent.top
- height: 2*ChatStyle.entry.message.file.margins + (mainRow.isAnimatedImage
- ? ChatStyle.entry.message.file.heightbetter
- : thumbnailProvider.sourceComponent == extension
- ? ChatStyle.entry.message.file.height
- : ChatStyle.entry.message.file.height
- )
- radius: ChatStyle.entry.message.radius
-
- // ---------------------------------------------------------------------
- // Thumbnail or extension.
- // ---------------------------------------------------------------------
-
- Component {
- id: thumbnailImage
-
- Image {
- id: thumbnailImageSource
- property real scaleAnimatorTo : ChatStyle.entry.message.file.animation.thumbnailTo
- anchors.centerIn: parent
- mipmap: SettingsModel.mipmapEnabled
- source: mainRow.contentModel.thumbnail
- autoTransform: true
- fillMode: Image.PreserveAspectFit
- height: ChatStyle.entry.message.file.height
- width: height*4/3
- Component.onCompleted: mainRow.fitHeight = height
-
- Loader{
- anchors.fill: parent
- sourceComponent: Image{// Better quality on zoom
- mipmap: SettingsModel.mipmapEnabled
- source:'image://external/'+mainRow.contentModel.filePath
- autoTransform: true
- fillMode: Image.PreserveAspectFit
- visible: status == Image.Ready
- }
- asynchronous: true
- active: thumbnailProvider.state == 'hovered'
- }
- }
- }
- Component {
- id: animatedImage
-
- AnimatedImage {
- id: animatedImageSource
- property real scaleAnimatorTo : ChatStyle.entry.message.file.animation.to
- mipmap: SettingsModel.mipmapEnabled
- source: 'file:/'+mainRow.contentModel.filePath
- autoTransform: true
- fillMode: Image.PreserveAspectFit
- height: ChatStyle.entry.message.file.heightbetter
- width: height*4/3
-
- Component.onCompleted: mainRow.fitHeight = height
- }
- }
-
- Component {
- id: extension
-
- Rectangle {
- property int fitWidth: Math.max(downloadText.implicitWidth, Math.max(fileName.visible ? fileName.implicitWidth : 0, fileIcon.iconSize)) + 20
- //property int fitHeight: fileIcon.iconSize + (fileName.visible ? fileName.implicitHeight + ChatStyle.entry.message.file.spacing : 0 )
-// + (downloadText.visible? downloadText.implicitHeight + ChatStyle.entry.message.file.spacing : 0) + 2*ChatStyle.entry.message.file.margins
- property real scaleAnimatorTo : ChatStyle.entry.message.file.animation.to
-
- anchors.centerIn: parent
- height: ChatStyle.entry.message.file.height
- width: height*4/3
- color: mainRow.backgroundColor
- radius: mainRow.backgroundRadius
- border.width: mainRow.borderWidth
- border.color: ChatStyle.entry.message.file.extension.background.borderColorModel.color
-
- ColumnLayout{
- anchors.fill: parent
- anchors.topMargin: ChatStyle.entry.message.file.margins
- anchors.bottomMargin: ChatStyle.entry.message.file.margins
- spacing: ChatStyle.entry.message.file.spacing
- Icon{
- id: fileIcon
- Layout.alignment: Qt.AlignCenter
- icon: extensionText.text != '' ? ChatStyle.entry.message.file.extension.icon : ChatStyle.entry.message.file.extension.unknownIcon
- iconSize: ChatStyle.entry.message.file.extension.iconSize
- Layout.fillHeight: true
- Layout.fillWidth: true
- Layout.preferredHeight: iconSize
- Layout.preferredWidth: iconSize
- Text {
- id: extensionText
- anchors.bottom: parent.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.bottomMargin: ChatStyle.entry.message.file.spacing
- width: parent.width - 2*ChatStyle.entry.message.file.spacing
- color: ChatStyle.entry.message.file.extension.text.colorModel.color
- font.bold: true
- font.pointSize: ChatStyle.entry.message.file.extension.text.pointSize
- clip: true
- text: (mainRow.contentModel?Utils.getExtension(mainRow.contentModel.name).toUpperCase():'')
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- }
- RoundProgressBar {
- id: progressBar
- anchors.centerIn: parent
- property int fileSize: mainRow.contentModel ? mainRow.contentModel.fileSize : 0
- to: 100
- value: mainRow.contentModel ? (fileSize>0 ? Math.floor(100 * mainRow.contentModel.fileOffset / fileSize) : 0) : to
- visible: rectangle.isTransferring && value != 0
- /* Change format? Current is %
- text: if(mainRow.contentModel){
- var fileSize = Utils.formatSize(mainRow.contentModel.fileSize)
- return progressBar.visible
- ? Utils.formatSize(mainRow.contentModel.fileOffset) + '/' + fileSize
- : fileSize
- }else
- return ''
- */
- }
- }
- Text {
- id: fileName
- Layout.fillWidth: true
- Layout.fillHeight: true
- visible: mainRow.contentModel && !mainRow.isAnimatedImage && !mainRow.haveThumbnail
-
- color: ChatStyle.entry.message.file.extension.text.colorModel.color
- font.pointSize: ChatStyle.entry.message.file.name.pointSize
- wrapMode: Text.WrapAnywhere
- horizontalAlignment: Text.AlignHCenter
- maximumLineCount: 2
-
- text: (mainRow.contentModel ? mainRow.contentModel.name : '')
- }
- Text{
- id: downloadText
- Layout.fillWidth: true
- Layout.fillHeight: true
- Layout.preferredHeight: visible ? contentHeight : 0
- //: 'Cancel' : Message link to cancel a transfer (upload/download)
- text: mainRow.contentModel ? rectangle.isTransferring ? qsTr('fileTransferCancel')
- //: 'Download' : Message link to download a file
- : qsTr('fileTransferDownload') +' ('+Utils.formatSize(mainRow.contentModel.fileSize)+')'
- : ''
- font.underline: true
- font.pointSize: ChatStyle.entry.message.file.download.pointSize
- color:ChatStyle.entry.message.file.extension.text.colorModel.color
- visible: (mainRow.contentModel? (!mainItem.isOutgoing && !mainRow.contentModel.wasDownloaded) || rectangle.isTransferring : false)
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- }
- }
-
- }
- }
- Loader {
- id: thumbnailProvider
- anchors.centerIn: parent
- sourceComponent: (mainRow.contentModel ?
- (mainRow.isAnimatedImage ? animatedImage
- : (mainRow.haveThumbnail ? thumbnailImage : extension )
- ) : undefined)
-
- states: State {
- name: 'hovered'
- }
-
- }
- }
-
-
- MouseArea {
- function handleMouseMove (mouse) {
- thumbnailProvider.state = Utils.pointIsInItem(this, thumbnailProvider, mouse)
- ? 'hovered'
- : ''
- }
-
+ FileView{
+ id: fileView
anchors.fill: parent
- visible: true
-
- onClicked: {
- if(rectangle.isTransferring)
- mainRow.contentModel.cancelDownloadFile()
- else if( !mainRow.contentModel.wasDownloaded) {
- thumbnailProvider.state = ''
- mainRow.contentModel.downloadFile()
- }else if (Utils.pointIsInItem(this, thumbnailProvider, mouse)) {
- if(SettingsModel.isVfsEncrypted){
- window.attachVirtualWindow(Utils.buildCommonDialogUri('FileViewDialog'), {
- contentModel: mainRow.contentModel,
- }, function (status) {
- })
- }else
- mainRow.contentModel.openFile()
- } else if (mainRow.contentModel ) {
- thumbnailProvider.state = ''
- mainRow.contentModel.openFile(true)// Show directory
- } else {
- thumbnailProvider.state = ''
- mainRow.contentModel.downloadFile()
-
- }
- }
- onExited: thumbnailProvider.state = ''
- onMouseXChanged: handleMouseMove.call(this, mouse)
- onMouseYChanged: handleMouseMove.call(this, mouse)
+ contentModel: mainRow.contentModel
+ thumbnail: mainRow.contentModel.thumbnail
+ name: mainRow.contentModel && mainRow.contentModel.name
+ filePath: mainRow.contentModel && mainRow.contentModel.filePath
+ isTransferring: mainRow.chatMessageModel && (mainRow.chatMessageModel.state == LinphoneEnums.ChatMessageStateFileTransferInProgress || mainRow.chatMessageModel.state == LinphoneEnums.ChatMessageStateInProgress )
}
}
-}
+}
\ No newline at end of file
diff --git a/linphone-app/ui/modules/Linphone/Chat/ChatFilePreview.qml b/linphone-app/ui/modules/Linphone/Chat/ChatFilePreview.qml
index b327f15c6..7728980f3 100644
--- a/linphone-app/ui/modules/Linphone/Chat/ChatFilePreview.qml
+++ b/linphone-app/ui/modules/Linphone/Chat/ChatFilePreview.qml
@@ -49,12 +49,10 @@ Item{
width: height * ChatFilePreviewStyle.filePreview.format
anchors.verticalCenter: parent ? parent.verticalCenter : ScrollableListView.verticalCenter
anchors.verticalCenterOffset: 7
+ contentModel: $modelData
thumbnail: $modelData.thumbnail
name: $modelData.name
animationScale: 1.1
- onClickOnFile: {
- $modelData.openFile()
- }
ActionButton{
anchors.bottom: parent.top
anchors.bottomMargin: -height/2
diff --git a/linphone-app/ui/modules/Linphone/Chat/ChatTextMessage.qml b/linphone-app/ui/modules/Linphone/Chat/ChatTextMessage.qml
index 3c179ccc8..008f0b397 100644
--- a/linphone-app/ui/modules/Linphone/Chat/ChatTextMessage.qml
+++ b/linphone-app/ui/modules/Linphone/Chat/ChatTextMessage.qml
@@ -23,8 +23,8 @@ TextEdit {
property ContentModel contentModel
property string lastTextSelected : ''
property font customFont : SettingsModel.textMessageFont
- property int fitHeight: contentHeight + padding + 8
- property int fitWidth: implicitWidth + 2 // add 2 because there is a bug on border that lead to not fit text exactly
+ property int fitHeight: contentHeight
+ property int fitWidth: implicitWidth
signal rightClicked()
@@ -33,9 +33,8 @@ TextEdit {
height: fitHeight
width: parent && parent.width || 1
- visible: contentModel// && contentModel.isText()
+ visible: contentModel
clip: false
- padding: ChatStyle.entry.message.padding
textMargin: 0
readOnly: true
selectByMouse: true
@@ -70,7 +69,6 @@ TextEdit {
}
deselect()
}
-
MouseArea {
id: mouseArea
property bool keepLastSelection: false
diff --git a/linphone-app/ui/modules/Linphone/File/FileView.qml b/linphone-app/ui/modules/Linphone/File/FileView.qml
index 8e45c0bc7..fbc1e3aa7 100644
--- a/linphone-app/ui/modules/Linphone/File/FileView.qml
+++ b/linphone-app/ui/modules/Linphone/File/FileView.qml
@@ -7,6 +7,7 @@ import Linphone 1.0
import LinphoneEnums 1.0
import Linphone.Styles 1.0
import Utils 1.0
+import UtilsCpp 1.0
import Units 1.0
import ColorsList 1.0
@@ -15,118 +16,249 @@ import ColorsList 1.0
Item {
id: mainItem
- property string thumbnail
- property string name
+ property ContentModel contentModel
+ property string thumbnail: contentModel && contentModel.thumbnail
+ property string name: contentModel && contentModel.name
+ property string filePath: contentModel && contentModel.filePath
+ property int fileHeight: FileViewStyle.height
property bool active: true
- property real animationScale : ChatStyle.entry.message.file.animation.to
+ property real animationScale : FileViewStyle.animation.to
property alias imageScale: thumbnailProvider.scale
+ property int fitHeight: mainItem.isAnimatedImage ? FileViewStyle.heightbetter : FileViewStyle.height
+ property int fitWidth: fitHeight*4/3
+ property bool isAnimatedImage : contentModel && contentModel.wasDownloaded && UtilsCpp.isAnimatedImage(filePath)
+ property bool haveThumbnail: contentModel && UtilsCpp.canHaveThumbnail(filePath)
+ property int borderWidth : 0
+ property color backgroundColor: FileViewStyle.extension.background.colorModel.color
+ property int backgroundRadius: FileViewStyle.extension.radius
+
+ property bool isTransferring
+ property bool isHovering: thumbnailProvider.state == 'hovered'
signal clickOnFile()
- // ---------------------------------------------------------------------
- // Thumbnail or extension.
- // ---------------------------------------------------------------------
+ MouseArea {
+ function handleMouseMove (mouse) {
+ thumbnailProvider.state = Utils.pointIsInItem(this, thumbnailProvider, mouse)
+ ? 'hovered'
+ : ''
+ }
+
+ anchors.fill: parent
+ visible: true
+
+ onClicked: {
+ if(mainItem.isTransferring)
+ mainItem.contentModel.cancelDownloadFile()
+ else if( !mainItem.contentModel.wasDownloaded) {
+ thumbnailProvider.state = ''
+ mainItem.contentModel.downloadFile()
+ }else if (Utils.pointIsInItem(this, thumbnailProvider, mouse)) {
+ if(SettingsModel.isVfsEncrypted){
+ window.attachVirtualWindow(Utils.buildCommonDialogUri('FileViewDialog'), {
+ contentModel: mainItem.contentModel,
+ }, function (status) {
+ })
+ }else
+ mainItem.contentModel.openFile()
+ } else if (mainItem.contentModel ) {
+ thumbnailProvider.state = ''
+ mainItem.contentModel.openFile(true)// Show directory
+ } else {
+ thumbnailProvider.state = ''
+ mainItem.contentModel.downloadFile()
+
+ }
+ mainItem.clickOnFile()
+ }
+ onExited: thumbnailProvider.state = ''
+ onMouseXChanged: handleMouseMove.call(this, mouse)
+ onMouseYChanged: handleMouseMove.call(this, mouse)
+ }
+
+
+ // ---------------------------------------------------------------------
+ // Thumbnail
+ // ---------------------------------------------------------------------
Component {
id: thumbnailImage
Image {
id: thumbnailImageSource
+ property real scaleAnimatorTo : FileViewStyle.animation.thumbnailTo
+ property bool isVideo: UtilsCpp.isVideo(mainItem.filePath)
mipmap: SettingsModel.mipmapEnabled
source: mainItem.thumbnail
+ autoTransform: true
fillMode: Image.PreserveAspectFit
+ anchors.fill: parent
+
+ Loader{
+ anchors.fill: parent
+ sourceComponent: Image{// Better quality on zoom
+ mipmap: SettingsModel.mipmapEnabled
+ source: !thumbnailImageSource.isVideo ? 'image://external/'+mainItem.filePath : ''
+ autoTransform: true
+ fillMode: Image.PreserveAspectFit
+ visible: status == Image.Ready
+ }
+ asynchronous: true
+ active: !thumbnailImageSource.isVideo && thumbnailProvider.state == 'hovered'
+ }
+ ActionButton{
+ id: thumbnailVideoButton
+ anchors.centerIn: parent
+ visible: thumbnailImageSource.isVideo
+ isCustom: true
+ backgroundRadius: width
+ colorSet: FileViewStyle.thumbnailVideoIcon
+ onClicked:{
+ window.attachVirtualWindow(Utils.buildCommonDialogUri('FileViewDialog'), {
+ contentModel: mainItem.contentModel,
+ }, function (status) {
+ })
+ }
+ }
}
}
-
+ Component {
+ id: animatedImage
+
+ AnimatedImage {
+ id: animatedImageSource
+ property real scaleAnimatorTo : FileViewStyle.animation.to
+ mipmap: SettingsModel.mipmapEnabled
+ source: 'file:/'+mainItem.filePath
+ autoTransform: true
+ fillMode: Image.PreserveAspectFit
+ anchors.fill: parent
+ }
+ }
+ // ---------------------------------------------------------------------
+ // Extension
+ // ---------------------------------------------------------------------
Component {
id: extension
Rectangle {
- color: ChatStyle.entry.message.file.extension.background.colorModel.color
-
- Text {
+ property real scaleAnimatorTo : FileViewStyle.animation.to
+ anchors.fill: parent
+ color: mainItem.backgroundColor
+ radius: mainItem.backgroundRadius
+ border.width: mainItem.borderWidth
+ border.color: FileViewStyle.extension.background.borderColorModel.color
+
+ ColumnLayout{
anchors.fill: parent
-
- color: ChatStyle.entry.message.file.extension.text.colorModel.color
- font.bold: true
- elide: Text.ElideRight
- text: Utils.getExtension(mainItem.name).toUpperCase()
-
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
+ anchors.topMargin: FileViewStyle.margins
+ anchors.bottomMargin: FileViewStyle.margins
+ spacing: FileViewStyle.spacing
+ Icon{
+ id: fileIcon
+ Layout.alignment: Qt.AlignCenter
+ icon: extensionText.text != '' ? FileViewStyle.extension.icon : FileViewStyle.extension.unknownIcon
+ iconSize: FileViewStyle.extension.iconSize
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ Layout.preferredHeight: iconSize
+ Layout.preferredWidth: iconSize
+ Text {
+ id: extensionText
+ anchors.bottom: parent.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottomMargin: FileViewStyle.spacing
+ width: FileViewStyle.extension.internalSize
+ onWidthChanged: extensionMetrics.font.pointSize = FileViewStyle.extension.text.pointSize // reset metrics
+ color: FileViewStyle.extension.text.colorModel.color
+ font.bold: true
+ font.pointSize: extensionMetrics.font.pointSize
+ clip: true
+ text: (mainItem.contentModel?Utils.getExtension(mainItem.name).toUpperCase():'')
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ TextMetrics{
+ id: extensionMetrics
+ text: extensionText.text
+ font.pointSize: FileViewStyle.extension.text.pointSize
+ onWidthChanged: if(width > extensionText.width) --font.pointSize
+ Component.onCompleted: if(width > extensionText.width) --font.pointSize
+ }
+ }
+ RoundProgressBar {
+ id: progressBar
+ anchors.centerIn: parent
+ property int fileSize: mainItem.contentModel ? mainItem.contentModel.fileSize : 0
+ to: 100
+ value: mainItem.contentModel ? (fileSize>0 ? Math.floor(100 * mainItem.contentModel.fileOffset / fileSize) : 0) : to
+ visible: mainItem.isTransferring && value != 0
+ /* Change format? Current is %
+ text: if(mainRow.contentModel){
+ var fileSize = Utils.formatSize(mainRow.contentModel.fileSize)
+ return progressBar.visible
+ ? Utils.formatSize(mainRow.contentModel.fileOffset) + '/' + fileSize
+ : fileSize
+ }else
+ return ''
+ */
+ }
+ }
+ Text {
+ id: fileName
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ visible: mainItem.contentModel && !mainItem.isAnimatedImage
+
+ color: FileViewStyle.extension.text.colorModel.color
+ font.pointSize: FileViewStyle.name.pointSize
+ wrapMode: Text.WrapAnywhere
+ horizontalAlignment: Text.AlignHCenter
+ maximumLineCount: 2
+
+ text: mainItem.name
+ }
+ Text{
+ id: downloadText
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.preferredHeight: visible ? contentHeight : 0
+ //: 'Cancel' : Message link to cancel a transfer (upload/download)
+ text: mainItem.contentModel ? mainItem.isTransferring ? qsTr('fileTransferCancel')
+ //: 'Download' : Message link to download a file
+ : qsTr('fileTransferDownload') +' ('+Utils.formatSize(mainItem.contentModel.fileSize)+')'
+ : ''
+ font.underline: true
+ font.pointSize: FileViewStyle.download.pointSize
+ color:FileViewStyle.extension.text.colorModel.color
+ visible: (mainItem.contentModel? (!mainItem.isOutgoing && !mainItem.contentModel.wasDownloaded) || mainItem.isTransferring : false)
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
}
+
}
}
+
Loader {
id: thumbnailProvider
-
anchors.fill: parent
-
- sourceComponent: (mainItem.active ? (mainItem.thumbnail ? thumbnailImage : extension ): undefined)
-
- ScaleAnimator {
- id: thumbnailProviderAnimator
-
- target: mainItem
-
- duration: ChatStyle.entry.message.file.animation.duration
- easing.type: Easing.InOutQuad
- from: 1.0
- }
-
+ sourceComponent: (mainItem.contentModel ?
+ (mainItem.isAnimatedImage ? animatedImage
+ : (mainItem.haveThumbnail ? thumbnailImage : extension )
+ ) : undefined)
+
states: State {
name: 'hovered'
}
-
- transitions: [
- Transition {
- from: ''
- to: 'hovered'
-
- ScriptAction {
- script: {
- if (thumbnailProviderAnimator.running) {
- thumbnailProviderAnimator.running = false
- }
-
- mainItem.z = Constants.zPopup
- thumbnailProviderAnimator.to = mainItem.animationScale
- thumbnailProviderAnimator.running = true
- }
- }
- },
- Transition {
- from: 'hovered'
- to: ''
-
- ScriptAction {
- script: {
- if (thumbnailProviderAnimator.running) {
- thumbnailProviderAnimator.running = false
- }
-
- thumbnailProviderAnimator.to = 1.0
- thumbnailProviderAnimator.running = true
- mainItem.z = 0
- }
- }
- }
- ]
}
- MouseArea {
- function handleMouseMove (mouse) {
- thumbnailProvider.state = Utils.pointIsInItem(this, thumbnailProvider, mouse)
- ? 'hovered'
- : ''
- }
+ Loader {
+ id: waitingProvider
anchors.fill: parent
-
- onClicked: {
- clickOnFile()
- thumbnailProvider.state = ''
+ sourceComponent: thumbnailProvider.sourceComponent == thumbnailImage && thumbnailProvider.item.status != Image.Ready
+ ? extension
+ : undefined
+ states: State {
+ name: 'hovered'
}
- onExited: thumbnailProvider.state = ''
- onMouseXChanged: handleMouseMove.call(this, mouse)
- onMouseYChanged: handleMouseMove.call(this, mouse)
}
}
\ No newline at end of file
diff --git a/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml b/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml
index e84240bba..ee59bc91c 100644
--- a/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml
+++ b/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml
@@ -201,7 +201,17 @@ QtObject {
property var outgoingColor: ColorsList.addImageColor(sectionName+'_download_out', icon, 'g')
property var incomingColor: ColorsList.addImageColor(sectionName+'_download_in', icon, 'q')
}
-
+ property QtObject thumbnailVideoIcon: QtObject {
+ property int iconSize: 40
+ property string name : 'play'
+ property string icon : 'thumbnail_video_custom'
+ property var backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'wr_n_b_bg')
+ property var backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'wr_h_b_bg')
+ property var backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'wr_p_b_bg')
+ property var foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'wr_n_b_fg')
+ property var foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'wr_h_b_fg')
+ property var foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'wr_p_b_fg')
+ }
property QtObject animation: QtObject {
property int duration: 300
property real to: 1.7
@@ -212,6 +222,7 @@ QtObject {
property string icon: 'file_extension_custom'
property string unknownIcon: 'file_unknown_custom'
property int iconSize: 60
+ property int internalSize: 37
property int radius: 0
property QtObject background: QtObject {
diff --git a/linphone-app/ui/modules/Linphone/Styles/File/FileViewStyle.qml b/linphone-app/ui/modules/Linphone/Styles/File/FileViewStyle.qml
new file mode 100644
index 000000000..bd82d5c74
--- /dev/null
+++ b/linphone-app/ui/modules/Linphone/Styles/File/FileViewStyle.qml
@@ -0,0 +1,82 @@
+pragma Singleton
+import QtQml 2.2
+
+import Units 1.0
+import ColorsList 1.0
+
+// =============================================================================
+
+QtObject {
+ property string sectionName : 'FileView'
+
+ property int height: 120
+ property int heightbetter: 200
+ property int iconSize: 18
+ property int margins: 8
+ property int spacing: 8
+ property int width: 100
+
+ property QtObject name: QtObject{
+ property int pointSize: Units.dp * 7
+ }
+
+ property QtObject download: QtObject{
+ property string icon: 'download_custom'
+ property int height: 20
+ property int pointSize: Units.dp * 8
+ property int iconSize: 30
+ property var outgoingColor: ColorsList.addImageColor(sectionName+'_download_out', icon, 'g')
+ property var incomingColor: ColorsList.addImageColor(sectionName+'_download_in', icon, 'q')
+ }
+ property QtObject thumbnailVideoIcon: QtObject {
+ property int iconSize: 40
+ property string name : 'play'
+ property string icon : 'thumbnail_video_custom'
+ property var backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'wr_n_b_bg')
+ property var backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'wr_h_b_bg')
+ property var backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'wr_p_b_bg')
+ property var foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'wr_n_b_fg')
+ property var foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'wr_h_b_fg')
+ property var foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'wr_p_b_fg')
+ }
+ property QtObject animation: QtObject {
+ property int duration: 300
+ property real to: 1.7
+ property real thumbnailTo: 2
+ }
+
+ property QtObject extension: QtObject {
+ property string icon: 'file_extension_custom'
+ property string unknownIcon: 'file_unknown_custom'
+ property int iconSize: 60
+ property int internalSize: 37
+ property int radius: 0
+
+ property QtObject background: QtObject {
+ property var colorModel: ColorsList.add(sectionName+'_file_extension_bg', 'q')
+ property var borderColorModel: ColorsList.add(sectionName+'_file_extension_border', 'extension_file_border')
+ }
+
+ property QtObject text: QtObject {
+ property var colorModel: ColorsList.add(sectionName+'_file_extension_text', 'd')
+ property int pointSize: Units.dp * 9
+ }
+ }
+
+ property QtObject status: QtObject {
+ property int spacing: 4
+
+ property QtObject bar: QtObject {
+ property int height: 6
+ property int radius: 3
+
+ property QtObject background: QtObject {
+ property var colorModel: ColorsList.add(sectionName+'_file_statusbar_bg', 'f')
+ }
+
+ property QtObject contentItem: QtObject {
+ property var colorModel: ColorsList.add(sectionName+'_file_statusbar_content', 'p')
+ }
+ }
+ }
+}
diff --git a/linphone-app/ui/modules/Linphone/Styles/qmldir b/linphone-app/ui/modules/Linphone/Styles/qmldir
index 98a4aa786..6e41d0f42 100644
--- a/linphone-app/ui/modules/Linphone/Styles/qmldir
+++ b/linphone-app/ui/modules/Linphone/Styles/qmldir
@@ -37,6 +37,7 @@ singleton OnlineInstallerDialogStyle 1.0 Dialog/OnlineInstallerDialogS
singleton SipAddressDialogStyle 1.0 Dialog/SipAddressDialogStyle.qml
singleton ZrtpTokenAuthenticationDialogStyle 1.0 Dialog/ZrtpTokenAuthenticationDialogStyle.qml
+singleton FileViewStyle 1.0 File/FileViewStyle.qml
singleton HistoryStyle 1.0 History/HistoryStyle.qml