From 879db5f49673bfbfbbb4221901afa393a11f23d3 Mon Sep 17 00:00:00 2001 From: Julien Wadel Date: Tue, 8 Mar 2022 11:10:48 +0100 Subject: [PATCH] - Send multiple files in one message, with preview. - Message preview manage huge heights. - Chat design rework. - Sort timelines by unread chat rooms. - Fix thumbnails that weren't deleted. - Play audio record on playback device instead of ringer device. - Fix binding loops on scrollable areas. - Change timeline filter to a minimal version. - Fix record button hovering. - Fix camera button in fullscreen. --- CHANGELOG.md | 6 +- linphone-app/CMakeLists.txt | 2 + .../assets/images/chat_audio_pause_custom.svg | 27 +- .../assets/images/chat_audio_play_custom.svg | 41 +-- .../chat_audio_preview_pause_custom.svg | 47 ++++ .../images/chat_audio_preview_play_custom.svg | 47 ++++ ...svg => chat_audio_preview_stop_custom.svg} | 0 .../images/chat_audio_soundwave_custom.svg | 32 +-- .../assets/images/menu_copy_text_custom.svg | 45 ++-- linphone-app/assets/images/send_custom.svg | 8 +- linphone-app/assets/languages/en.ts | 15 ++ linphone-app/resources.qrc | 7 +- .../chat-events/ChatMessageModel.cpp | 75 +++--- .../components/chat-room/ChatRoomModel.cpp | 72 ++--- .../components/chat-room/ChatRoomModel.hpp | 6 +- .../chat-room/ChatRoomProxyModel.cpp | 1 - .../chat-room/ChatRoomProxyModel.hpp | 2 - .../src/components/chat/ChatModel.cpp | 52 ++++ .../src/components/chat/ChatModel.hpp | 48 ++++ .../components/content/ContentListModel.cpp | 81 +++++- .../components/content/ContentListModel.hpp | 9 + .../src/components/content/ContentModel.cpp | 27 +- .../src/components/content/ContentModel.hpp | 10 +- .../components/content/ContentProxyModel.cpp | 29 +- .../components/content/ContentProxyModel.hpp | 6 +- .../src/components/core/CoreManager.cpp | 2 + .../src/components/core/CoreManager.hpp | 6 + .../other/colors/ColorListModel.hpp | 10 + .../src/components/settings/SettingsModel.cpp | 11 +- .../src/components/settings/SettingsModel.hpp | 5 + .../components/sound-player/SoundPlayer.cpp | 2 +- .../timeline/TimelineProxyModel.cpp | 6 +- .../modules/Common/Form/DroppableTextArea.qml | 15 +- .../modules/Common/Form/Fields/TextField.qml | 4 +- .../Common/Indicators/MediaProgressBar.qml | 19 +- .../Styles/Form/DroppableTextAreaStyle.qml | 31 ++- .../Indicators/MediaProgressBarStyle.qml | 6 +- .../Common/View/ScrollableListView.qml | 45 +++- linphone-app/ui/modules/Linphone/Chat/Chat.js | 2 +- .../ui/modules/Linphone/Chat/Chat.qml | 252 +++++++++--------- .../Linphone/Chat/ChatAudioMessage.qml | 11 +- .../Linphone/Chat/ChatAudioPreview.qml | 28 +- .../ui/modules/Linphone/Chat/ChatContent.qml | 2 +- .../modules/Linphone/Chat/ChatFileMessage.qml | 2 +- .../modules/Linphone/Chat/ChatFilePreview.qml | 86 ++++++ .../Linphone/Chat/ChatMessagePreview.qml | 36 ++- .../Linphone/Chat/ChatReplyMessage.qml | 18 +- .../Linphone/Chat/ChatReplyPreview.qml | 17 +- .../modules/Linphone/Chat/ChatTextMessage.qml | 2 +- .../ui/modules/Linphone/Chat/Message.qml | 24 +- .../ui/modules/Linphone/File/FileView.qml | 135 ++++++++++ .../Styles/Chat/ChatAudioMessageStyle.qml | 54 ++-- .../Styles/Chat/ChatAudioPreviewStyle.qml | 86 +++--- .../Styles/Chat/ChatFilePreviewStyle.qml | 49 ++++ .../Linphone/Styles/Chat/ChatStyle.qml | 17 +- .../ui/modules/Linphone/Styles/qmldir | 1 + .../ui/modules/Linphone/Timeline/Timeline.qml | 81 +++++- linphone-app/ui/modules/Linphone/qmldir | 2 + linphone-app/ui/views/App/Calls/Incall.qml | 4 +- .../App/Calls/IncallFullscreenWindow.qml | 6 +- .../App/Main/Assistant/AssistantHome.qml | 26 +- .../ui/views/App/Main/Conversation.qml | 7 +- .../ui/views/App/Settings/SettingsUi.qml | 14 + .../App/Styles/Calls/CallFullscreenStyle.qml | 9 + .../ui/views/App/Styles/Calls/CallStyle.qml | 9 + linphone-sdk | 2 +- 66 files changed, 1360 insertions(+), 479 deletions(-) create mode 100644 linphone-app/assets/images/chat_audio_preview_pause_custom.svg create mode 100644 linphone-app/assets/images/chat_audio_preview_play_custom.svg rename linphone-app/assets/images/{chat_audio_stop_custom.svg => chat_audio_preview_stop_custom.svg} (100%) create mode 100644 linphone-app/src/components/chat/ChatModel.cpp create mode 100644 linphone-app/src/components/chat/ChatModel.hpp create mode 100644 linphone-app/ui/modules/Linphone/Chat/ChatFilePreview.qml create mode 100644 linphone-app/ui/modules/Linphone/File/FileView.qml create mode 100644 linphone-app/ui/modules/Linphone/Styles/Chat/ChatFilePreviewStyle.qml diff --git a/CHANGELOG.md b/CHANGELOG.md index a241abd4d..b2e0b0717 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Features: - * Messages features : Reply, forward (to contact, to a SIP address or to a timeline), Vocal record and play, multi contents. + * Messages features : Reply, forward (to contact, to a SIP address or to a timeline), Vocal record and play, multi contents, preview. - Add a feedback on fetching remote provisioning when it failed. - Option to enable message notifications. - CPIM on basic chat rooms. @@ -18,7 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Based on Linphone SDK 5.1 ### Fixed -- Simplify filtering timelines on 3 kind of search : security level, simple/group chats, ephemerals. +- Simplify filtering timelines with 2 modes (minimal or exhaustive) and on 3 kind of search : security level, simple/group chats, ephemerals. +- Sort timelines by taken account of unread events in chat rooms. - Fix systemTrayIcon that could be cloned on each restart. - Fix errors on Action-Buttons on restart. - Enable G729 on public builds. @@ -27,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Adapt UserAgent with device name. - Video freeze on network change. - Set default log size to 50MB +- Crash on the smart search bar. ## 4.3.2 diff --git a/linphone-app/CMakeLists.txt b/linphone-app/CMakeLists.txt index 68867c1eb..fc3118677 100644 --- a/linphone-app/CMakeLists.txt +++ b/linphone-app/CMakeLists.txt @@ -128,6 +128,7 @@ set(SOURCES src/components/calls/CallsListProxyModel.cpp src/components/camera/Camera.cpp src/components/camera/CameraPreview.cpp + src/components/chat/ChatModel.cpp src/components/chat-events/ChatCallModel.cpp src/components/chat-events/ChatEvent.cpp src/components/chat-events/ChatMessageModel.cpp @@ -238,6 +239,7 @@ set(HEADERS src/components/calls/CallsListProxyModel.hpp src/components/camera/Camera.hpp src/components/camera/CameraPreview.hpp + src/components/chat/ChatModel.hpp src/components/chat-events/ChatCallModel.hpp src/components/chat-events/ChatEvent.hpp src/components/chat-events/ChatMessageModel.hpp diff --git a/linphone-app/assets/images/chat_audio_pause_custom.svg b/linphone-app/assets/images/chat_audio_pause_custom.svg index e6b1d6996..8b5393cf8 100644 --- a/linphone-app/assets/images/chat_audio_pause_custom.svg +++ b/linphone-app/assets/images/chat_audio_pause_custom.svg @@ -5,8 +5,8 @@ viewBox="0 0 80 80" version="1.1" id="svg8" - sodipodi:docname="chat_audio_pause.svg" - inkscape:version="1.1 (c68e22c387, 2021-05-23)" + sodipodi:docname="chat_audio_pause_custom.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" @@ -22,26 +22,17 @@ inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" showgrid="false" - inkscape:zoom="5.3231707" - inkscape:cx="28.460481" - inkscape:cy="64.623139" + inkscape:zoom="7.5281002" + inkscape:cx="28.493245" + inkscape:cy="43.304418" inkscape:window-width="1920" inkscape:window-height="1043" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg8" /> - - - - - + diff --git a/linphone-app/assets/images/chat_audio_play_custom.svg b/linphone-app/assets/images/chat_audio_play_custom.svg index 34dfe5c4c..b8376fb5e 100644 --- a/linphone-app/assets/images/chat_audio_play_custom.svg +++ b/linphone-app/assets/images/chat_audio_play_custom.svg @@ -4,44 +4,51 @@ height="80" viewBox="0 0 80 80" version="1.1" - id="svg8" - sodipodi:docname="chat_audio_play.svg" - inkscape:version="1.1 (c68e22c387, 2021-05-23)" + id="svg14" + sodipodi:docname="chat_audio_play_custom.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> + id="defs18" /> + inkscape:current-layer="g8" /> + id="g12"> - + id="g10"> + + + + + diff --git a/linphone-app/assets/images/chat_audio_preview_pause_custom.svg b/linphone-app/assets/images/chat_audio_preview_pause_custom.svg new file mode 100644 index 000000000..e6b1d6996 --- /dev/null +++ b/linphone-app/assets/images/chat_audio_preview_pause_custom.svg @@ -0,0 +1,47 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/chat_audio_preview_play_custom.svg b/linphone-app/assets/images/chat_audio_preview_play_custom.svg new file mode 100644 index 000000000..34dfe5c4c --- /dev/null +++ b/linphone-app/assets/images/chat_audio_preview_play_custom.svg @@ -0,0 +1,47 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/chat_audio_stop_custom.svg b/linphone-app/assets/images/chat_audio_preview_stop_custom.svg similarity index 100% rename from linphone-app/assets/images/chat_audio_stop_custom.svg rename to linphone-app/assets/images/chat_audio_preview_stop_custom.svg diff --git a/linphone-app/assets/images/chat_audio_soundwave_custom.svg b/linphone-app/assets/images/chat_audio_soundwave_custom.svg index 773208494..eefa978f1 100644 --- a/linphone-app/assets/images/chat_audio_soundwave_custom.svg +++ b/linphone-app/assets/images/chat_audio_soundwave_custom.svg @@ -1,12 +1,12 @@ + inkscape:zoom="2.5408164" + inkscape:cx="-1.37751" + inkscape:cy="35.421686" + inkscape:window-width="1458" + inkscape:window-height="749" + inkscape:window-x="146" + inkscape:window-y="99" + inkscape:window-maximized="0" + inkscape:current-layer="svg8" + showguides="false" /> + transform="matrix(0.5,0,0,0.66666667,0,10)"> + id="path2" + d="m 65,0 c 2.761,0 5,2.239 5,5 v 50 c 0,2.761 -2.239,5 -5,5 -2.761,0 -5,-2.239 -5,-5 V 5 c 0,-2.761 2.239,-5 5,-5 z m 20,5 c 2.761,0 5,2.239 5,5 v 40 c 0,2.761 -2.239,5 -5,5 -2.761,0 -5,-2.239 -5,-5 V 10 C 80,7.239 82.239,5 85,5 Z M 45,5 c 2.761,0 5,2.239 5,5 v 40 c 0,2.761 -2.239,5 -5,5 -2.761,0 -5,-2.239 -5,-5 V 10 C 40,7.239 42.239,5 45,5 Z M 25,15 c 2.761,0 5,2.239 5,5 v 20 c 0,2.761 -2.239,5 -5,5 -2.761,0 -5,-2.239 -5,-5 V 20 c 0,-2.761 2.239,-5 5,-5 z m 80,0 c 2.761,0 5,2.239 5,5 v 20 c 0,2.761 -2.239,5 -5,5 -2.761,0 -5,-2.239 -5,-5 V 20 c 0,-2.761 2.239,-5 5,-5 z M 5,25 c 2.761,0 5,2.239 5,5 0,2.761 -2.239,5 -5,5 -2.761,0 -5,-2.239 -5,-5 0,-2.761 2.239,-5 5,-5 z m 115,5 C 40,5.0000002e-8 80,15 120,30 Z" + sodipodi:nodetypes="sssssssssssssssssssssssssssssssssssssssscc" /> diff --git a/linphone-app/assets/images/menu_copy_text_custom.svg b/linphone-app/assets/images/menu_copy_text_custom.svg index ad5a13053..6498ca958 100644 --- a/linphone-app/assets/images/menu_copy_text_custom.svg +++ b/linphone-app/assets/images/menu_copy_text_custom.svg @@ -2,18 +2,19 @@ + id="defs16" /> - + inkscape:current-layer="svg12" /> + + + + + + + + + diff --git a/linphone-app/assets/images/send_custom.svg b/linphone-app/assets/images/send_custom.svg index 79c8416cd..63038ee36 100644 --- a/linphone-app/assets/images/send_custom.svg +++ b/linphone-app/assets/images/send_custom.svg @@ -6,7 +6,7 @@ version="1.1" id="svg10" sodipodi:docname="send_custom.svg" - inkscape:version="1.1 (c68e22c387, 2021-05-23)" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" @@ -26,8 +26,8 @@ inkscape:cx="9.408651" inkscape:cy="46.187923" inkscape:window-width="1920" - inkscape:window-height="1131" - inkscape:window-x="0" + inkscape:window-height="1043" + inkscape:window-x="1920" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg10" /> @@ -35,7 +35,7 @@ fill="none" fill-rule="evenodd" id="g8" - transform="matrix(2.0833639,0,0,2.083344,15.000137,14.999696)"> + transform="matrix(2.5000367,0,0,2.5000128,10.000164,9.9996351)"> 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering). + + minimalTimelineFilterLabel + 'Minimal Timeline filter' + Minimal Timeline filter + + + minimalTimelineFilterTooltip + 'Show a minimal version of what to display in timeline.' : + Show a minimal version of what to display in timeline. + SettingsVideo @@ -2833,6 +2843,11 @@ Click here: <a href="%1">%1</a> 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. Without ephemerals + + timelineFilterConferences + 'Conferences' : Filter item. Selecting it will show all conferences. + Conferences + UseAppSipAccount diff --git a/linphone-app/resources.qrc b/linphone-app/resources.qrc index 40c729121..61ffa4d41 100644 --- a/linphone-app/resources.qrc +++ b/linphone-app/resources.qrc @@ -33,7 +33,9 @@ assets/images/chat_audio_pause_custom.svg assets/images/chat_audio_play_custom.svg assets/images/chat_audio_soundwave_custom.svg - assets/images/chat_audio_stop_custom.svg + assets/images/chat_audio_preview_pause_custom.svg + assets/images/chat_audio_preview_play_custom.svg + assets/images/chat_audio_preview_stop_custom.svg assets/images/chat_count.svg assets/images/chat_delivered.svg assets/images/chat_error.svg @@ -281,6 +283,7 @@ ui/modules/Linphone/Chat/ChatAudioMessage.qml ui/modules/Linphone/Chat/ChatAudioPreview.qml ui/modules/Linphone/Chat/ChatFileMessage.qml + ui/modules/Linphone/Chat/ChatFilePreview.qml ui/modules/Linphone/Chat/ChatForwardMessage.qml ui/modules/Linphone/Chat/ChatMessagePreview.qml ui/modules/Linphone/Chat/ChatReplyMessage.qml @@ -301,6 +304,7 @@ ui/modules/Linphone/Contact/Contact.qml ui/modules/Linphone/Dialog/OnlineInstallerDialog.qml ui/modules/Linphone/Dialog/SipAddressDialog.qml + ui/modules/Linphone/File/FileView.qml ui/modules/Linphone/History/History.qml ui/modules/Linphone/History/History.js ui/modules/Linphone/History/Event.qml @@ -327,6 +331,7 @@ ui/modules/Linphone/Styles/Chat/ChatStyle.qml ui/modules/Linphone/Styles/Chat/ChatAudioMessageStyle.qml ui/modules/Linphone/Styles/Chat/ChatAudioPreviewStyle.qml + ui/modules/Linphone/Styles/Chat/ChatFilePreviewStyle.qml ui/modules/Linphone/Styles/Chat/ChatForwardMessageStyle.qml ui/modules/Linphone/Styles/Chat/ChatReplyMessageStyle.qml ui/modules/Linphone/Styles/Codecs/CodecsViewerStyle.qml diff --git a/linphone-app/src/components/chat-events/ChatMessageModel.cpp b/linphone-app/src/components/chat-events/ChatMessageModel.cpp index 7f8eddfe9..1d40c2ff5 100644 --- a/linphone-app/src/components/chat-events/ChatMessageModel.cpp +++ b/linphone-app/src/components/chat-events/ChatMessageModel.cpp @@ -120,33 +120,36 @@ QString ChatMessageModel::AppDataManager::toString(){ } ChatMessageModel::ChatMessageModel ( std::shared_ptr chatMessage, QObject * parent) : ChatEvent(ChatRoomModel::EntryType::MessageEntry, parent) { App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it - mParticipantImdnStateListModel = std::make_shared(chatMessage); - mChatMessageListener = std::make_shared(this, parent); - mChatMessage = chatMessage; + if(chatMessage){ + mParticipantImdnStateListModel = std::make_shared(chatMessage); + mChatMessageListener = std::make_shared(this, parent); + mChatMessage = chatMessage; + mChatMessage->addListener(mChatMessageListener); + if( mChatMessage->isReply()){ + auto replyMessage = mChatMessage->getReplyMessage(); + if( replyMessage)// Reply message could be inexistant (for example : when locally deleted) + mReplyChatMessageModel = create(replyMessage, parent); + } + connect(this, &ChatMessageModel::remove, dynamic_cast(parent), &ChatRoomModel::removeEntry); + + std::list> contents = chatMessage->getContents(); + QString txt; + for(auto content : contents){ + if(content->isText()) + txt += content->getUtf8Text().c_str(); + } + mContent = txt; + } mWasDownloaded = false; - mChatMessage->addListener(mChatMessageListener); + mTimestamp = QDateTime::fromMSecsSinceEpoch(chatMessage->getTime() * 1000); - if( mChatMessage->isReply()){ - auto replyMessage = mChatMessage->getReplyMessage(); - if( replyMessage)// Reply message could be inexistant (for example : when locally deleted) - mReplyChatMessageModel = create(replyMessage, parent); - } - - connect(this, &ChatMessageModel::remove, dynamic_cast(parent), &ChatRoomModel::removeEntry); - - std::list> contents = chatMessage->getContents(); - QString txt; - for(auto content : contents){ - if(content->isText()) - txt += content->getUtf8Text().c_str(); - } - mContent = txt; mContentListModel = std::make_shared(this); } ChatMessageModel::~ChatMessageModel(){ - mChatMessage->removeListener(mChatMessageListener); + if(mChatMessage) + mChatMessage->removeListener(mChatMessageListener); } std::shared_ptr ChatMessageModel::create(std::shared_ptr chatMessage, QObject * parent){ auto model = std::make_shared(chatMessage, parent); @@ -163,7 +166,7 @@ std::shared_ptr ChatMessageModel::getContentModel(std::shared_ptr< //----------------------------------------------------------------------------------------------------------------------- QString ChatMessageModel::getFromDisplayName() const{ - return Utils::getDisplayName(mChatMessage->getFromAddress()); + return mChatMessage ? Utils::getDisplayName(mChatMessage->getFromAddress()) : ""; } QString ChatMessageModel::getFromDisplayNameReplyMessage() const{ @@ -174,40 +177,40 @@ QString ChatMessageModel::getFromDisplayNameReplyMessage() const{ } QString ChatMessageModel::getFromSipAddress() const{ - return Utils::cleanSipAddress(Utils::coreStringToAppString(mChatMessage->getFromAddress()->asStringUriOnly())); + return mChatMessage ? Utils::cleanSipAddress(Utils::coreStringToAppString(mChatMessage->getFromAddress()->asStringUriOnly())) : ""; } QString ChatMessageModel::getToDisplayName() const{ - return Utils::getDisplayName(mChatMessage->getToAddress()); + return mChatMessage ? Utils::getDisplayName(mChatMessage->getToAddress()) : ""; } QString ChatMessageModel::getToSipAddress() const{ - return Utils::cleanSipAddress(Utils::coreStringToAppString(mChatMessage->getToAddress()->asStringUriOnly())); + return mChatMessage ? Utils::cleanSipAddress(Utils::coreStringToAppString(mChatMessage->getToAddress()->asStringUriOnly())) : ""; } ContactModel * ChatMessageModel::getContactModel() const{ - return CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(Utils::coreStringToAppString(mChatMessage->getFromAddress()->asString())); + return mChatMessage ? CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(Utils::coreStringToAppString(mChatMessage->getFromAddress()->asString())) : nullptr; } bool ChatMessageModel::isEphemeral() const{ - return mChatMessage->isEphemeral(); + return mChatMessage && mChatMessage->isEphemeral(); } qint64 ChatMessageModel::getEphemeralExpireTime() const{ - time_t t = mChatMessage->getEphemeralExpireTime(); + time_t t = mChatMessage ? mChatMessage->getEphemeralExpireTime() : 0; return t >0 ? t - QDateTime::currentSecsSinceEpoch() : 0; } long ChatMessageModel::getEphemeralLifetime() const{ - return mChatMessage->getEphemeralLifetime(); + return mChatMessage ? mChatMessage->getEphemeralLifetime() : 0; } LinphoneEnums::ChatMessageState ChatMessageModel::getState() const{ - return LinphoneEnums::fromLinphone(mChatMessage->getState()); + return mChatMessage ? LinphoneEnums::fromLinphone(mChatMessage->getState()) : LinphoneEnums::ChatMessageStateIdle; } bool ChatMessageModel::isOutgoing() const{ - return mChatMessage->isOutgoing(); + return mChatMessage && mChatMessage->isOutgoing(); } ParticipantImdnStateProxyModel * ChatMessageModel::getProxyImdnStates(){ @@ -229,7 +232,7 @@ std::shared_ptr ChatMessageModel::getContents() const{ } bool ChatMessageModel::isReply() const{ - return mChatMessage->isReply(); + return mChatMessage && mChatMessage->isReply(); } ChatMessageModel * ChatMessageModel::getReplyChatMessageModel() const{ @@ -237,11 +240,11 @@ ChatMessageModel * ChatMessageModel::getReplyChatMessageModel() const{ } bool ChatMessageModel::isForward() const{ - return mChatMessage->isForward(); + return mChatMessage && mChatMessage->isForward(); } QString ChatMessageModel::getForwardInfo() const{ - return Utils::coreStringToAppString(mChatMessage->getForwardInfo()); + return mChatMessage ? Utils::coreStringToAppString(mChatMessage->getForwardInfo()) : ""; } QString ChatMessageModel::getForwardInfoDisplayName() const{ @@ -279,7 +282,6 @@ void ChatMessageModel::resendMessage (){ } } - void ChatMessageModel::deleteEvent(){ if (mChatMessage && mChatMessage->getFileTransferInformation()) {// Remove thumbnail mChatMessage->cancelFileTransfer(); @@ -293,7 +295,8 @@ void ChatMessageModel::deleteEvent(){ } mChatMessage->setAppdata("");// Remove completely Thumbnail from the message } - mChatMessage->getChatRoom()->deleteMessage(mChatMessage); + if(mChatMessage) + mChatMessage->getChatRoom()->deleteMessage(mChatMessage); } void ChatMessageModel::updateFileTransferInformation(){ mContentListModel->updateContents(this); @@ -317,7 +320,7 @@ void ChatMessageModel::onFileTransferProgressIndication (const std::shared_ptr &message, linphone::ChatMessage::State state) { updateFileTransferInformation();// On message state, file transfert information Content can be changed // File message downloaded. - if (state == linphone::ChatMessage::State::FileTransferDone && !mChatMessage->isOutgoing()) { + if (mChatMessage && state == linphone::ChatMessage::State::FileTransferDone && !mChatMessage->isOutgoing()) { mContentListModel->downloaded(); setWasDownloaded(true); App::getInstance()->getNotifier()->notifyReceivedFileMessage(message); diff --git a/linphone-app/src/components/chat-room/ChatRoomModel.cpp b/linphone-app/src/components/chat-room/ChatRoomModel.cpp index 975351d85..4642fc9bc 100644 --- a/linphone-app/src/components/chat-room/ChatRoomModel.cpp +++ b/linphone-app/src/components/chat-room/ChatRoomModel.cpp @@ -38,6 +38,7 @@ #include "app/paths/Paths.hpp" #include "app/providers/ThumbnailProvider.hpp" #include "components/calls/CallsListModel.hpp" +#include "components/chat/ChatModel.hpp" #include "components/chat-events/ChatCallModel.hpp" #include "components/chat-events/ChatEvent.hpp" #include "components/chat-events/ChatMessageModel.hpp" @@ -45,6 +46,8 @@ #include "components/contact/ContactModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/contacts/ContactsListModel.hpp" +#include "components/content/ContentListModel.hpp" +#include "components/content/ContentModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/notifier/Notifier.hpp" @@ -567,6 +570,10 @@ QString ChatRoomModel::getParticipantAddress(){ } } +int ChatRoomModel::getAllUnreadCount(){ + return mUnreadMessagesCount + mMissedCallsCount; +} + //------------------------------------------------------------------------------------------------ void ChatRoomModel::setSubject(QString& subject){ @@ -654,7 +661,6 @@ ChatMessageModel * ChatRoomModel::getReply()const{ return mReplyModel.get(); } - //------------------------------------------------------------------------------------------------ void ChatRoomModel::deleteChatRoom(){ @@ -694,63 +700,33 @@ void ChatRoomModel::updateParticipants(const QVariantList& participants){ void ChatRoomModel::sendMessage (const QString &message) { shared_ptr _message; - if(mReplyModel && mReplyModel->getChatMessage()) + if(mReplyModel && mReplyModel->getChatMessage()) { _message = mChatRoom->createReplyMessage(mReplyModel->getChatMessage()); - else + }else _message= mChatRoom->createEmptyMessage(); auto recorder = CoreManager::getInstance()->getRecorderManager(); if(recorder->haveVocalRecorder()) { recorder->getVocalRecorder()->stop(); auto content = recorder->getVocalRecorder()->getRecorder()->createContent(); - if(content) + if(content) { _message->addContent(content); - } - if(!message.isEmpty()) - _message->addUtf8TextContent(message.toUtf8().toStdString()); - _message->send(); - emit messageSent(_message); - setReply(nullptr); - if(recorder->haveVocalRecorder()) - recorder->clearVocalRecorder(); -} - -void ChatRoomModel::sendFileMessage (const QString &path) { - - QFile file(path); - if (!file.exists()) - return; - - qint64 fileSize = file.size(); - if (fileSize > Constants::FileSizeLimit) { - qWarning() << QStringLiteral("Unable to send file. (Size limit=%1)").arg(Constants::FileSizeLimit); - return; - } - - shared_ptr content = CoreManager::getInstance()->getCore()->createContent(); - { - QStringList mimeType = QMimeDatabase().mimeTypeForFile(path).name().split('/'); - if (mimeType.length() != 2) { - qWarning() << QStringLiteral("Unable to get supported mime type for: `%1`.").arg(path); - return; } - content->setType(Utils::appStringToCoreString(mimeType[0])); - content->setSubtype(Utils::appStringToCoreString(mimeType[1])); } - content->setSize(size_t(fileSize)); - content->setName(QFileInfo(file).fileName().toStdString()); - - shared_ptr message = mChatRoom->createFileTransferMessage(content); - message->getContents().front()->setFilePath(Utils::appStringToCoreString(path)); - - auto recorder = CoreManager::getInstance()->getRecorderManager(); - if(recorder->haveVocalRecorder()) { - auto content = recorder->getVocalRecorder()->getRecorder()->createContent(); - if(content) - message->addContent(content); + auto fileContents = CoreManager::getInstance()->getChatModel()->getContentListModel()->getContents(); + for(auto content : fileContents){ + _message->addFileContent(content->getContent()); + } + if(!message.isEmpty()) { + _message->addUtf8TextContent(message.toUtf8().toStdString()); + } + if(_message->getContents().size() > 0){// Have something to send + _message->send(); + emit messageSent(_message); + setReply(nullptr); + if(recorder->haveVocalRecorder()) + recorder->clearVocalRecorder(); + CoreManager::getInstance()->getChatModel()->clear(); } - message->send(); - - emit messageSent(message); } void ChatRoomModel::forwardMessage(ChatMessageModel * model){ diff --git a/linphone-app/src/components/chat-room/ChatRoomModel.hpp b/linphone-app/src/components/chat-room/ChatRoomModel.hpp index 9079da4a3..0379089f2 100644 --- a/linphone-app/src/components/chat-room/ChatRoomModel.hpp +++ b/linphone-app/src/components/chat-room/ChatRoomModel.hpp @@ -154,8 +154,6 @@ public: Q_PROPERTY(bool entriesLoading READ isEntriesLoading WRITE setEntriesLoading NOTIFY entriesLoadingChanged) - - //ChatRoomModel (const QString &peerAddress, const QString &localAddress, const bool& isSecure); static std::shared_ptr create(std::shared_ptr chatRoom); ChatRoomModel (std::shared_ptr chatRoom, QObject * parent = nullptr); ~ChatRoomModel (); @@ -202,6 +200,7 @@ public: std::shared_ptr getChatRoom(); QList getComposers(); QString getParticipantAddress(); // return peerAddress if not secure else return the first participant SIP address. + int getAllUnreadCount(); // Return unread messages and missed call. //---- Setters void setSubject(QString& subject); @@ -219,6 +218,8 @@ public: void setReply(ChatMessageModel * model); ChatMessageModel * getReply()const; void clearReply(); + + void clearFilesToSend(); // Tools @@ -226,7 +227,6 @@ public: Q_INVOKABLE void leaveChatRoom (); Q_INVOKABLE void updateParticipants(const QVariantList& participants); void sendMessage (const QString &message); - void sendFileMessage (const QString &path); Q_INVOKABLE void forwardMessage(ChatMessageModel * model); void compose (); Q_INVOKABLE void resetMessageCount (); diff --git a/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp b/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp index 7eb98792f..fe2a6bc4d 100644 --- a/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp +++ b/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp @@ -82,7 +82,6 @@ ChatRoomProxyModel::ChatRoomProxyModel (QObject *parent) : QSortFilterProxyModel CREATE_PARENT_MODEL_FUNCTION(removeAllEntries) -CREATE_PARENT_MODEL_FUNCTION_WITH_PARAM(sendFileMessage, const QString &) CREATE_PARENT_MODEL_FUNCTION_WITH_PARAM(sendMessage, const QString &) CREATE_PARENT_MODEL_FUNCTION_WITH_PARAM(forwardMessage, ChatMessageModel *) diff --git a/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp b/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp index f25bff3ce..ed0b32221 100644 --- a/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp +++ b/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp @@ -67,8 +67,6 @@ public: Q_INVOKABLE void sendMessage (const QString &message); - Q_INVOKABLE void sendFileMessage (const QString &path); - Q_INVOKABLE void forwardMessage(ChatMessageModel * model); Q_INVOKABLE void compose (const QString& text); diff --git a/linphone-app/src/components/chat/ChatModel.cpp b/linphone-app/src/components/chat/ChatModel.cpp new file mode 100644 index 000000000..b3377a105 --- /dev/null +++ b/linphone-app/src/components/chat/ChatModel.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ChatModel.hpp" + +#include +#include +#include +#include + +#include "app/App.hpp" +#include "app/paths/Paths.hpp" +#include "app/providers/ThumbnailProvider.hpp" + +#include "components/chat-events/ChatMessageModel.hpp" + +#include "utils/QExifImageHeader.hpp" +#include "utils/Utils.hpp" +#include "utils/Constants.hpp" +#include "components/Components.hpp" + +// ============================================================================= + +ChatModel::ChatModel(QObject * parent ) : QObject(parent){ + App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE + mContents = std::make_shared(nullptr); +} + +std::shared_ptr ChatModel::getContentListModel() const { + return mContents; +} + +void ChatModel::clear() { + mContents->clear(); +} \ No newline at end of file diff --git a/linphone-app/src/components/chat/ChatModel.hpp b/linphone-app/src/components/chat/ChatModel.hpp new file mode 100644 index 000000000..5197ccf7f --- /dev/null +++ b/linphone-app/src/components/chat/ChatModel.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// Used to store data between chats + +#ifndef CHAT_MODEL_H_ +#define CHAT_MODEL_H_ + +#include +// ============================================================================= +#include +#include +#include +class ContentListModel; + +class ChatModel : public QObject{ + Q_OBJECT +public: + ChatModel(QObject * parent = nullptr); + +// Getters + std::shared_ptr getContentListModel() const; + +// Tools + Q_INVOKABLE void clear(); + +private: + std::shared_ptr mContents; +}; + +#endif diff --git a/linphone-app/src/components/content/ContentListModel.cpp b/linphone-app/src/components/content/ContentListModel.cpp index 304aa03e5..cd22115c0 100644 --- a/linphone-app/src/components/content/ContentListModel.cpp +++ b/linphone-app/src/components/content/ContentListModel.cpp @@ -24,6 +24,7 @@ #include "ContentListModel.hpp" #include "ContentModel.hpp" +#include "utils/Constants.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" @@ -31,11 +32,14 @@ // ============================================================================= ContentListModel::ContentListModel (ChatMessageModel * message) : QAbstractListModel(message) { - std::list> contents = message->getChatMessage()->getContents() ; - for(auto content : contents){ - auto contentModel = std::make_shared(content, message); - connect(this, &ContentListModel::updateTransferDataRequested, contentModel.get(), &ContentModel::updateTransferData); - mList << contentModel; + mParent = message; + if(message){ + std::list> contents = message->getChatMessage()->getContents() ; + for(auto content : contents){ + auto contentModel = std::make_shared(content, message); + connect(this, &ContentListModel::updateTransferDataRequested, contentModel.get(), &ContentModel::updateTransferData); + mList << contentModel; + } } } @@ -65,6 +69,57 @@ QVariant ContentListModel::data (const QModelIndex &index, int role) const { return QVariant(); } +std::shared_ptr ContentListModel::add(std::shared_ptr content){ + int row = mList.count(); + auto contentModel = std::make_shared(content, mParent); + beginInsertRows(QModelIndex(), row, row); + mList << contentModel; + endInsertRows(); + emit contentsChanged(); + return contentModel; +} + +void ContentListModel::addFile(const QString& path){ + QFile file(path); + if (!file.exists()) + return; + + qint64 fileSize = file.size(); + if (fileSize > Constants::FileSizeLimit) { + qWarning() << QStringLiteral("Unable to send file. (Size limit=%1)").arg(Constants::FileSizeLimit); + return; + } + + std::shared_ptr content = CoreManager::getInstance()->getCore()->createContent(); + { + QStringList mimeType = QMimeDatabase().mimeTypeForFile(path).name().split('/'); + if (mimeType.length() != 2) { + qWarning() << QStringLiteral("Unable to get supported mime type for: `%1`.").arg(path); + return; + } + content->setType(Utils::appStringToCoreString(mimeType[0])); + content->setSubtype(Utils::appStringToCoreString(mimeType[1])); + } + content->setSize(size_t(fileSize)); + content->setName(QFileInfo(file).fileName().toStdString()); + content->setFilePath(Utils::appStringToCoreString(path)); + + auto modelAdded = add(content); + if(!content->isFile()) + modelAdded->createThumbnail(true); // Was not created because linphone::Content is not considered as a file (yet) +} + +void ContentListModel::remove(ContentModel * model){ + int count = 0; + for(auto it = mList.begin() ; it != mList.end() ; ++count, ++it) { + if( it->get() == model) { + model->removeThumbnail(); + removeRow(count, QModelIndex()); + return; + } + } +} + bool ContentListModel::removeRow (int row, const QModelIndex &parent){ return removeRows(row, 1, parent); } @@ -84,6 +139,17 @@ bool ContentListModel::removeRows (int row, int count, const QModelIndex &parent return true; } + +void ContentListModel::clear(){ +// Delete thumbnails + for(auto contentModel : mList){ + contentModel->removeThumbnail(); + } + beginResetModel(); + mList.clear(); + endResetModel(); +} + std::shared_ptr ContentListModel::getContentModel(std::shared_ptr content){ for(auto c : mList) if(c->getContent() == content) @@ -95,6 +161,11 @@ std::shared_ptr ContentListModel::getContentModel(std::shared_ptr< } return nullptr; } + +QList> ContentListModel::getContents(){ + return mList; +} + void ContentListModel::updateContent(std::shared_ptr oldContent, std::shared_ptr newContent){ int row = 0; for(auto content = mList.begin() ; content != mList.end() ; ++content, ++row){ diff --git a/linphone-app/src/components/content/ContentListModel.hpp b/linphone-app/src/components/content/ContentListModel.hpp index e5821dc93..6ed96b002 100644 --- a/linphone-app/src/components/content/ContentListModel.hpp +++ b/linphone-app/src/components/content/ContentListModel.hpp @@ -43,8 +43,15 @@ public: virtual QHash roleNames () const override; virtual QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; + std::shared_ptr add(std::shared_ptr content); + void addFile(const QString& path); + Q_INVOKABLE void remove(ContentModel * model); + + void clear(); std::shared_ptr getContentModel(std::shared_ptr content);// Return the contentModel by checking Content, or if it is the same file. + QList> getContents(); + void updateContent(std::shared_ptr oldContent, std::shared_ptr newContent); void updateContents(ChatMessageModel * messageModel); void updateAllTransferData(); @@ -52,12 +59,14 @@ public: signals: void updateTransferDataRequested(); + void contentsChanged(); private: bool removeRow (int row, const QModelIndex &parent = QModelIndex()); virtual bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; QList> mList; + ChatMessageModel * mParent; }; diff --git a/linphone-app/src/components/content/ContentModel.cpp b/linphone-app/src/components/content/ContentModel.cpp index d8bdff3ea..d82f4ef58 100644 --- a/linphone-app/src/components/content/ContentModel.cpp +++ b/linphone-app/src/components/content/ContentModel.cpp @@ -38,13 +38,13 @@ // ============================================================================= -ContentModel::ContentModel(ChatMessageModel* chatModel){ +ContentModel::ContentModel(ChatMessageModel* chatModel) : mAppData(""){ App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mChatMessageModel = chatModel; mWasDownloaded = false; mFileOffset = 0; } -ContentModel::ContentModel(std::shared_ptr content, ChatMessageModel* chatModel){ +ContentModel::ContentModel(std::shared_ptr content, ChatMessageModel* chatModel) : mAppData(""){ App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mChatMessageModel = chatModel; mWasDownloaded = false; @@ -131,12 +131,12 @@ bool ContentModel::isVoiceRecording()const{ } // Create a thumbnail from the first content that have a file and store it in Appdata -void ContentModel::createThumbnail () { - if(isFile() || isFileEncrypted() || isFileTransfer()){ +void ContentModel::createThumbnail (const bool& force) { + if(force || isFile() || isFileEncrypted() || isFileTransfer()){ QString id; QString path = getFilePath(); - auto appdata = ChatMessageModel::AppDataManager(QString::fromStdString(mChatMessageModel->getChatMessage()->getAppdata())); + auto appdata = ChatMessageModel::AppDataManager(mChatMessageModel ? QString::fromStdString(mChatMessageModel->getChatMessage()->getAppdata()) : ""); if(!appdata.mData.contains(path) || !QFileInfo(QString::fromStdString(Paths::getThumbnailsDirPath())+appdata.mData[path]).isFile()){ @@ -179,7 +179,9 @@ void ContentModel::createThumbnail () { qWarning() << QStringLiteral("Unable to create thumbnail of: `%1`.").arg(path); }else{ appdata.mData[path] = id; - mChatMessageModel->getChatMessage()->setAppdata(appdata.toString().toStdString()); + mAppData.mData[path] = id; + if(mChatMessageModel) + mChatMessageModel->getChatMessage()->setAppdata(appdata.toString().toStdString()); } } } @@ -192,6 +194,16 @@ void ContentModel::createThumbnail () { } } +void ContentModel::removeThumbnail(){ + for(QMap::iterator itData = mAppData.mData.begin() ; itData != mAppData.mData.end() ; ++itData){ + QString thumbnailPath = QString::fromStdString(Paths::getThumbnailsDirPath()) +itData.value(); + if( QFileInfo(thumbnailPath).isFile()){ + QFile(thumbnailPath).remove(); + } + } + mAppData.mData.clear(); +} + void ContentModel::downloadFile(){ switch (mChatMessageModel->getState()) { case LinphoneEnums::ChatMessageStateDelivered: @@ -228,10 +240,11 @@ void ContentModel::downloadFile(){ } void ContentModel::openFile (bool showDirectory) { - if ((!mWasDownloaded && !mChatMessageModel->isOutgoing()) || mContent->getFilePath() == "") { + if (mChatMessageModel && ((!mWasDownloaded && !mChatMessageModel->isOutgoing()) || mContent->getFilePath() == "")) { downloadFile(); }else{ QFileInfo info( Utils::coreStringToAppString(mContent->getFilePath())); + showDirectory = showDirectory || !info.exists(); QDesktopServices::openUrl( QUrl(QStringLiteral("file:///%1").arg(showDirectory ? info.absolutePath() : info.absoluteFilePath())) ); diff --git a/linphone-app/src/components/content/ContentModel.hpp b/linphone-app/src/components/content/ContentModel.hpp index 3029be813..d379a70ce 100644 --- a/linphone-app/src/components/content/ContentModel.hpp +++ b/linphone-app/src/components/content/ContentModel.hpp @@ -27,7 +27,8 @@ #include #include #include -class ChatMessageModel; + +#include "components/chat-events/ChatMessageModel.hpp" class ContentModel : public QObject{ Q_OBJECT @@ -67,9 +68,11 @@ public: Q_INVOKABLE bool isText() const; Q_INVOKABLE bool isVoiceRecording()const; - void createThumbnail (); + void createThumbnail (const bool& force = false); + void removeThumbnail (); + Q_INVOKABLE void downloadFile(); - Q_INVOKABLE void openFile (bool showDirectory = false); + Q_INVOKABLE void openFile (bool showDirectory = false); QString mThumbnail; @@ -88,6 +91,7 @@ signals: private: std::shared_ptr mContent; ChatMessageModel* mChatMessageModel; + ChatMessageModel::AppDataManager mAppData; // Used if there is no Chat Message model set. }; Q_DECLARE_METATYPE(std::shared_ptr) diff --git a/linphone-app/src/components/content/ContentProxyModel.cpp b/linphone-app/src/components/content/ContentProxyModel.cpp index 1cdd3c099..9ac520d92 100644 --- a/linphone-app/src/components/content/ContentProxyModel.cpp +++ b/linphone-app/src/components/content/ContentProxyModel.cpp @@ -26,13 +26,14 @@ #include "utils/Utils.hpp" #include "components/Components.hpp" +#include "components/chat/ChatModel.hpp" #include "components/chat-events/ChatMessageModel.hpp" #include "ContentListModel.hpp" // ============================================================================= ContentProxyModel::ContentProxyModel (QObject * parent) : QSortFilterProxyModel(parent){ - + setContentListModel(CoreManager::getInstance()->getChatModel()->getContentListModel().get()); } void ContentProxyModel::setChatMessageModel(ChatMessageModel * message){ @@ -43,6 +44,17 @@ void ContentProxyModel::setChatMessageModel(ChatMessageModel * message){ emit chatMessageModelChanged(); } +void ContentProxyModel::setContentListModel(ContentListModel * model){ + setSourceModel(model); + sort(0); + emit chatMessageModelChanged(); +} + +void ContentProxyModel::addFile(const QString& path){ + ContentListModel* model = dynamic_cast(sourceModel()); + model->addFile(path); +} + bool ContentProxyModel::filterAcceptsRow ( int sourceRow, const QModelIndex &sourceParent @@ -55,13 +67,13 @@ bool ContentProxyModel::filterAcceptsRow ( bool ContentProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { const ContentModel *contentA = sourceModel()->data(left).value(); const ContentModel *contentB = sourceModel()->data(right).value(); - bool aIsForward = contentA->getChatMessageModel()->isForward(); - bool aIsReply = contentA->getChatMessageModel()->isReply(); + bool aIsForward = contentA->getChatMessageModel() && contentA->getChatMessageModel()->isForward(); + bool aIsReply = contentA->getChatMessageModel() && contentA->getChatMessageModel()->isReply(); bool aIsVoiceRecording = contentA->isVoiceRecording(); bool aIsFile = contentA->isFile() || contentA->isFileEncrypted() || contentA->isFileTransfer(); bool aIsText = contentA->isText() ; - bool bIsForward = contentB->getChatMessageModel()->isForward(); - bool bIsReply = contentB->getChatMessageModel()->isReply(); + bool bIsForward = contentB->getChatMessageModel() && contentB->getChatMessageModel()->isForward(); + bool bIsReply = contentB->getChatMessageModel() && contentB->getChatMessageModel()->isReply(); bool bIsVoiceRecording = contentB->isVoiceRecording(); bool bIsFile = contentB->isFile() || contentB->isFileEncrypted() || contentB->isFileTransfer(); bool bIsText = contentB->isText() ; @@ -76,3 +88,10 @@ bool ContentProxyModel::lessThan (const QModelIndex &left, const QModelIndex &ri ) ); } +void ContentProxyModel::remove(ContentModel * model){ + dynamic_cast(sourceModel())->remove(model); +} + +void ContentProxyModel::clear(){ + dynamic_cast(sourceModel())->clear(); +} \ No newline at end of file diff --git a/linphone-app/src/components/content/ContentProxyModel.hpp b/linphone-app/src/components/content/ContentProxyModel.hpp index 9bb9b2cdc..25a029aaf 100644 --- a/linphone-app/src/components/content/ContentProxyModel.hpp +++ b/linphone-app/src/components/content/ContentProxyModel.hpp @@ -41,6 +41,10 @@ public: Q_PROPERTY(ChatMessageModel * chatMessageModel WRITE setChatMessageModel NOTIFY chatMessageModelChanged) void setChatMessageModel(ChatMessageModel * message); + Q_INVOKABLE void setContentListModel(ContentListModel * model); + Q_INVOKABLE void addFile(const QString& path); + Q_INVOKABLE void remove(ContentModel * model); + Q_INVOKABLE void clear(); signals: void chatMessageModelChanged(); @@ -49,7 +53,7 @@ protected: virtual bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; virtual bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; - std::shared_ptr mContens; + std::shared_ptr mContents; }; diff --git a/linphone-app/src/components/core/CoreManager.cpp b/linphone-app/src/components/core/CoreManager.cpp index 099cef8b5..9b6be783c 100644 --- a/linphone-app/src/components/core/CoreManager.cpp +++ b/linphone-app/src/components/core/CoreManager.cpp @@ -29,6 +29,7 @@ #include "app/paths/Paths.hpp" #include "components/calls/CallsListModel.hpp" +#include "components/chat/ChatModel.hpp" #include "components/chat-room/ChatRoomModel.hpp" #include "components/chat-room/ChatRoomListModel.hpp" #include "components/contact/VcardModel.hpp" @@ -90,6 +91,7 @@ CoreManager::~CoreManager(){ void CoreManager::initCoreManager(){ mCallsListModel = new CallsListModel(this); + mChatModel = new ChatModel(this); mChatRoomListModel = new ChatRoomListModel(this); mContactsListModel = new ContactsListModel(this); mContactsImporterListModel = new ContactsImporterListModel(this); diff --git a/linphone-app/src/components/core/CoreManager.hpp b/linphone-app/src/components/core/CoreManager.hpp index 541759053..c219588d6 100644 --- a/linphone-app/src/components/core/CoreManager.hpp +++ b/linphone-app/src/components/core/CoreManager.hpp @@ -34,6 +34,7 @@ class QTimer; class AbstractEventCountNotifier; class AccountSettingsModel; class CallsListModel; +class ChatModel; class ChatRoomModel; class ChatRoomListModel; class ContactsListModel; @@ -140,6 +141,10 @@ public: AbstractEventCountNotifier * getEventCountNotifier(); + ChatModel * getChatModel() const{ + return mChatModel; + } + static CoreManager *getInstance (); // --------------------------------------------------------------------------- @@ -220,6 +225,7 @@ private: ContactsImporterListModel *mContactsImporterListModel = nullptr; TimelineListModel *mTimelineListModel = nullptr; ChatRoomListModel *mChatRoomListModel = nullptr; + ChatModel *mChatModel = nullptr; SipAddressesModel *mSipAddressesModel = nullptr; SettingsModel *mSettingsModel = nullptr; diff --git a/linphone-app/src/components/other/colors/ColorListModel.hpp b/linphone-app/src/components/other/colors/ColorListModel.hpp index 4cb3daa24..4529d9597 100644 --- a/linphone-app/src/components/other/colors/ColorListModel.hpp +++ b/linphone-app/src/components/other/colors/ColorListModel.hpp @@ -217,7 +217,17 @@ class ColorListModel : public QAbstractListModel { ADD_COLOR("me_d_b_inv_fg", "#80FFFFFF", "[M] Menu disabled button : inverse foreground") ADD_COLOR("me_h_b_inv_fg", "#B0FFFFFF", "[M] Menu hovered button : inverse foreground") ADD_COLOR("me_p_b_inv_fg", "white", "[M] Menu pressed button : inverse foreground") +//------------------------------------- +// Wave Play + ADD_COLOR_WITH_LINK("w_n_b_bg", "", "[M] Wave play normal button : background", "ma_n_b_bg") + ADD_COLOR_WITH_LINK("w_d_b_bg", "", "[M] Wave play disabled button : background", "ma_d_b_bg") + ADD_COLOR_WITH_LINK("w_h_b_bg", "", "[M] Wave play hovered button : background", "ma_h_b_bg") + ADD_COLOR_WITH_LINK("w_p_b_bg", "", "[M] Wave play pressed button : background", "ma_p_b_bg") + ADD_COLOR_WITH_LINK("w_n_b_fg", "", "[M] Wave play normal button : foreground", "ma_n_b_fg") + ADD_COLOR_WITH_LINK("w_d_b_fg", "", "[M] Wave play disabled button : foreground", "ma_d_b_fg") + ADD_COLOR_WITH_LINK("w_h_b_fg", "", "[M] Wave play hovered button : foreground", "ma_h_b_fg") + ADD_COLOR_WITH_LINK("w_p_b_fg", "", "[M] Wave play pressed button : foreground", "ma_p_b_fg") //-------------------------------------------------------------------------------------------------------------------- /* diff --git a/linphone-app/src/components/settings/SettingsModel.cpp b/linphone-app/src/components/settings/SettingsModel.cpp index 8860231d4..9f1c51a7b 100644 --- a/linphone-app/src/components/settings/SettingsModel.cpp +++ b/linphone-app/src/components/settings/SettingsModel.cpp @@ -1372,7 +1372,16 @@ void SettingsModel::setMipmapEnabled(const bool& enabled){ mConfig->setInt(UiSection, "mipmap_enabled", enabled); emit mipmapEnabledChanged(); } - + +bool SettingsModel::useMinimalTimelineFilter() const{ + return !!mConfig->getInt(UiSection, "use_minimal_timeline_filter", 1); +} + +void SettingsModel::setUseMinimalTimelineFilter(const bool& useMinimal) { + mConfig->setInt(UiSection, "use_minimal_timeline_filter", useMinimal); + emit useMinimalTimelineFilterChanged(); +} + // ============================================================================= // Advanced. // ============================================================================= diff --git a/linphone-app/src/components/settings/SettingsModel.hpp b/linphone-app/src/components/settings/SettingsModel.hpp index d6f4139c3..9db273962 100644 --- a/linphone-app/src/components/settings/SettingsModel.hpp +++ b/linphone-app/src/components/settings/SettingsModel.hpp @@ -197,6 +197,7 @@ class SettingsModel : public QObject { Q_PROPERTY(bool showStartVideoCallButton READ getShowStartVideoCallButton CONSTANT) Q_PROPERTY(bool mipmapEnabled READ isMipmapEnabled WRITE setMipmapEnabled NOTIFY mipmapEnabledChanged) + Q_PROPERTY(bool useMinimalTimelineFilter READ useMinimalTimelineFilter WRITE setUseMinimalTimelineFilter NOTIFY useMinimalTimelineFilterChanged) // Advanced. ----------------------------------------------------------------- @@ -509,6 +510,9 @@ public: bool isMipmapEnabled() const; void setMipmapEnabled(const bool& enabled); + bool useMinimalTimelineFilter() const; + void setUseMinimalTimelineFilter(const bool& useMinimal); + // Advanced. --------------------------------------------------------------------------- @@ -688,6 +692,7 @@ signals: void exitOnCloseChanged (bool value); void mipmapEnabledChanged(); + void useMinimalTimelineFilterChanged(); void checkForUpdateEnabledChanged(); void versionCheckUrlChanged(); diff --git a/linphone-app/src/components/sound-player/SoundPlayer.cpp b/linphone-app/src/components/sound-player/SoundPlayer.cpp index 2beedeb90..fae3b9806 100644 --- a/linphone-app/src/components/sound-player/SoundPlayer.cpp +++ b/linphone-app/src/components/sound-player/SoundPlayer.cpp @@ -138,7 +138,7 @@ void SoundPlayer::buildInternalPlayer () { SettingsModel *settingsModel = coreManager->getSettingsModel(); mInternalPlayer = coreManager->getCore()->createLocalPlayer( - Utils::appStringToCoreString(settingsModel->getRingerDevice()), "", nullptr + Utils::appStringToCoreString(settingsModel->getPlaybackDevice()), "", nullptr ); if(mInternalPlayer) mInternalPlayer->addListener(mHandlers); diff --git a/linphone-app/src/components/timeline/TimelineProxyModel.cpp b/linphone-app/src/components/timeline/TimelineProxyModel.cpp index f70e149ec..9d0825f5d 100644 --- a/linphone-app/src/components/timeline/TimelineProxyModel.cpp +++ b/linphone-app/src/components/timeline/TimelineProxyModel.cpp @@ -123,6 +123,8 @@ bool TimelineProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &sou bool TimelineProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { const TimelineModel* a = sourceModel()->data(left).value(); const TimelineModel* b = sourceModel()->data(right).value(); - - return a->getChatRoomModel()->mLastUpdateTime > b->getChatRoomModel()->mLastUpdateTime; + bool aHaveUnread = a->getChatRoomModel()->getAllUnreadCount() > 0; + bool bHaveUnread = b->getChatRoomModel()->getAllUnreadCount() > 0; + return (aHaveUnread && !bHaveUnread) + || (aHaveUnread == bHaveUnread && a->getChatRoomModel()->mLastUpdateTime > b->getChatRoomModel()->mLastUpdateTime); } diff --git a/linphone-app/ui/modules/Common/Form/DroppableTextArea.qml b/linphone-app/ui/modules/Common/Form/DroppableTextArea.qml index 5837a211f..14e802e8e 100644 --- a/linphone-app/ui/modules/Common/Form/DroppableTextArea.qml +++ b/linphone-app/ui/modules/Common/Form/DroppableTextArea.qml @@ -19,6 +19,7 @@ Item { property alias placeholderText: textArea.placeholderText property alias text: textArea.text property alias cursorPosition: textArea.cursorPosition + property alias recordAudioToggled: recordAudioButton.toggled property bool dropEnabled: true property string dropDisabledReason @@ -56,11 +57,11 @@ Item { // --------------------------------------------------------------------------- RowLayout{ anchors.fill: parent - spacing: DroppableTextAreaStyle.fileChooserButton.margins + spacing: 0 // Handle click to select files. ActionButton { id: fileChooserButton - property int totalWidth: DroppableTextAreaStyle.fileChooserButton.margins + width + property int totalWidth: width Layout.leftMargin: DroppableTextAreaStyle.fileChooserButton.margins Layout.alignment: Qt.AlignVCenter @@ -88,12 +89,11 @@ Item { } // Record audio ActionButton { - visible:droppableTextArea.enabled id: recordAudioButton + visible: droppableTextArea.enabled - //anchors.verticalCenter: parent.verticalCenter Layout.alignment: Qt.AlignVCenter - + Layout.leftMargin: 0 enabled: droppableTextArea.dropEnabled isCustom: true backgroundRadius: 8 @@ -111,6 +111,7 @@ Item { Layout.maximumHeight: parent.height-20 Layout.topMargin: 10 Layout.bottomMargin: 10 + Layout.leftMargin: 2 //anchors.fill: parent boundsBehavior: Flickable.StopAtBounds clip:true @@ -137,9 +138,7 @@ Item { } } function handleValidation () { - if (RecorderManager.haveVocalRecorder || text.length !== 0) { validText(text) - } } background: Rectangle { @@ -194,7 +193,7 @@ Item { ActionButton { id: sendButton property int totalWidth: Layout.rightMargin + Layout.leftMargin + width - Layout.rightMargin: DroppableTextAreaStyle.fileChooserButton.margins+15 + Layout.rightMargin: 15 Layout.leftMargin: 10 Layout.alignment: Qt.AlignVCenter visible: droppableTextArea.enabled diff --git a/linphone-app/ui/modules/Common/Form/Fields/TextField.qml b/linphone-app/ui/modules/Common/Form/Fields/TextField.qml index 47b90fe18..239160499 100644 --- a/linphone-app/ui/modules/Common/Form/Fields/TextField.qml +++ b/linphone-app/ui/modules/Common/Form/Fields/TextField.qml @@ -19,7 +19,7 @@ Controls.TextField { property string error: '' property var tools property QtObject textFieldStyle : TextFieldStyle.normal - property bool persistentIcon: false + property bool showWhenEmpty: true onTextFieldStyleChanged: if( !textFieldStyle) textFieldStyle = TextFieldStyle.normal signal iconClicked() @@ -87,7 +87,7 @@ Controls.TextField { } iconSize: parent.contentHeight - visible: persistentIcon || !parent.text + visible: showWhenEmpty && !parent.text || !showWhenEmpty && parent.text MouseArea{ anchors.fill: parent onClicked: textField.iconClicked() diff --git a/linphone-app/ui/modules/Common/Indicators/MediaProgressBar.qml b/linphone-app/ui/modules/Common/Indicators/MediaProgressBar.qml index c640b3616..89a70b7cc 100644 --- a/linphone-app/ui/modules/Common/Indicators/MediaProgressBar.qml +++ b/linphone-app/ui/modules/Common/Indicators/MediaProgressBar.qml @@ -21,6 +21,7 @@ ProgressBar { property alias colorSet: progression.colorSet property alias backgroundColor: backgroundArea.color property alias durationTextColor: durationText.color + property int waveLeftMargin: 0 function start(){ progressBar.value = 0 @@ -61,13 +62,14 @@ ProgressBar { progression.percentageDisplayed = value } - anchors.topMargin: 5 - anchors.bottomMargin: 5 + anchors.topMargin: 2 + anchors.bottomMargin: 2 background: Rectangle { id: backgroundArea color: MediaProgressBarStyle.backgroundColor radius: 5 + clip: false } @@ -76,16 +78,19 @@ ProgressBar { anchors.fill: parent radius: 5 color: 'transparent' + clip: false RowLayout{ anchors.fill: parent - spacing: 10 + spacing: 0 ActionButton{ id: progression Layout.fillWidth: true Layout.fillHeight: true + Layout.leftMargin: progressBar.waveLeftMargin backgroundRadius: 5 fillMode: Image.TileHorizontally verticalAlignment: Image.AlignLeft + horizontalAlignment: Image.AlignLeft isCustom: true colorSet: MediaProgressBarStyle.progressionWave percentageDisplayed: 0 @@ -95,13 +100,15 @@ ProgressBar { id: durationText Layout.fillHeight: true Layout.preferredWidth: implicitWidth - Layout.rightMargin: 5 + Layout.leftMargin: 15 + Layout.rightMargin: 6 horizontalAlignment: Qt.AlignRight verticalAlignment: Qt.AlignVCenter - text: progressBar.progressPosition >= 0 ? Utils.formatElapsedTime( progressBar.progressPosition / 1000 ) : '-' + text: progressBar.progressPosition > 0 ? Utils.formatElapsedTime( progressBar.progressPosition / 1000 ) + :( progressBar.progressPosition == 0 ? Utils.formatElapsedTime( progressBar.progressDuration / 1000) : '-') property font customFont : SettingsModel.textMessageFont font.family: customFont.family - font.pointSize: Units.dp * (customFont.pointSize + 2) + font.pointSize: Units.dp * (customFont.pointSize + 1) } } } diff --git a/linphone-app/ui/modules/Common/Styles/Form/DroppableTextAreaStyle.qml b/linphone-app/ui/modules/Common/Styles/Form/DroppableTextAreaStyle.qml index fad890de4..d72a4d0d1 100644 --- a/linphone-app/ui/modules/Common/Styles/Form/DroppableTextAreaStyle.qml +++ b/linphone-app/ui/modules/Common/Styles/Form/DroppableTextAreaStyle.qml @@ -22,11 +22,11 @@ QtObject { property int iconSize: 40 property string icon : 'attachment_custom' property string name : 'attachment' - property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color - property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color - property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color - property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } @@ -35,24 +35,27 @@ QtObject { property int iconSize: 40 property string name : 'micro' property string icon : 'chat_micro_custom' - property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color - property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color - property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color - property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color + property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 'me_p_b_bg').color + + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color + property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 'me_p_b_fg').color } property QtObject send: QtObject { - property int margins: 6 - property int iconSize: 40 + property int margins: 5 + property int iconSize: 30 property string name : 'send' property string icon : 'send_custom' - property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color - property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color - property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color - property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } diff --git a/linphone-app/ui/modules/Common/Styles/Indicators/MediaProgressBarStyle.qml b/linphone-app/ui/modules/Common/Styles/Indicators/MediaProgressBarStyle.qml index 6ebbb6af1..9417b2d68 100644 --- a/linphone-app/ui/modules/Common/Styles/Indicators/MediaProgressBarStyle.qml +++ b/linphone-app/ui/modules/Common/Styles/Indicators/MediaProgressBarStyle.qml @@ -12,9 +12,9 @@ QtObject { property string gaugeIcon: 'chat_audio_soundwave_custom' property QtObject progressionWave: QtObject{ - property int iconSize: 30 - property int iconHeight: 40 - property int iconWidth: 250 + property int iconSize: 60 + property int iconHeight: 60 + property int iconWidth: 60 property string name : 'progression_soundwave' property string icon : 'chat_audio_soundwave_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'a_n_b_bg').color diff --git a/linphone-app/ui/modules/Common/View/ScrollableListView.qml b/linphone-app/ui/modules/Common/View/ScrollableListView.qml index 33faf5f0a..1825036b1 100644 --- a/linphone-app/ui/modules/Common/View/ScrollableListView.qml +++ b/linphone-app/ui/modules/Common/View/ScrollableListView.qml @@ -7,6 +7,9 @@ import Common 1.0 ListView { id: view + property bool hideScrollBars: false + property alias verticalScrollPolicy : vScrollBar.policy + property alias horizontalScrollPolicy : hScrollBar.policy function getVisibleIndex(checkMax) { var center_x = view.x + view.width / 2 @@ -35,21 +38,55 @@ ListView { ScrollBar.vertical: ForceScrollBar { id: vScrollBar + onPressedChanged: pressed ? view.movementStarted() : view.movementEnded() + // ScrollBar.AsNeeded doesn't work. Do it ourself. + policy: ScrollBar.AlwaysOff + function updatePolicy(){ + policy = (view.orientation == Qt.Vertical && view.contentHeight > view.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff) + } + Timer{// Delay to avoid binding loops + id:delayUpdateVPolicy + interval:10 + onTriggered: vScrollBar.updatePolicy() + } + Component.onCompleted: if(!hideScrollBars) updatePolicy() + } + ScrollBar.horizontal: ForceScrollBar { + id: hScrollBar onPressedChanged: pressed ? view.movementStarted() : view.movementEnded() // ScrollBar.AsNeeded doesn't work. Do it ourself. - policy: (view.contentHeight > view.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff) + policy: ScrollBar.AlwaysOff + function updatePolicy() { + policy = (view.orientation == Qt.Horizontal && view.contentWidth > view.width? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff) + } + Timer{// Delay to avoid binding loops + id:delayUpdateHPolicy + interval:10 + onTriggered: hScrollBar.updatePolicy() + } + Component.onCompleted: if(!hideScrollBars) updatePolicy() } // --------------------------------------------------------------------------- boundsMovement: Flickable.StopAtBounds boundsBehavior: Flickable.DragOverBounds clip: true contentWidth: width - (vScrollBar.visible?vScrollBar.width:0) + contentHeight: height - (hScrollBar.visible?hScrollBar.height:0) spacing: 0 synchronousDrag: true - onContentHeightChanged: cacheBuffer=view.contentHeight - cacheBuffer: height - + onContentHeightChanged: { + cacheBuffer= (view.contentHeight > 0 ? view.contentHeight : 0) + if(!hideScrollBars) + delayUpdateVPolicy.restart() + } + onHeightChanged: { + if(!hideScrollBars) + delayUpdateVPolicy.restart() + } + onContentWidthChanged: if(!hideScrollBars) delayUpdateHPolicy.restart() + onWidthChanged: if(!hideScrollBars) delayUpdateHPolicy.restart() + cacheBuffer: height > 0 ? height : 0 // --------------------------------------------------------------------------- // TODO: Find a solution at this bug => diff --git a/linphone-app/ui/modules/Linphone/Chat/Chat.js b/linphone-app/ui/modules/Linphone/Chat/Chat.js index b5070780e..15075bc2c 100644 --- a/linphone-app/ui/modules/Linphone/Chat/Chat.js +++ b/linphone-app/ui/modules/Linphone/Chat/Chat.js @@ -54,7 +54,7 @@ function getComponentFromEntry (chatEntry) { function handleFilesDropped (files) { chat.bindToEnd = true - files.forEach(container.proxyModel.sendFileMessage) + files.forEach(chatMessagePreview.addFile) } function handleMoreEntriesLoaded (n) { diff --git a/linphone-app/ui/modules/Linphone/Chat/Chat.qml b/linphone-app/ui/modules/Linphone/Chat/Chat.qml index a9c97f39a..ded7b7121 100644 --- a/linphone-app/ui/modules/Linphone/Chat/Chat.qml +++ b/linphone-app/ui/modules/Linphone/Chat/Chat.qml @@ -7,6 +7,7 @@ import Linphone 1.0 import Linphone.Styles 1.0 import Utils 1.0 import UtilsCpp 1.0 +import LinphoneEnums 1.0 import Units 1.0 @@ -46,7 +47,6 @@ Rectangle { ScrollableListView { id: chat - // ----------------------------------------------------------------------- property bool bindToEnd: false property bool displaying: false @@ -371,128 +371,140 @@ Rectangle { } - ChatMessagePreview{ - id: chatMessagePreview - Layout.fillWidth: true - - replyChatRoomModel: proxyModel.chatRoomModel - - } - Rectangle{ - id: messageBlock - onHeightChanged: height = Layout.preferredHeight - Layout.preferredHeight: visible && opacity > 0 ? 32 : 0 - Layout.fillWidth: true - Layout.leftMargin: ChatStyle.entry.leftMargin - Layout.rightMargin: ChatStyle.entry.rightMargin - color: ChatStyle.messageBanner.color - radius: 10 - state: "hidden" - Timer{ - id: hideNoticeBanner - interval: 4000 - repeat: false - onTriggered: messageBlock.state = "hidden" - } - RowLayout{ - anchors.centerIn: parent - spacing: 5 - Icon{ - icon: ChatStyle.copyTextIcon - overwriteColor: ChatStyle.messageBanner.textColor - iconSize: 20 - } - Text{ - Layout.fillHeight: true - Layout.fillWidth: true - text: container.noticeBannerText - font { - pointSize: ChatStyle.messageBanner.pointSize - } - color: ChatStyle.messageBanner.textColor - } - } - states: [ - State { - name: "hidden" - PropertyChanges { target: messageBlock; opacity: 0 } - }, - State { - name: "showed" - PropertyChanges { target: messageBlock; opacity: 1 } - } - ] - transitions: [ - Transition { - from: "*"; to: "showed" - SequentialAnimation{ - NumberAnimation{ properties: "opacity"; easing.type: Easing.OutBounce; duration: 500 } - ScriptAction{ script: hideNoticeBanner.start()} - } - }, - Transition { - SequentialAnimation{ - NumberAnimation{ properties: "opacity"; duration: 1000 } - ScriptAction{ script: container.noticeBannerText = '' } - } - } - ] - } - // ------------------------------------------------------------------------- - // Send area. - // ------------------------------------------------------------------------- - - Borders { - id: textAreaBorders + Rectangle { + id: bottomChatBackground Layout.fillWidth: true - Layout.preferredHeight: textArea.height - - borderColor: ChatStyle.sendArea.border.color - topWidth: ChatStyle.sendArea.border.width - visible: proxyModel.chatRoomModel && !proxyModel.chatRoomModel.hasBeenLeft && (!proxyModel.chatRoomModel.haveEncryption && SettingsModel.standardChatEnabled || proxyModel.chatRoomModel.haveEncryption && SettingsModel.secureChatEnabled) - - - DroppableTextArea { - id: textArea - - enabled:proxyModel && proxyModel.chatRoomModel ? !proxyModel.chatRoomModel.hasBeenLeft:false - isEphemeral : proxyModel && proxyModel.chatRoomModel ? proxyModel.chatRoomModel.ephemeralEnabled:false - - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - - height:ChatStyle.sendArea.height + ChatStyle.sendArea.border.width - minimumHeight:ChatStyle.sendArea.height + ChatStyle.sendArea.border.width - maximumHeight:container.height/2 - - dropEnabled: SettingsModel.fileTransferUrl.length > 0 - dropDisabledReason: qsTr('noFileTransferUrl') - placeholderText: qsTr('newMessagePlaceholder') - - onDropped: Logic.handleFilesDropped(files) - onTextChanged: Logic.handleTextChanged(text) - onValidText: { - textArea.text = '' - chat.bindToEnd = true - if(proxyModel.chatRoomModel) { - proxyModel.sendMessage(text) - }else{ - console.log("Peer : " +proxyModel.peerAddress+ "/"+chat.model.peerAddress) - proxyModel.chatRoomModel = CallsListModel.createChat(proxyModel.peerAddress) - proxyModel.sendMessage(text) - } - } - onAudioRecordRequest: RecorderManager.resetVocalRecorder() - Component.onCompleted: {text = proxyModel.cachedText; cursorPosition=text.length} + Layout.preferredHeight: textAreaBorders.height + chatMessagePreview.height+messageBlock.height + color: ChatStyle.sendArea.backgroundBorder.color + clip: true + ColumnLayout{ + anchors.fill: parent + spacing: 0 Rectangle{ - anchors.fill:parent - color:'white' - opacity: 0.5 - visible:!textArea.enabled + id: messageBlock + onHeightChanged: height = Layout.preferredHeight + Layout.preferredHeight: visible && opacity > 0 ? 32 : 0 + Layout.fillWidth: true + Layout.leftMargin: ChatStyle.entry.leftMargin + Layout.rightMargin: ChatStyle.entry.rightMargin + color: ChatStyle.messageBanner.color + radius: 10 + state: "hidden" + Timer{ + id: hideNoticeBanner + interval: 4000 + repeat: false + onTriggered: messageBlock.state = "hidden" + } + RowLayout{ + anchors.centerIn: parent + spacing: 5 + Icon{ + icon: ChatStyle.copyTextIcon + overwriteColor: ChatStyle.messageBanner.textColor + iconSize: 20 + } + Text{ + Layout.fillHeight: true + Layout.fillWidth: true + text: container.noticeBannerText + font { + pointSize: ChatStyle.messageBanner.pointSize + } + color: ChatStyle.messageBanner.textColor + } + } + states: [ + State { + name: "hidden" + PropertyChanges { target: messageBlock; opacity: 0 } + }, + State { + name: "showed" + PropertyChanges { target: messageBlock; opacity: 1 } + } + ] + transitions: [ + Transition { + from: "*"; to: "showed" + SequentialAnimation{ + NumberAnimation{ properties: "opacity"; easing.type: Easing.OutBounce; duration: 500 } + ScriptAction{ script: hideNoticeBanner.start()} + } + }, + Transition { + SequentialAnimation{ + NumberAnimation{ properties: "opacity"; duration: 1000 } + ScriptAction{ script: container.noticeBannerText = '' } + } + } + ] + }// MessageBlock + ChatMessagePreview{ + id: chatMessagePreview + Layout.fillWidth: true + Layout.leftMargin: ChatStyle.sendArea.backgroundBorder.width + maxHeight: container.height - textAreaBorders.height + replyChatRoomModel: proxyModel.chatRoomModel + } - } - } + // ------------------------------------------------------------------------- + // Send area. + // ------------------------------------------------------------------------- + + Borders { + id: textAreaBorders + Layout.fillWidth: true + Layout.preferredHeight: textArea.height + Layout.leftMargin: ChatStyle.sendArea.backgroundBorder.width + borderColor: ChatStyle.sendArea.border.color + topWidth: ChatStyle.sendArea.border.width + visible: proxyModel.chatRoomModel && !proxyModel.chatRoomModel.hasBeenLeft && (!proxyModel.chatRoomModel.haveEncryption && SettingsModel.standardChatEnabled || proxyModel.chatRoomModel.haveEncryption && SettingsModel.secureChatEnabled) + + DroppableTextArea { + id: textArea + + enabled:proxyModel && proxyModel.chatRoomModel ? !proxyModel.chatRoomModel.hasBeenLeft:false + isEphemeral : proxyModel && proxyModel.chatRoomModel ? proxyModel.chatRoomModel.ephemeralEnabled:false + + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + height:ChatStyle.sendArea.height + ChatStyle.sendArea.border.width + minimumHeight:ChatStyle.sendArea.height + ChatStyle.sendArea.border.width + maximumHeight:container.height/2 + + dropEnabled: SettingsModel.fileTransferUrl.length > 0 + dropDisabledReason: qsTr('noFileTransferUrl') + placeholderText: qsTr('newMessagePlaceholder') + recordAudioToggled: RecorderManager.haveVocalRecorder && RecorderManager.getVocalRecorder().state != LinphoneEnums.RecorderStateClosed + + onDropped: Logic.handleFilesDropped(files) + onTextChanged: Logic.handleTextChanged(text) + onValidText: { + textArea.text = '' + chat.bindToEnd = true + if(proxyModel.chatRoomModel) { + proxyModel.sendMessage(text) + }else{ + console.log("Peer : " +proxyModel.peerAddress+ "/"+chat.model.peerAddress) + proxyModel.chatRoomModel = CallsListModel.createChat(proxyModel.peerAddress) + proxyModel.sendMessage(text) + } + } + onAudioRecordRequest: RecorderManager.resetVocalRecorder() + Component.onCompleted: {text = proxyModel.cachedText; cursorPosition=text.length} + Rectangle{ + anchors.fill:parent + color:'white' + opacity: 0.5 + visible:!textArea.enabled + } + } + }// Send Area + }// ColumnLayout + }// Bottom background } diff --git a/linphone-app/ui/modules/Linphone/Chat/ChatAudioMessage.qml b/linphone-app/ui/modules/Linphone/Chat/ChatAudioMessage.qml index b304b6690..05fd3d44c 100644 --- a/linphone-app/ui/modules/Linphone/Chat/ChatAudioMessage.qml +++ b/linphone-app/ui/modules/Linphone/Chat/ChatAudioMessage.qml @@ -26,12 +26,14 @@ Loader{ property ContentModel contentModel property int maxWidth : parent.width property int fitWidth: active ? Math.max(maxWidth - ChatAudioMessageStyle.emptySpace, ChatAudioMessageStyle.minWidth) : 0 - property int fitHeight: active ? 40 : 0 + property int fitHeight: active ? 60 : 0 property font customFont : SettingsModel.textMessageFont - property bool isOutgoing : contentModel && (contentModel.chatMessageModel.isOutgoing || contentModel.chatMessageModel.state == LinphoneEnums.ChatMessageStateIdle); + property bool isOutgoing : contentModel && contentModel.chatMessageModel && (contentModel.chatMessageModel.isOutgoing || contentModel.chatMessageModel.state == LinphoneEnums.ChatMessageStateIdle); + property bool isActive: active active: contentModel && contentModel.isVoiceRecording() + sourceComponent: Item{ id: loadedItem property bool isPlaying : vocalPlayer.item && vocalPlayer.item.playbackState === SoundPlayer.PlayingState @@ -70,6 +72,7 @@ Loader{ Layout.leftMargin: 15 Layout.alignment: Qt.AlignVCenter isCustom: true + backgroundRadius: width colorSet: (loadedItem.isPlaying ? ChatAudioMessageStyle.pauseAction : ChatAudioMessageStyle.playAction) onClicked:{ @@ -84,7 +87,9 @@ Loader{ Layout.fillHeight: true Layout.fillWidth: true Layout.alignment: Qt.AlignVCenter - Layout.rightMargin: 15 + Layout.rightMargin: 10 + Layout.topMargin: 10 + Layout.bottomMargin: 10 MediaProgressBar{ id: mediaProgressBar anchors.fill: parent diff --git a/linphone-app/ui/modules/Linphone/Chat/ChatAudioPreview.qml b/linphone-app/ui/modules/Linphone/Chat/ChatAudioPreview.qml index 74ad98f4e..bc57cf5f8 100644 --- a/linphone-app/ui/modules/Linphone/Chat/ChatAudioPreview.qml +++ b/linphone-app/ui/modules/Linphone/Chat/ChatAudioPreview.qml @@ -28,7 +28,7 @@ Rectangle{ mediaProgressBar.stop() onIsPlayingChanged: isPlaying ? mediaProgressBar.resume() : mediaProgressBar.stop() - Layout.preferredHeight: visible ? 70 : 0 + Layout.preferredHeight: visible ? ChatAudioPreviewStyle.height : 0 color: ChatAudioPreviewStyle.backgroundColor radius: 0 @@ -54,17 +54,19 @@ Rectangle{ RowLayout{ id: lineLayout anchors.fill: parent - spacing: 10 + spacing: 0 ActionButton{ Layout.preferredHeight: iconSize Layout.preferredWidth: iconSize - Layout.leftMargin: 10 + Layout.leftMargin: 6 Layout.alignment: Qt.AlignVCenter isCustom: true colorSet: ChatAudioPreviewStyle.deleteAction onClicked: RecorderManager.clearVocalRecorder() } VuMeter { + Layout.leftMargin: 6 + Layout.rightMargin: 6 Timer { interval: 50 repeat: true @@ -78,17 +80,22 @@ Rectangle{ Layout.fillHeight: true Layout.fillWidth: true Layout.alignment: Qt.AlignVCenter - Layout.topMargin: 5 - Layout.bottomMargin: 5 + Layout.topMargin: 10 + Layout.bottomMargin: 10 + Layout.leftMargin: 6 MediaProgressBar{ id: mediaProgressBar anchors.fill: parent + waveLeftMargin: !vocalPlayer.item && vocalRecorder ? 10 : 0 progressDuration: !vocalPlayer.item && vocalRecorder? vocalRecorder.getDuration() : 0 progressPosition: !vocalPlayer.item ? progressDuration : 0 value: !vocalPlayer.item ? 0.01 * progressDuration / 5 : 100 stopAtEnd: !audioPreviewBlock.isRecording resetAtEnd: false colorSet: isRecording ? ChatAudioPreviewStyle.recordingProgressionWave : ChatAudioPreviewStyle.progressionWave + function progressComputation(t) { + return 1 * Math.sqrt(1 - (t=t/1-1)*t); + } function refresh(){ if( vocalPlayer.item){ progressPosition = vocalPlayer.item.getPosition() @@ -96,7 +103,10 @@ Rectangle{ }else{// Recording progressDuration = vocalRecorder.getDuration() progressPosition = progressDuration - value = value + 0.01 + if( value == 0) + value = 1 + else + value = value + Math.pow(value,-0.7) } } onEndReached:{ @@ -114,8 +124,8 @@ Rectangle{ ActionButton{ Layout.preferredHeight: iconSize Layout.preferredWidth: iconSize - Layout.rightMargin: 15 - Layout.leftMargin: 5 + Layout.rightMargin: ChatStyle.rightButtonMargin + Layout.leftMargin: ChatStyle.rightButtonLMargin Layout.alignment: Qt.AlignVCenter isCustom: true colorSet: audioPreviewBlock.isRecording ? ChatAudioPreviewStyle.stopAction @@ -124,7 +134,7 @@ Rectangle{ onClicked:{ if(audioPreviewBlock.isRecording){// Stop the record and save the file audioPreviewBlock.vocalRecorder.stop() - //mediaProgressBar.value = 100 + mediaProgressBar.value = 0 }else if(audioPreviewBlock.isPlaying){// Pause the play vocalPlayer.item.pause() }else{// Play the audio diff --git a/linphone-app/ui/modules/Linphone/Chat/ChatContent.qml b/linphone-app/ui/modules/Linphone/Chat/ChatContent.qml index 7a8b90cb6..5b1ba13ff 100644 --- a/linphone-app/ui/modules/Linphone/Chat/ChatContent.qml +++ b/linphone-app/ui/modules/Linphone/Chat/ChatContent.qml @@ -35,7 +35,7 @@ Column{ spacing: 0 - property bool isOutgoing : contentModel && (contentModel.chatMessageModel.isOutgoing || contentModel.chatMessageModel.state == LinphoneEnums.ChatMessageStateIdle); + property bool isOutgoing : contentModel && contentModel.chatMessageModel && (contentModel.chatMessageModel.isOutgoing || contentModel.chatMessageModel.state == LinphoneEnums.ChatMessageStateIdle); ChatAudioMessage{ id: audioMessage diff --git a/linphone-app/ui/modules/Linphone/Chat/ChatFileMessage.qml b/linphone-app/ui/modules/Linphone/Chat/ChatFileMessage.qml index 54e77b09c..7b5c0fc5c 100644 --- a/linphone-app/ui/modules/Linphone/Chat/ChatFileMessage.qml +++ b/linphone-app/ui/modules/Linphone/Chat/ChatFileMessage.qml @@ -18,7 +18,7 @@ Row { property ChatMessageModel chatMessageModel: contentModel && contentModel.chatMessageModel property ContentModel contentModel - property bool isOutgoing : contentModel && ( chatMessageModel.isOutgoing || chatMessageModel.state == LinphoneEnums.ChatMessageStateIdle); + property bool isOutgoing : chatMessageModel && ( chatMessageModel.isOutgoing || chatMessageModel.state == LinphoneEnums.ChatMessageStateIdle); property int fitWidth: visible ? Math.max(fileName.implicitWidth + 5 + 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 diff --git a/linphone-app/ui/modules/Linphone/Chat/ChatFilePreview.qml b/linphone-app/ui/modules/Linphone/Chat/ChatFilePreview.qml new file mode 100644 index 000000000..bba5b330e --- /dev/null +++ b/linphone-app/ui/modules/Linphone/Chat/ChatFilePreview.qml @@ -0,0 +1,86 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 + +import Common 1.0 +import Linphone 1.0 +import Linphone.Styles 1.0 +import Utils 1.0 +import UtilsCpp 1.0 + +import Units 1.0 + +import 'Chat.js' as Logic + +// ============================================================================= +Item{ + visible: mainListView.count > 0 + Layout.preferredHeight: visible ? ChatFilePreviewStyle.height : 0 + + function addFile(path){ + contents.addFile(path) + } + + ScrollableListView{ + id: mainListView + + spacing: ChatFilePreviewStyle.filePreview.closeButton.iconSize + anchors.fill: parent + anchors.rightMargin: ChatStyle.rightButtonMargin + ChatStyle.rightButtonLMargin + ChatStyle.rightButtonSize + orientation: Qt.Horizontal + model: ContentProxyModel{ + id: contents + } + header:Component{ + Item{ + width: ChatFilePreviewStyle.filePreview.closeButton.iconSize/2 + height:mainListView.height + } + } + footer: Component{ + Item{ + width: ChatFilePreviewStyle.filePreview.closeButton.iconSize + height:mainListView.height + } + } + delegate: + FileView{ + height:mainListView.height-ChatFilePreviewStyle.filePreview.heightMargins + width: height * ChatFilePreviewStyle.filePreview.format + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 7 + //anchors.horizontalCenter: parent.horizontalCenter + thumbnail: modelData.thumbnail + name: modelData.name + animationScale: 1.1 + onClickOnFile: { + modelData.openFile() + } + ActionButton{ + anchors.bottom: parent.top + anchors.bottomMargin: -height/2 + anchors.left: parent.right + anchors.leftMargin: -width/2 + isCustom: true + backgroundRadius: width + colorSet: ChatFilePreviewStyle.filePreview.removeButton + z: parent.z+1 + onClicked:{ + contents.remove(modelData) + } + } + } + } + ActionButton{ + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: ChatStyle.rightButtonMargin + isCustom: true + backgroundRadius: width + colorSet: ChatFilePreviewStyle.filePreview.closeButton + z: parent.z+1 + onClicked:{ + contents.clear() + } + } +} \ No newline at end of file diff --git a/linphone-app/ui/modules/Linphone/Chat/ChatMessagePreview.qml b/linphone-app/ui/modules/Linphone/Chat/ChatMessagePreview.qml index d4e3ac716..46e9c1d13 100644 --- a/linphone-app/ui/modules/Linphone/Chat/ChatMessagePreview.qml +++ b/linphone-app/ui/modules/Linphone/Chat/ChatMessagePreview.qml @@ -15,18 +15,48 @@ import 'Chat.js' as Logic // ============================================================================= ColumnLayout{ property alias replyChatRoomModel : replyPreview.chatRoomModel - property int maxHeight: parent.height - ( audioPreview.visible ? audioPreview.height : 0) + property int maxHeight: parent.height + property int fitHeight: (replyPreview.visible ? replyPreview.height + replySeparator.height: 0 ) + + (audioPreview.visible ? audioPreview.height + audioSeparator.height: 0) + + (filesPreview.visible ? filesPreview.height + filesSeparator.height: 0) spacing: 0 - Layout.preferredHeight: (replyPreview.visible ? replyPreview.height : 0 ) + (audioPreview.visible ? audioPreview.height : 0) - Layout.maximumHeight: Layout.preferredHeight + Layout.preferredHeight: fitHeight + Layout.maximumHeight: fitHeight> maxHeight ? maxHeight : fitHeight // ?? just using maxHeight doesn't work. function hide(){ } + function addFile(path){ + filesPreview.addFile(path) + } ChatReplyPreview{ id: replyPreview Layout.fillWidth: true + maxHeight: parent.maxHeight - (audioPreview.visible ? audioPreview.height + audioSeparator.height: 0) + - (filesPreview.visible ? filesPreview.height + filesSeparator.height: 0) + } + Item{ + id: replySeparator + visible: replyPreview.visible + Layout.preferredHeight: visible ? ChatStyle.separatorHeight : 0 + Layout.fillWidth: true } ChatAudioPreview{ id: audioPreview Layout.fillWidth: true } + Item{ + id: audioSeparator + visible: audioPreview.visible + Layout.preferredHeight: visible ? ChatStyle.separatorHeight : 0 + Layout.fillWidth: true + } + ChatFilePreview{ + id: filesPreview + Layout.fillWidth: true + } + Item{ + id: filesSeparator + visible: filesPreview.visible + Layout.preferredHeight: visible ? ChatStyle.separatorHeight : 0 + Layout.fillWidth: true + } } \ No newline at end of file diff --git a/linphone-app/ui/modules/Linphone/Chat/ChatReplyMessage.qml b/linphone-app/ui/modules/Linphone/Chat/ChatReplyMessage.qml index 02577f5a9..5768e5988 100644 --- a/linphone-app/ui/modules/Linphone/Chat/ChatReplyMessage.qml +++ b/linphone-app/ui/modules/Linphone/Chat/ChatReplyMessage.qml @@ -108,9 +108,10 @@ Item { color: ChatReplyMessageStyle.replyArea.foregroundColor } - ListView { + ScrollableListView { id: replyMessage property int fitWidth : 0 + hideScrollBars: true anchors.top: usernameReplied.bottom anchors.left: parent.left anchors.right: parent.right @@ -130,7 +131,13 @@ Item { model: ContentProxyModel{ chatMessageModel: mainItem.chatMessageModel } - height: contentHeight + Timer{// Delay to avoid binding loops + id:delayUpdate + interval:10 + onTriggered: replyMessage.height = replyMessage.contentHeight + } + onContentHeightChanged: delayUpdate.restart() + //height: contentHeight delegate: ChatContent{ contentModel: modelData @@ -140,6 +147,13 @@ Item { onFitWidthChanged:{ replyMessage.updateWidth() } + Rectangle{ + anchors.left: parent.left + anchors.right: parent.right + color: ChatStyle.entry.separator.color + height: visible ? ChatStyle.entry.separator.width : 0 + visible: (index !== (replyMessage.count - 1)) + } } } } diff --git a/linphone-app/ui/modules/Linphone/Chat/ChatReplyPreview.qml b/linphone-app/ui/modules/Linphone/Chat/ChatReplyPreview.qml index 6449e509d..98f39aac1 100644 --- a/linphone-app/ui/modules/Linphone/Chat/ChatReplyPreview.qml +++ b/linphone-app/ui/modules/Linphone/Chat/ChatReplyPreview.qml @@ -17,9 +17,8 @@ import 'Chat.js' as Logic Rectangle{ id: replyPreviewBlock property ChatRoomModel chatRoomModel - - Layout.preferredHeight: visible ? Math.min(messageContentsList.height + replyPreviewHeaderArea.implicitHeight + 15, parent.maxHeight) : 0 - + property int maxHeight : parent.maxHeight + Layout.preferredHeight: visible ? Math.min(messageContentsList.height + replyPreviewHeaderArea.implicitHeight + 15, replyPreviewBlock.maxHeight) : 0 property int leftMargin: textArea.textLeftMargin property int rightMargin: textArea.textRightMargin @@ -70,9 +69,10 @@ Rectangle{ color: ChatStyle.replyPreview.headerTextColor } } + Flickable { id: replyPreviewTextArea - ScrollBar.vertical: ForceScrollBar {visible: replyPreviewTextArea.height < messageContentsList.implicitHeight} + ScrollBar.vertical: ForceScrollBar {visible: replyPreviewTextArea.height < messageContentsList.height} boundsBehavior: Flickable.StopAtBounds clip: true contentHeight: messageContentsList.height @@ -93,13 +93,20 @@ Rectangle{ delegate: ChatContent{ contentModel: modelData textFont.pointSize: Units.dp * (SettingsModel.textMessageFont.pointSize - 2) + Rectangle{ + anchors.left: parent.left + anchors.right: parent.right + color: ChatStyle.entry.separator.color + height: visible ? ChatStyle.entry.separator.width : 0 + visible: (index !== (messageContentsList.count - 1)) + } } } } } ActionButton{ anchors.right:parent.right - anchors.rightMargin: 14 + anchors.rightMargin: ChatStyle.rightButtonMargin anchors.verticalCenter: parent.verticalCenter height: ChatStyle.replyPreview.closeButton.iconSize isCustom: true diff --git a/linphone-app/ui/modules/Linphone/Chat/ChatTextMessage.qml b/linphone-app/ui/modules/Linphone/Chat/ChatTextMessage.qml index d11fdd34e..0d66906e0 100644 --- a/linphone-app/ui/modules/Linphone/Chat/ChatTextMessage.qml +++ b/linphone-app/ui/modules/Linphone/Chat/ChatTextMessage.qml @@ -23,7 +23,7 @@ TextEdit { property ContentModel contentModel property string lastTextSelected : '' property font customFont : SettingsModel.textMessageFont - property int fitHeight: visible ? contentHeight + padding + 6 : 0 + property int fitHeight: visible ? contentHeight + padding + 8 : 0 property int fitWidth: visible ? implicitWidth + 2: 0 // add 2 because there is a bug on border that lead to not fit text exactly signal rightClicked() diff --git a/linphone-app/ui/modules/Linphone/Chat/Message.qml b/linphone-app/ui/modules/Linphone/Chat/Message.qml index 522f35e28..96bbe341c 100644 --- a/linphone-app/ui/modules/Linphone/Chat/Message.qml +++ b/linphone-app/ui/modules/Linphone/Chat/Message.qml @@ -90,11 +90,11 @@ Item { } onGoToMessage: container.goToMessage(message) } - ListView { id: messageContentsList anchors.left: parent.left anchors.right: parent.right + visible: count > 0 spacing: 0 model: ContentProxyModel{ chatMessageModel: $chatEntry @@ -102,14 +102,22 @@ Item { height: contentHeight boundsBehavior: Flickable.StopAtBounds interactive: false - delegate: ChatContent{ - contentModel: modelData - onFitWidthChanged:{ - rectangle.updateWidth() + delegate: + ChatContent{ + contentModel: modelData + onFitWidthChanged:{ + rectangle.updateWidth() + } + onLastTextSelectedChanged: container.lastTextSelected= lastTextSelected + onRightClicked: chatMenu.open() + Rectangle{ + anchors.left: parent.left + anchors.right: parent.right + color: ChatStyle.entry.separator.color + height: visible ? ChatStyle.entry.separator.width : 0 + visible: (index !== (messageContentsList.count - 1)) + } } - onLastTextSelectedChanged: container.lastTextSelected= lastTextSelected - onRightClicked: chatMenu.open() - } } } Row{ diff --git a/linphone-app/ui/modules/Linphone/File/FileView.qml b/linphone-app/ui/modules/Linphone/File/FileView.qml new file mode 100644 index 000000000..4a5e5607a --- /dev/null +++ b/linphone-app/ui/modules/Linphone/File/FileView.qml @@ -0,0 +1,135 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 + +import Common 1.0 +import Linphone 1.0 +import LinphoneUtils 1.0 +import LinphoneEnums 1.0 +import Linphone.Styles 1.0 +import Utils 1.0 +import Units 1.0 +import ColorsList 1.0 + +// ============================================================================= + + +Item { + id: mainItem + property string thumbnail + property string name + property bool active: true + property real animationScale : ChatStyle.entry.message.file.animation.to + property alias imageScale: thumbnailProvider.scale + + signal clickOnFile() + // --------------------------------------------------------------------- + // Thumbnail or extension. + // --------------------------------------------------------------------- + + Component { + id: thumbnailImage + + Image { + id: thumbnailImageSource + mipmap: SettingsModel.mipmapEnabled + source: mainItem.thumbnail + fillMode: Image.PreserveAspectFit + } + } + + Component { + id: extension + + Rectangle { + color: ChatStyle.entry.message.file.extension.background.color + + Text { + anchors.fill: parent + + color: ChatStyle.entry.message.file.extension.text.color + font.bold: true + elide: Text.ElideRight + text: Utils.getExtension(mainItem.name).toUpperCase() + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + } + Loader { + id: thumbnailProvider + + anchors.fill: parent + //Layout.fillHeight: true + //Layout.preferredWidth: parent.height + + 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 + } + + states: State { + name: 'hovered' + } + + transitions: [ + Transition { + from: '' + to: 'hovered' + + ScriptAction { + script: { + if (thumbnailProviderAnimator.running) { + thumbnailProviderAnimator.running = false + } + + mainItem.z = 999//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' + : '' + } + + anchors.fill: parent + + onClicked: { + clickOnFile() + thumbnailProvider.state = '' + } + 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/ChatAudioMessageStyle.qml b/linphone-app/ui/modules/Linphone/Styles/Chat/ChatAudioMessageStyle.qml index a2d7eac2e..585733b78 100644 --- a/linphone-app/ui/modules/Linphone/Styles/Chat/ChatAudioMessageStyle.qml +++ b/linphone-app/ui/modules/Linphone/Styles/Chat/ChatAudioMessageStyle.qml @@ -8,54 +8,54 @@ import ColorsList 1.0 QtObject { property string sectionName : 'ChatAudioMessage' - property int minWidth: 400 - property int emptySpace: 100 + property int minWidth: 500 + property int emptySpace: 10 property color color: ColorsList.add(sectionName, 'q').color property color backgroundColor: ColorsList.add(sectionName+'_bg', 'a').color property QtObject pauseAction: QtObject { - property int iconSize: 30 + property int iconSize: 25 property string name : 'pause' property string icon : 'chat_audio_pause_custom' - property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color - property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color - property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color - property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color - property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'q').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'q').color + property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'q').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject playAction: QtObject { - property int iconSize: 30 + property int iconSize: 25 property string name : 'play' property string icon : 'chat_audio_play_custom' - property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color - property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color - property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color - property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color - property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'q').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'q').color + property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'q').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject progressionWave: QtObject{ - property int iconSize: 30 - property int iconHeight: 40 - property int iconWidth: 250 + property int iconSize: 60 + property int iconHeight: 60 + property int iconWidth: 60 property string name : 'progression_soundwave' property string icon : 'chat_audio_soundwave_custom' - property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'a_n_b_bg').color - property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'a_h_b_bg').color - property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'a_p_b_bg').color - property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'a_n_b_fg').color - property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'a_h_b_fg').color - property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'a_p_b_fg').color + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'w_n_b_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'w_h_b_bg').color + property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'w_p_b_bg').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'w_n_b_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'w_h_b_fg').color + property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'w_p_b_fg').color - property color backgroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_n', icon, 'l_n_b_bg').color - property color backgroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_h', icon, 'l_h_b_bg').color + property color backgroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_n', icon, 'l_h_b_bg').color + property color backgroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_h', icon, 'l_n_b_bg').color property color backgroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_p', icon, 'l_p_b_bg').color - property color foregroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_n', icon, 'l_n_b_fg').color - property color foregroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_h', icon, 'l_h_b_fg').color + property color foregroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_n', icon, 'l_h_b_fg').color + property color foregroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_h', icon, 'l_n_b_fg').color property color foregroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_p', icon, 'l_p_b_fg').color } diff --git a/linphone-app/ui/modules/Linphone/Styles/Chat/ChatAudioPreviewStyle.qml b/linphone-app/ui/modules/Linphone/Styles/Chat/ChatAudioPreviewStyle.qml index f27727b2b..e5eef4945 100644 --- a/linphone-app/ui/modules/Linphone/Styles/Chat/ChatAudioPreviewStyle.qml +++ b/linphone-app/ui/modules/Linphone/Styles/Chat/ChatAudioPreviewStyle.qml @@ -9,6 +9,8 @@ import ColorsList 1.0 QtObject { property string sectionName : 'ChatAudioPreview' property color color: ColorsList.add(sectionName, 'q').color + property int height: 70 + property QtObject header: QtObject{ property color color: ColorsList.add(sectionName+'_header', 'h').color property int pointSizeOffset: -3 @@ -31,51 +33,51 @@ QtObject { property int iconSize: 40 property string name : 'delete' property string icon : 'delete_custom' - property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color - property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color - property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color - property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject stopAction: QtObject { property int iconSize: 30 property string name : 'stop' - property string icon : 'chat_audio_stop_custom' - property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color - property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color + property string icon : 'chat_audio_preview_stop_custom' + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color - property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color - property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject pauseAction: QtObject { property int iconSize: 30 property string name : 'pause' - property string icon : 'chat_audio_pause_custom' - property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color - property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color + property string icon : 'chat_audio_preview_pause_custom' + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color - property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color - property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject playAction: QtObject { property int iconSize: 30 property string name : 'play' - property string icon : 'chat_audio_play_custom' - property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color - property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color + property string icon : 'chat_audio_preview_play_custom' + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color - property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color - property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject progressionWave: QtObject{ - property int iconSize: 30 - property int iconHeight: 40 - property int iconWidth: 250 + property int iconSize: 60 + property int iconHeight: 60 + property int iconWidth: 60 property string name : 'progression_soundwave' property string icon : 'chat_audio_soundwave_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'a_n_b_bg').color @@ -85,35 +87,37 @@ QtObject { property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'a_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'a_p_b_fg').color - property color backgroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_n', icon, 'l_n_b_bg').color - property color backgroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_h', icon, 'l_h_b_bg').color + property color backgroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_n', icon, 'l_h_b_bg').color + property color backgroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_h', icon, 'l_n_b_bg').color property color backgroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_p', icon, 'l_p_b_bg').color - property color foregroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_n', icon, 'l_n_b_fg').color - property color foregroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_h', icon, 'l_h_b_fg').color + property color foregroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_n', icon, 'l_h_b_fg').color + property color foregroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_h', icon, 'l_n_b_fg').color property color foregroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_p', icon, 'l_p_b_fg').color } property QtObject recordingProgressionWave: QtObject{ - property int iconSize: 30 - property int iconHeight: 40 - property int iconWidth: 250 + property int iconSize: 60 + property int iconHeight: 60 + property int iconWidth: 60 property string name : 'recording_progression_soundwave' property string icon : 'chat_audio_soundwave_custom' - property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'r_n_b_bg').color - property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'r_h_b_bg').color - property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'r_p_b_bg').color - property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'r_n_b_fg').color - property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'r_h_b_fg').color - property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'r_p_b_fg').color - property color backgroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_n', icon, 'l_n_b_bg').color - property color backgroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_h', icon, 'l_h_b_bg').color - property color backgroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_p', icon, 'l_p_b_bg').color + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color + property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_h_b_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_n_b_fg').color + property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color - property color foregroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_n', icon, 'l_n_b_fg').color - property color foregroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_h', icon, 'l_h_b_fg').color - property color foregroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_p', icon, 'l_p_b_fg').color + // Old color: l_n_b_bg + property color backgroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_n', icon, 'ai').color + property color backgroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_h', icon, 'ai').color + property color backgroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_p', icon, 'ai').color + + property color foregroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_n', icon, 'ai').color + property color foregroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_h', icon, 'ai').color + property color foregroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_p', icon, 'ai').color } property int padding: 8 diff --git a/linphone-app/ui/modules/Linphone/Styles/Chat/ChatFilePreviewStyle.qml b/linphone-app/ui/modules/Linphone/Styles/Chat/ChatFilePreviewStyle.qml new file mode 100644 index 000000000..52813ca85 --- /dev/null +++ b/linphone-app/ui/modules/Linphone/Styles/Chat/ChatFilePreviewStyle.qml @@ -0,0 +1,49 @@ +pragma Singleton +import QtQml 2.2 + +import Units 1.0 +import ColorsList 1.0 + +// ============================================================================= + +QtObject { + property string sectionName : 'ChatFilePreview' + property int height: 160 + + property QtObject filePreview: QtObject{ + id: filePreviewObject + property int heightMargins: 60 + property real format: 16/9 + + property string name: 'filePreview' + property string icon: 'menu_reply_custom' + property color backgroundColor: ColorsList.add(sectionName+'_'+name+'_bg', 'e').color + property color headerTextColor: ColorsList.add(sectionName+'_'+name+'_header_fg', 'i').color + property color iconColor: ColorsList.add(sectionName+'_'+name+'_header_fg', 'i').color + property color textColor: ColorsList.add(sectionName+'_'+name+'_fg', 'd').color + property int pointSize: Units.dp * 9 + property int headerPointSize: Units.dp * 9 + property QtObject removeButton: QtObject{ + property int iconSize: 30 + property string name : 'remove' + property string icon : 'close_custom' + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_b_n', icon, 's_n_b_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_b_h', icon, 's_h_b_bg').color + property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_b_p', icon, 's_p_b_bg').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_f_n', icon, 's_n_b_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_f_h', icon, 's_h_b_fg').color + property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_f_p', icon, 's_p_b_fg').color + } + property QtObject closeButton: QtObject{ + property int iconSize: 30 + property string name : 'close' + property string icon : 'close_custom' + property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_b_n', icon, 'l_n_b_bg').color + property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_b_h', icon, 'l_h_b_bg').color + property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_b_p', icon, 'l_p_b_bg').color + property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_f_n', icon, 'l_n_b_fg').color + property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_f_h', icon, 'l_h_b_fg').color + property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_f_p', icon, 'l_p_b_fg').color + } + } +} diff --git a/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml b/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml index 1523a51f5..dfacfa2a3 100644 --- a/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml +++ b/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml @@ -10,6 +10,10 @@ QtObject { property string sectionName : 'Chat' property color color: ColorsList.add(sectionName, 'q').color property string copyTextIcon : 'copy_custom' + property int rightButtonMargin: 15 + property int rightButtonSize: 30 + property int rightButtonLMargin: 10 + property int separatorHeight: 2 property QtObject sectionHeading: QtObject { property int padding: 5 @@ -45,6 +49,10 @@ QtObject { property color color: ColorsList.add(sectionName+'_send_border', 'f').color property int width: 1 } + property QtObject backgroundBorder: QtObject { + property color color: ColorsList.add(sectionName+'_send_background_border', 'ag').color + property int width: 2 + } } property QtObject composingText: QtObject { @@ -64,7 +72,7 @@ QtObject { property int pointSize: Units.dp * 9 property int headerPointSize: Units.dp * 9 property QtObject closeButton: QtObject{ - property int iconSize: 30 + property int iconSize: rightButtonSize property string name : 'close' property string icon : 'close_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+replyPreviewObject.name+'_'+name+'_b_n', icon, 'l_n_b_bg').color @@ -94,6 +102,11 @@ QtObject { property int lineHeight: 30 property int metaWidth: 40 + property QtObject separator: QtObject { + property color color: ColorsList.add(sectionName+'_separator_border', 'g10').color + property int width: 2 + } + property QtObject menu: QtObject { property int iconSize: 22 property string name : 'menu' @@ -164,7 +177,7 @@ QtObject { } property QtObject message: QtObject { - property int padding: 10 + property int padding: 8 property int radius: 4 property QtObject extraContent: QtObject { diff --git a/linphone-app/ui/modules/Linphone/Styles/qmldir b/linphone-app/ui/modules/Linphone/Styles/qmldir index b853636b9..b0d90c152 100644 --- a/linphone-app/ui/modules/Linphone/Styles/qmldir +++ b/linphone-app/ui/modules/Linphone/Styles/qmldir @@ -12,6 +12,7 @@ singleton RequestBlockStyle 1.0 Blocks/RequestBlockStyle.qml singleton ChatStyle 1.0 Chat/ChatStyle.qml singleton ChatAudioMessageStyle 1.0 Chat/ChatAudioMessageStyle.qml singleton ChatAudioPreviewStyle 1.0 Chat/ChatAudioPreviewStyle.qml +singleton ChatFilePreviewStyle 1.0 Chat/ChatFilePreviewStyle.qml singleton ChatForwardMessageStyle 1.0 Chat/ChatForwardMessageStyle.qml singleton ChatReplyMessageStyle 1.0 Chat/ChatReplyMessageStyle.qml diff --git a/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml b/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml index d6c682040..0bfc21755 100644 --- a/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml +++ b/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml @@ -22,7 +22,7 @@ Rectangle { property string _selectedSipAddress property bool showHistoryButton : true property bool updateSelectionModels : true - property bool isFilterVisible: searchView.visible || filterView.visible + property bool isFilterVisible: searchView.visible || showFilterView // --------------------------------------------------------------------------- @@ -32,7 +32,7 @@ Rectangle { signal showHistoryRequest() // --------------------------------------------------------------------------- - + property bool showFilterView : false color: TimelineStyle.color ColumnLayout { @@ -68,7 +68,7 @@ Rectangle { id:showHistory anchors.fill:parent onClicked: { - filterView.visible = !filterView.visible + timeline.showFilterView = !timeline.showFilterView } } RowLayout{ @@ -99,7 +99,7 @@ Rectangle { MouseArea{ anchors.fill:parent onClicked:{ - filterView.visible = !filterView.visible + timeline.showFilterView = !timeline.showFilterView } } } @@ -146,13 +146,13 @@ Rectangle { // Filter. // ------------------------------------------------------------------------- Rectangle{ - id:filterView + id:exhaustiveFilterView Layout.fillWidth: true Layout.preferredHeight: filterChoices.height Layout.alignment: Qt.AlignCenter border.color: TimelineStyle.filterField.borderColor border.width: 2 - visible:false + visible: timeline.showFilterView && !SettingsModel.useMinimalTimelineFilter ColumnLayout{ id:filterChoices @@ -234,6 +234,75 @@ Rectangle { } } } + Rectangle{ + id:minimalFilterView + Layout.fillWidth: true + Layout.preferredHeight: minimalFilterChoices.height + Layout.alignment: Qt.AlignCenter + border.color: TimelineStyle.filterField.borderColor + border.width: 2 + visible: timeline.showFilterView && SettingsModel.useMinimalTimelineFilter + + ColumnLayout{ + id:minimalFilterChoices + anchors.leftMargin: 20 + anchors.left:parent.left + anchors.right:parent.right + spacing:-4 + function getFilterFlags(){ + return securedCheckBox.getValue() | groupCheckBox.getValue() | conferenceCheckBox.getValue(); + } + CheckBoxText { + id: securedCheckBox + Layout.fillWidth: true + visible: SettingsModel.secureChatEnabled && SettingsModel.standardChatEnabled + //: 'Secure rooms' : Filter item. Selecting it will show all secure rooms. + text: qsTr('timelineFilterSecureRooms') + + onClicked: { + timeline.model.filterFlags = minimalFilterChoices.getFilterFlags() + } + function getValue(){ + if( checked) + return TimelineProxyModel.SecureChatRoom + else + return 0 + } + } + CheckBoxText { + id: groupCheckBox + Layout.fillWidth: true + visible: SettingsModel.secureChatEnabled || SettingsModel.standardChatEnabled + //: 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). + text: qsTr('timelineFilterChatGroups') + + onClicked: { + timeline.model.filterFlags = minimalFilterChoices.getFilterFlags() + } + function getValue(){ + if( checked) + return TimelineProxyModel.GroupChatRoom + else + return 0 + } + } + + CheckBoxText { + id: conferenceCheckBox + Layout.fillWidth: true + visible: false + //: 'Conferences' : Filter item. Selecting it will show all conferences. + text: qsTr('timelineFilterConferences') + + onClicked: { + timeline.model.filterFlags = minimalFilterChoices.getFilterFlags() + } + function getValue(){ + return 0 + } + } + } + } // ------------------------------------------------------------------------- // Search. // ------------------------------------------------------------------------- diff --git a/linphone-app/ui/modules/Linphone/qmldir b/linphone-app/ui/modules/Linphone/qmldir index 3f216954d..d9f533348 100644 --- a/linphone-app/ui/modules/Linphone/qmldir +++ b/linphone-app/ui/modules/Linphone/qmldir @@ -30,6 +30,8 @@ ContactDescription 1.0 Contact/ContactDescription.qml SipAddressDialog 1.0 Dialog/SipAddressDialog.qml +FileView 1.0 File/FileView.qml + History 1.0 History/History.qml SipAddressesMenu 1.0 Menus/SipAddressesMenu.qml diff --git a/linphone-app/ui/views/App/Calls/Incall.qml b/linphone-app/ui/views/App/Calls/Incall.qml index 1af7b86d0..99f2f1c4e 100644 --- a/linphone-app/ui/views/App/Calls/Incall.qml +++ b/linphone-app/ui/views/App/Calls/Incall.qml @@ -204,11 +204,9 @@ Rectangle { } } - TooltipArea { - text: !incall.call.recording + tooltipText: !incall.call.recording ? qsTr('startRecordingLabel') : qsTr('stopRecordingLabel') - } } ActionButton { diff --git a/linphone-app/ui/views/App/Calls/IncallFullscreenWindow.qml b/linphone-app/ui/views/App/Calls/IncallFullscreenWindow.qml index b51531020..a7d467c62 100644 --- a/linphone-app/ui/views/App/Calls/IncallFullscreenWindow.qml +++ b/linphone-app/ui/views/App/Calls/IncallFullscreenWindow.qml @@ -266,11 +266,9 @@ Window { ? call.startRecording() : call.stopRecording() - TooltipArea { - text: !recordingSwitch.recording + tooltipText: !recordingSwitch.recording ? qsTr('startRecordingLabel') : qsTr('stopRecordingLabel') - } } ActionButton { @@ -373,7 +371,7 @@ Window { ActionButton { isCustom: true backgroundRadius: 90 - colorSet: CallFullscreenStyle.buttons.cameraOn + colorSet: call && call.videoEnabled ? CallStyle.buttons.cameraOn : CallStyle.buttons.cameraOff updating: call && call.updating iconSize: CallFullscreenStyle.actionArea.iconSize diff --git a/linphone-app/ui/views/App/Main/Assistant/AssistantHome.qml b/linphone-app/ui/views/App/Main/Assistant/AssistantHome.qml index 21e3e8d0e..17b5bf90d 100644 --- a/linphone-app/ui/views/App/Main/Assistant/AssistantHome.qml +++ b/linphone-app/ui/views/App/Main/Assistant/AssistantHome.qml @@ -69,25 +69,25 @@ ColumnLayout { text: qsTr('homeDescription') } - CheckBoxText{ - id: cguCheckBox - Layout.topMargin: 10 - Layout.maximumWidth: infoItem.width - Layout.alignment: Qt.AlignHCenter - visible: applicationVendor != '' && ConstantsCpp.CguUrl != '' && ConstantsCpp.PrivatePolicyUrl != '' - checked: SettingsModel.cguAccepted - onCheckedChanged: SettingsModel.cguAccepted = checked - - //: 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. - text: qsTr('homeCgu').arg(applicationVendor).arg('< a href="'+ConstantsCpp.CguUrl+'">').arg('').arg('').arg('') - } + } } // --------------------------------------------------------------------------- // Buttons. // --------------------------------------------------------------------------- - + CheckBoxText{ + id: cguCheckBox + Layout.bottomMargin: 10 + Layout.maximumWidth: infoItem.width + Layout.alignment: Qt.AlignHCenter + visible: applicationVendor != '' && ConstantsCpp.CguUrl != '' && ConstantsCpp.PrivatePolicyUrl != '' + checked: SettingsModel.cguAccepted + onCheckedChanged: SettingsModel.cguAccepted = checked + + //: 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. + text: qsTr('homeCgu').arg(applicationVendor).arg('< a href="'+ConstantsCpp.CguUrl+'">').arg('').arg('').arg('') + } GridView { id: buttons diff --git a/linphone-app/ui/views/App/Main/Conversation.qml b/linphone-app/ui/views/App/Main/Conversation.qml index 35aa10d5e..4c2ddbc5e 100644 --- a/linphone-app/ui/views/App/Main/Conversation.qml +++ b/linphone-app/ui/views/App/Main/Conversation.qml @@ -492,7 +492,7 @@ ColumnLayout { anchors.leftMargin: 50 anchors.topMargin: 10 anchors.bottomMargin: 10 - visible: false + visible: true TextField { id:searchBar @@ -503,14 +503,13 @@ ColumnLayout { width: parent.width-14 icon: 'close_custom' overwriteColor: ConversationStyle.filters.iconColor - persistentIcon: true + showWhenEmpty: false //: 'Search in messages' : this is a placeholder when searching something in the timeline list placeholderText: qsTr('searchMessagesPlaceholder') onTextChanged: searchDelay.restart() onIconClicked: { - searchView.visible = false - chatRoomProxyModel.filterText = '' + searchView.text = '' } font.pointSize: ConversationStyle.filters.pointSize diff --git a/linphone-app/ui/views/App/Settings/SettingsUi.qml b/linphone-app/ui/views/App/Settings/SettingsUi.qml index e3204b480..060f0820b 100644 --- a/linphone-app/ui/views/App/Settings/SettingsUi.qml +++ b/linphone-app/ui/views/App/Settings/SettingsUi.qml @@ -222,6 +222,20 @@ TabContainer { } } } + FormGroup { + //: 'Minimal Timeline filter' + label: qsTr('minimalTimelineFilterLabel') + + Switch { + checked: SettingsModel.useMinimalTimelineFilter + + onClicked: SettingsModel.useMinimalTimelineFilter = !checked + TooltipArea{ + //: 'Show a minimal version of what to display in timeline.' : + text: qsTr('minimalTimelineFilterTooltip') + } + } + } } FormLine { maxItemWidth: parent.width diff --git a/linphone-app/ui/views/App/Styles/Calls/CallFullscreenStyle.qml b/linphone-app/ui/views/App/Styles/Calls/CallFullscreenStyle.qml index 4bc8bdef8..10d4d7c3b 100644 --- a/linphone-app/ui/views/App/Styles/Calls/CallFullscreenStyle.qml +++ b/linphone-app/ui/views/App/Styles/Calls/CallFullscreenStyle.qml @@ -113,6 +113,15 @@ QtObject { property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 'me_d_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color + + property color backgroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_n', icon, 'me_n_b_inv_bg').color + property color backgroundHiddenPartDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_d', icon, 'me_d_b_inv_bg').color + property color backgroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_h', icon, 'me_h_b_inv_bg').color + property color backgroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_p', icon, 'me_p_b_inv_bg').color + property color foregroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_n', icon, 'me_n_b_inv_fg').color + property color foregroundHiddenPartDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_d', icon, 'me_d_b_inv_fg').color + property color foregroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_h', icon, 'me_h_b_inv_fg').color + property color foregroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_p', icon, 'me_p_b_inv_fg').color } property QtObject telKeyad: QtObject { property int iconSize: 16 diff --git a/linphone-app/ui/views/App/Styles/Calls/CallStyle.qml b/linphone-app/ui/views/App/Styles/Calls/CallStyle.qml index 2ebce71fb..fd99f9932 100644 --- a/linphone-app/ui/views/App/Styles/Calls/CallStyle.qml +++ b/linphone-app/ui/views/App/Styles/Calls/CallStyle.qml @@ -113,6 +113,15 @@ QtObject { property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 'me_d_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color + + property color backgroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_n', icon, 'me_n_b_inv_bg').color + property color backgroundHiddenPartDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_d', icon, 'me_d_b_inv_bg').color + property color backgroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_h', icon, 'me_h_b_inv_bg').color + property color backgroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_p', icon, 'me_p_b_inv_bg').color + property color foregroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_n', icon, 'me_n_b_inv_fg').color + property color foregroundHiddenPartDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_d', icon, 'me_d_b_inv_fg').color + property color foregroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_h', icon, 'me_h_b_inv_fg').color + property color foregroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_p', icon, 'me_p_b_inv_fg').color } property QtObject telKeyad: QtObject { property int iconSize: 16 diff --git a/linphone-sdk b/linphone-sdk index b2a3ebaff..18cd2436e 160000 --- a/linphone-sdk +++ b/linphone-sdk @@ -1 +1 @@ -Subproject commit b2a3ebaffde438fb23c7b7d91327a35fa3a0f23d +Subproject commit 18cd2436eda9cb72023c4727691e4a2b764966d3