New chat layout :

- Split content type to be filtered by proxy lists.
- Add a message in notification when receiving a conference invitation.
- Change chat bubbles colors to match mobile application.
- Change date display on messages to remove sections. It allows to be more coherent when sorting messages.
- Change Chat Layout : outgoing messages to right, incoming messages to left.
- Change bubble design to be squared when grouped.
- Group messages on 1 second away from previous (and same sender).
- Add a background color with radius to files in reply messages.
- Make color corners on reply.
- Fix filename to 2 lines in file download icon.
- Add a background color on conference invitations.
- Change conference title from bold to normal on invitations.
- Rework chat message content layout to be used with grids and lists : files are now displayed in grid.
- Remove cyclic dependencies with reply design (which was recursivly linked with ChatContent).
- Fix center layouts that were not bind to the correct one.
- Align pictures to center.
- Fix hidden admin usernames in participant view.
This commit is contained in:
Julien Wadel 2023-03-03 16:57:06 +01:00
parent 6604d2874b
commit 4532e278ac
46 changed files with 776 additions and 466 deletions

View file

@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- OAuth2 connection to retrieve remote provisioning (Experimental and not usable without configuration).
- Add/View contact from a message.
- Mute option for each chatrooms.
- New Chat Layout.
## 5.0.11 - undefined

View file

@ -2215,6 +2215,11 @@ Klik her: <a href="%1">%1</a>
<extracomment>&apos;New messages received!&apos; Notification that warn the user of new messages.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>newConferenceInvitation</source>
<extracomment>&apos;Conference invitation received!&apos; : Notification about receiving an invitation to a conference.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>

View file

@ -2215,6 +2215,11 @@ Klicken Sie hier: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;New messages received!&apos; Notification that warn the user of new messages.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>newConferenceInvitation</source>
<extracomment>&apos;Conference invitation received!&apos; : Notification about receiving an invitation to a conference.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>

View file

@ -2215,6 +2215,11 @@ Click here: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;New messages received!&apos; Notification that warn the user of new messages.</extracomment>
<translation>New messages received!</translation>
</message>
<message>
<source>newConferenceInvitation</source>
<extracomment>&apos;Conference invitation received!&apos; : Notification about receiving an invitation to a conference.</extracomment>
<translation>Conference invitation received!</translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>

View file

@ -2215,6 +2215,11 @@ Haga clic aquí: &lt;a href=&quot;%1&quot;&gt;%1 &lt;/a&gt;
<extracomment>&apos;New messages received!&apos; Notification that warn the user of new messages.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>newConferenceInvitation</source>
<extracomment>&apos;Conference invitation received!&apos; : Notification about receiving an invitation to a conference.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>

View file

@ -2215,6 +2215,11 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;New messages received!&apos; Notification that warn the user of new messages.</extracomment>
<translation>Nouveaux messages arrivés !</translation>
</message>
<message>
<source>newConferenceInvitation</source>
<extracomment>&apos;Conference invitation received!&apos; : Notification about receiving an invitation to a conference.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>

View file

@ -2202,6 +2202,11 @@ Kattintson ide: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;New messages received!&apos; Notification that warn the user of new messages.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>newConferenceInvitation</source>
<extracomment>&apos;Conference invitation received!&apos; : Notification about receiving an invitation to a conference.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>

View file

@ -2215,6 +2215,11 @@ Clicca: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;New messages received!&apos; Notification that warn the user of new messages.</extracomment>
<translation>E&apos; stato ricevuto un nuovo messaggio!</translation>
</message>
<message>
<source>newConferenceInvitation</source>
<extracomment>&apos;Conference invitation received!&apos; : Notification about receiving an invitation to a conference.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>

View file

@ -2202,6 +2202,11 @@
<extracomment>&apos;New messages received!&apos; Notification that warn the user of new messages.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>newConferenceInvitation</source>
<extracomment>&apos;Conference invitation received!&apos; : Notification about receiving an invitation to a conference.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>

View file

@ -2228,6 +2228,11 @@ Spustelėkite čia: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;New messages received!&apos; Notification that warn the user of new messages.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>newConferenceInvitation</source>
<extracomment>&apos;Conference invitation received!&apos; : Notification about receiving an invitation to a conference.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>

View file

@ -2215,6 +2215,11 @@ Clique aqui: &lt;a href=&quot;%1&quot;&gt;%1 &lt;/a&gt;
<extracomment>&apos;New messages received!&apos; Notification that warn the user of new messages.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>newConferenceInvitation</source>
<extracomment>&apos;Conference invitation received!&apos; : Notification about receiving an invitation to a conference.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>

View file

@ -2228,6 +2228,11 @@
<extracomment>&apos;New messages received!&apos; Notification that warn the user of new messages.</extracomment>
<translation>Получены новые сообщения!</translation>
</message>
<message>
<source>newConferenceInvitation</source>
<extracomment>&apos;Conference invitation received!&apos; : Notification about receiving an invitation to a conference.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>

View file

@ -2215,6 +2215,11 @@ Klicka här: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;New messages received!&apos; Notification that warn the user of new messages.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>newConferenceInvitation</source>
<extracomment>&apos;Conference invitation received!&apos; : Notification about receiving an invitation to a conference.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>

View file

@ -2202,6 +2202,11 @@ Buraya tıklayın: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;New messages received!&apos; Notification that warn the user of new messages.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>newConferenceInvitation</source>
<extracomment>&apos;Conference invitation received!&apos; : Notification about receiving an invitation to a conference.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>

View file

@ -2228,6 +2228,11 @@
<extracomment>&apos;New messages received!&apos; Notification that warn the user of new messages.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>newConferenceInvitation</source>
<extracomment>&apos;Conference invitation received!&apos; : Notification about receiving an invitation to a conference.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>

View file

@ -2202,6 +2202,11 @@
<extracomment>&apos;New messages received!&apos; Notification that warn the user of new messages.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>newConferenceInvitation</source>
<extracomment>&apos;Conference invitation received!&apos; : Notification about receiving an invitation to a conference.</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>OnlineInstallerDialog</name>

View file

@ -331,6 +331,7 @@
<file>ui/modules/Linphone/Chat/Chat.qml</file>
<file>ui/modules/Linphone/Chat/ChatContent.qml</file>
<file>ui/modules/Linphone/Chat/ChatDeliveries.qml</file>
<file>ui/modules/Linphone/Chat/ChatFullContent.qml</file>
<file>ui/modules/Linphone/Chat/ChatMenu.qml</file>
<file>ui/modules/Linphone/Chat/ChatAudioMessage.qml</file>
<file>ui/modules/Linphone/Chat/ChatAudioPreview.qml</file>

View file

@ -40,9 +40,9 @@ QString ChatRoomProxyModel::gCachedText;
// =============================================================================
ChatRoomProxyModel::ChatRoomProxyModel (QObject *parent) : QSortFilterProxyModel(parent) {
ChatRoomProxyModel::ChatRoomProxyModel (QObject *parent) : SortFilterProxyModel(parent) {
mMarkAsReadEnabled = true;
mDeleteSourceModel= false;
App *app = App::getInstance();
QObject::connect(app->getMainWindow(), &QWindow::activeChanged, this, [this]() {
handleIsActiveChanged(App::getInstance()->getMainWindow());

View file

@ -21,7 +21,7 @@
#ifndef CHAT_ROOM_PROXY_MODEL_H_
#define CHAT_ROOM_PROXY_MODEL_H_
#include <QSortFilterProxyModel>
#include "app/proxyModel/SortFilterProxyModel.hpp"
#include "ChatRoomModel.hpp"
@ -29,7 +29,7 @@
class QWindow;
class ChatRoomProxyModel : public QSortFilterProxyModel {
class ChatRoomProxyModel : public SortFilterProxyModel {
class ChatRoomModelFilter;
Q_OBJECT

View file

@ -63,9 +63,25 @@ bool ContentProxyModel::filterAcceptsRow (
int sourceRow,
const QModelIndex &sourceParent
) const {
Q_UNUSED(sourceRow)
Q_UNUSED(sourceParent)
return true;
bool show = false;
if (mFilter == FilterContentType::All)
show = true;
else{
QModelIndex index = sourceModel()->index(sourceRow, 0, QModelIndex());
auto contentModel = sourceModel()->data(index).value<ContentModel*>();
if( mFilter == FilterContentType::Text && contentModel->isText())
show = true;
else if( mFilter == FilterContentType::Voice && contentModel->isVoiceRecording())
show = true;
else if( mFilter == FilterContentType::Conference && contentModel->getConferenceInfoModel())
show = true;
else if( mFilter == FilterContentType::File && !contentModel->isIcalendar() && (contentModel->isFile() || contentModel->isFileTransfer()|| contentModel->isFileEncrypted()) && !contentModel->isVoiceRecording() )
show = true;
}
return show;
}
bool ContentProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const {
@ -98,4 +114,15 @@ void ContentProxyModel::remove(ContentModel * model){
void ContentProxyModel::clear(){
qobject_cast<ContentListModel*>(sourceModel())->clear();
}
ContentProxyModel::FilterContentType ContentProxyModel::getFilter() const{
return mFilter;
}
void ContentProxyModel::setFilter(const FilterContentType& contentType){
if(contentType != mFilter){
mFilter = contentType;
emit filterChanged();
invalidate();
}
}

View file

@ -39,10 +39,24 @@ class ContentProxyModel : public QSortFilterProxyModel {
public:
ContentProxyModel (QObject *parent = nullptr);
Q_PROPERTY(ChatMessageModel * chatMessageModel READ getChatMessageModel WRITE setChatMessageModel NOTIFY chatMessageModelChanged)
Q_PROPERTY(FilterContentType filter READ getFilter WRITE setFilter NOTIFY filterChanged)
enum FilterContentType {
All,
File,
Text,
Voice,
Conference,
Unknown
};
Q_ENUM(FilterContentType)
ChatMessageModel * getChatMessageModel() const;
void setChatMessageModel(ChatMessageModel * message);
FilterContentType getFilter() const;
void setFilter(const FilterContentType& contentType);
Q_INVOKABLE void setContentListModel(ContentListModel * model);
Q_INVOKABLE void addFile(const QString& path);
Q_INVOKABLE void remove(ContentModel * model);
@ -50,13 +64,14 @@ public:
signals:
void chatMessageModelChanged();
void filterChanged();
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<ContentListModel> mContents;
FilterContentType mFilter = All;
};
#endif

View file

@ -288,6 +288,9 @@ void Notifier::notifyReceivedMessages (const list<shared_ptr<linphone::ChatMessa
}
}else
txt = tr("newFileMessage");
if(txt.isEmpty() && message->hasConferenceInvitationContent())
//: 'Conference invitation received!' : Notification about receiving an invitation to a conference.
txt = tr("newConferenceInvitation");
}else
//: 'New messages received!' Notification that warn the user of new messages.
txt = tr("newChatRoomMessages");

View file

@ -76,8 +76,14 @@ class ColorListModel : public ProxyListModel {
ADD_COLOR("n", "#A1A1A1", "Primary color for pressed button")
ADD_COLOR("o", "#D0D8DE", "Primary color for disabled button")
ADD_COLOR("outgoing_bg","#F3F3F3","Outgoing message background")
ADD_COLOR("incoming_bg","#D0D8DE","Incoming message background")
ADD_COLOR("outgoing_bg","#FFEEE5","Outgoing message background")
ADD_COLOR("incoming_bg","#F3F3F3","Incoming message background")
ADD_COLOR("outgoing_reply_mark_bg","#FF9E67","Outgoing reply message mark background")
ADD_COLOR("incoming_reply_mark_bg","#9B9B9B","Incoming reply message mark background")
ADD_COLOR("reply_file_bg","#F4F4F4","File icon background in reply")
ADD_COLOR("extension_file_border","#DEDEDE","File icon border in reply")
ADD_COLOR("primary_accept", "#9ECD1D", "Primary color for accepting button")

View file

@ -125,8 +125,8 @@ QString Utils::toTimeString(QDateTime date, const QString& format){
return getOffsettedUTC(date).toString(format);
}
QString Utils::toDateString(QDateTime date){
return getOffsettedUTC(date).toString("yyyy/MM/dd");
QString Utils::toDateString(QDateTime date, const QString& format){
return getOffsettedUTC(date).toString(format);
}
QString Utils::getDisplayName(const QString& address){

View file

@ -61,7 +61,7 @@ public:
static QDateTime getOffsettedUTC(const QDateTime& date);
Q_INVOKABLE static QString toDateTimeString(QDateTime date);
Q_INVOKABLE static QString toTimeString(QDateTime date, const QString& format = "hh:mm:ss");
Q_INVOKABLE static QString toDateString(QDateTime date);
Q_INVOKABLE static QString toDateString(QDateTime date, const QString& format = "yyyy/MM/dd");
Q_INVOKABLE static QString getDisplayName(const QString& address);
Q_INVOKABLE static QString getInitials(const QString& username); // Support UTF32
Q_INVOKABLE static QString toString(const LinphoneEnums::TunnelMode& mode);

View file

@ -97,13 +97,7 @@ Rectangle {
container.proxyModel.loadMoreEntriesAsync()
}
}
section {
criteria: ViewSection.FullString
delegate: sectionHeading
property: '$sectionDate'
}
// -----------------------------------------------------------------------
Component.onCompleted: Logic.initView()
onMovementStarted: {Logic.handleMovementStarted(); chat.isMoving = true}
onMovementEnded: {Logic.handleMovementEnded(); chat.isMoving = false}
@ -124,47 +118,6 @@ Rectangle {
}
}
// -----------------------------------------------------------------------
// Heading.
// -----------------------------------------------------------------------
Component {
id: sectionHeading
Item {
implicitHeight: container.height + ChatStyle.sectionHeading.bottomMargin
width: parent.width
clip: false
Borders {
id: container
borderColor: ChatStyle.sectionHeading.border.colorModel.color
bottomWidth: ChatStyle.sectionHeading.border.width
implicitHeight: text.contentHeight +
ChatStyle.sectionHeading.padding * 2 +
ChatStyle.sectionHeading.border.width * 2
topWidth: ChatStyle.sectionHeading.border.width
width: parent.width
Text {
id: text
anchors.fill: parent
color: ChatStyle.sectionHeading.text.colorModel.color
font {
bold: true
pointSize: ChatStyle.sectionHeading.text.pointSize
capitalization: Font.Capitalize
}
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: new Date(section).toLocaleDateString(App.locale)
}
}
}
}
// -----------------------------------------------------------------------
// Message/Event renderer.
// -----------------------------------------------------------------------
@ -174,7 +127,22 @@ Rectangle {
property bool isNotice : $chatEntry.type === ChatRoomModel.NoticeEntry
property bool isCall : $chatEntry.type === ChatRoomModel.CallEntry
property bool isMessage : $chatEntry.type === ChatRoomModel.MessageEntry
property var previousItem : proxyModel.count > 0 && index >0 ? proxyModel.getAt(index-1) : null
property var nextItem : proxyModel.count > 0 ? proxyModel.getAt(index+1) : null // bind to count
property bool displayDate: !Utils.equalDate(new Date($chatEntry.timestamp), new Date())
property bool isTopGrouped: isGrouped(entry.previousItem, $chatEntry) || false
property bool isBottomGrouped: isGrouped($chatEntry, entry.nextItem) || false
onIsBottomGroupedChanged: if(loader.item) loader.item.isBottomGrouped = isBottomGrouped
onIsTopGroupedChanged: if(loader.item) loader.item.isTopGrouped = isTopGrouped
function isGrouped(item1, item2){
return item1 && item2 //Have a previous entry
&& item1.type == ChatRoomModel.MessageEntry // Previous entry is a message
&& item2.type == ChatRoomModel.MessageEntry // Previous entry is a message
&& item2.fromSipAddress == item1.fromSipAddress // Same user
&& Math.abs((new Date(item2.timestamp)).getTime() - (new Date(item1.timestamp)).getTime())/1000 < 60
}
function isHoverEntry () {
return mouseArea.containsMouse
}
@ -182,23 +150,13 @@ Rectangle {
function removeEntry () {
proxyModel.removeRow(index)
}
anchors {
left: parent ? parent.left : undefined
leftMargin: isNotice?0:ChatStyle.entry.leftMargin
right: parent ? parent.right : undefined
rightMargin: isNotice?0:ChatStyle.entry.deleteIconSize +
ChatStyle.entry.message.extraContent.spacing +
ChatStyle.entry.message.extraContent.rightMargin +
ChatStyle.entry.message.extraContent.leftMargin +
ChatStyle.entry.message.outgoing.areaSize
}
color: ChatStyle.colorModel.color
implicitHeight: layout.height + ChatStyle.entry.bottomMargin
implicitHeight: layout.height + (entry.isBottomGrouped? 1 : ChatStyle.entry.bottomMargin)
width: chat.contentWidth // Fill all space
clip: false
// ---------------------------------------------------------------------
MouseArea {
@ -208,51 +166,37 @@ Rectangle {
hoverEnabled: true
implicitHeight: layout.height
width: parent.width + parent.anchors.rightMargin
anchors.top: parent.top
//anchors.topMargin: (entry.isTopGrouped? 1 : ChatStyle.entry.bottomMargin)
clip: false
acceptedButtons: Qt.NoButton
onContainsMouseChanged: if(loader.item) loader.item.isHovering = containsMouse
ColumnLayout{
id: layout
spacing: 0
width: entry.width
Text{
id:authorName
Layout.leftMargin: timeDisplay.width + 10
RowLayout{
id: headerLayout
Layout.fillWidth: true
text : $chatEntry.fromDisplayName ? $chatEntry.fromDisplayName : ''
property var previousItem : {
if(index >0)
return proxyModel.getAt(index-1)
else
return null
}
color: ChatStyle.entry.event.text.colorModel.color
font.pointSize: ChatStyle.entry.event.text.pointSize
visible: isMessage
&& $chatEntry != undefined
&& !$chatEntry.isOutgoing // Only outgoing
&& (!previousItem //No previous entry
|| previousItem.type != ChatRoomModel.MessageEntry // Previous entry is a message
|| previousItem.fromSipAddress != $chatEntry.fromSipAddress // Different user
|| (new Date(previousItem.timestamp)).setHours(0, 0, 0, 0) != (new Date($chatEntry.timestamp)).setHours(0, 0, 0, 0) // Same day == section
)
}
RowLayout {
spacing: 0
width: entry.width
Layout.alignment: Qt.AlignTop | ($chatEntry.isOutgoing ? Qt.AlignRight : Qt.AlignLeft)
Layout.leftMargin: ChatStyle.entry.metaWidth// + ChatStyle.entry.message.extraContent.spacing
Layout.rightMargin: ChatStyle.entry.message.outgoing.areaSize
spacing:0
// Display time.
visible: !entry.isTopGrouped
Text {
id:timeDisplay
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: ChatStyle.entry.lineHeight
Layout.preferredWidth: ChatStyle.entry.time.width
Layout.alignment: Qt.AlignTop | ($chatEntry.isOutgoing ? Qt.AlignRight : Qt.AlignLeft)
Layout.preferredHeight: implicitHeight// ChatStyle.entry.lineHeight
//Layout.preferredWidth: ChatStyle.entry.time.width
color: ChatStyle.entry.event.text.colorModel.color
font.pointSize: ChatStyle.entry.time.pointSize
text: UtilsCpp.toTimeString($chatEntry.timestamp, 'hh:mm')
property bool displayYear: entry.displayDate && (new Date($chatEntry.timestamp)).getFullYear() != (new Date()).getFullYear()
text: $chatEntry
? (entry.displayDate ? UtilsCpp.toDateString($chatEntry.timestamp, (displayYear ? 'yyyy/':'') + 'MM/dd') + ' ' : '')
+ UtilsCpp.toTimeString($chatEntry.timestamp, 'hh:mm') + (authorName.visible ? ' - ' : '')
: ''
verticalAlignment: Text.AlignVCenter
@ -261,65 +205,85 @@ Rectangle {
}
visible:!isNotice
}
// Display content.
Loader {
id: loader
height: (item !== null && typeof(item)!== 'undefined')? item.height: 0
Layout.fillWidth: true
source: Logic.getComponentFromEntry($chatEntry)
property int loaderIndex: 0 // index of loader from remaining loaders
property int remainingIndex : loaderIndex % ((chat.remainingLoadersCount) / chat.syncLoaderBatch) != 0 // Check loader index to remaining loader.
onRemainingIndexChanged: if( remainingIndex == 0 && asynchronous) asynchronous = false
asynchronous: true
z:1
onStatusChanged: if( status == Loader.Ready) {
remainingIndex = -1 // overwrite to remove signal changed. That way, there is no more binding loops.
--chat.remainingLoadersCount // Loader is ready: remove one from remaining count.
}
Text{
id:authorName
//Layout.leftMargin: timeDisplay.width + ChatStyle.entry.metaWidth + ChatStyle.entry.message.extraContent.spacing
property var displayName: $chatEntry.fromDisplayName ? $chatEntry.fromDisplayName : $chatEntry.name
text : displayName != undefined ? displayName : ''
Component.onCompleted: loaderIndex = ++chat.remainingLoadersCount // on new Loader : one more remaining
Component.onDestruction: if( status != Loader.Ready) --chat.remainingLoadersCount // Remove remaining count if not loaded
color: ChatStyle.entry.event.text.colorModel.color
font.pointSize: ChatStyle.entry.event.text.pointSize
visible: isMessage
&& $chatEntry != undefined
&& !$chatEntry.isOutgoing // Only outgoing
&& (!entry.previousItem //No previous entry
|| entry.previousItem.type != ChatRoomModel.MessageEntry // Previous entry is a message
|| entry.previousItem.fromSipAddress != $chatEntry.fromSipAddress // Different user
|| (new Date(entry.previousItem.timestamp)).setHours(0, 0, 0, 0) != (new Date($chatEntry.timestamp)).setHours(0, 0, 0, 0) // Same day == section
)
}
}
// Display content.
Loader {
id: loader
height: (item !== null && typeof(item)!== 'undefined')? item.height: 0
Layout.fillWidth: true
source: Logic.getComponentFromEntry($chatEntry)
property int loaderIndex: 0 // index of loader from remaining loaders
property int remainingIndex : loaderIndex % ((chat.remainingLoadersCount) / chat.syncLoaderBatch) != 0 // Check loader index to remaining loader.
onRemainingIndexChanged: if( remainingIndex == 0 && asynchronous) asynchronous = false
asynchronous: true
z:1
onStatusChanged: if( status == Loader.Ready) {
loader.item.isTopGrouped = entry.isTopGrouped
loader.item.isBottomGrouped = entry.isBottomGrouped
remainingIndex = -1 // overwrite to remove signal changed. That way, there is no more binding loops.
--chat.remainingLoadersCount // Loader is ready: remove one from remaining count.
}
Component.onCompleted: {
loaderIndex = ++chat.remainingLoadersCount // on new Loader : one more remaining
}
Component.onDestruction: if( status != Loader.Ready) --chat.remainingLoadersCount // Remove remaining count if not loaded
}
Connections{
target: loader.item
ignoreUnknownSignals: true
//: "Copied to clipboard" : when a user copy a text from the menu, this message show up.
onCopyAllDone: container.noticeBannerText = qsTr("allTextCopied")
//: "Selection copied to clipboard" : when a user copy a text from the menu, this message show up.
onCopySelectionDone: container.noticeBannerText = qsTr("selectedTextCopied")
onReplyClicked: {
proxyModel.chatRoomModel.reply = $chatEntry
}
onForwardClicked:{
window.attachVirtualWindow(Qt.resolvedUrl('../Dialog/SipAddressDialog.qml')
//: 'Choose where to forward the message' : Dialog title for choosing where to forward the current message.
, {title: qsTr('forwardDialogTitle'),
addressSelectedCallback: function (sipAddress) {
var chat = CallsListModel.createChatRoom( '', proxyModel.chatRoomModel.haveEncryption, [sipAddress], false )
if(chat){
chat.chatRoomModel.forwardMessage($chatEntry)
TimelineListModel.select(chat.chatRoomModel)
}
},
chatRoomSelectedCallback: function (chatRoomModel){
if(chatRoomModel){
chatRoomModel.forwardMessage($chatEntry)
TimelineListModel.select(chatRoomModel)
}
}
})
}
Connections{
target: loader.item
ignoreUnknownSignals: true
//: "Copied to clipboard" : when a user copy a text from the menu, this message show up.
onCopyAllDone: container.noticeBannerText = qsTr("allTextCopied")
//: "Selection copied to clipboard" : when a user copy a text from the menu, this message show up.
onCopySelectionDone: container.noticeBannerText = qsTr("selectedTextCopied")
onReplyClicked: {
proxyModel.chatRoomModel.reply = $chatEntry
}
onForwardClicked:{
window.attachVirtualWindow(Qt.resolvedUrl('../Dialog/SipAddressDialog.qml')
//: 'Choose where to forward the message' : Dialog title for choosing where to forward the current message.
, {title: qsTr('forwardDialogTitle'),
addressSelectedCallback: function (sipAddress) {
var chat = CallsListModel.createChatRoom( '', proxyModel.chatRoomModel.haveEncryption, [sipAddress], false )
if(chat){
chat.chatRoomModel.forwardMessage($chatEntry)
TimelineListModel.select(chat.chatRoomModel)
}
},
chatRoomSelectedCallback: function (chatRoomModel){
if(chatRoomModel){
chatRoomModel.forwardMessage($chatEntry)
TimelineListModel.select(chatRoomModel)
}
}
})
}
onGoToMessage:{
container.goToMessage(message) // sometimes, there is no access to chat id (maybe because of cleaning component while loading new items). Use a global intermediate.
}
onConferenceIcsCopied: container.noticeBannerText = qsTr('conferencesCopiedICS')
onAddContactClicked: container.addContactClicked(contactAddress)
onViewContactClicked: container.viewContactClicked(contactAddress)
onGoToMessage:{
container.goToMessage(message) // sometimes, there is no access to chat id (maybe because of cleaning component while loading new items). Use a global intermediate.
}
onConferenceIcsCopied: container.noticeBannerText = qsTr('conferencesCopiedICS')
onAddContactClicked: container.addContactClicked(contactAddress)
onViewContactClicked: container.viewContactClicked(contactAddress)
}
}
}

View file

@ -24,8 +24,8 @@ import 'Message.js' as Logic
Loader{
id: mainItem
property ContentModel contentModel
property int maxWidth : parent.width
property int fitWidth: active ? Math.max(maxWidth - ChatAudioMessageStyle.emptySpace, ChatAudioMessageStyle.minWidth) : 0
property int availableWidth : parent.width
property int fitWidth: active ? Math.max(availableWidth - ChatAudioMessageStyle.emptySpace, ChatAudioMessageStyle.minWidth) : 0
property int fitHeight: active ? 60 : 0
property font customFont : SettingsModel.textMessageFont
@ -49,7 +49,7 @@ Loader{
property bool isPlaying : vocalPlayer.item && vocalPlayer.item.playbackState === SoundPlayer.PlayingState
onIsPlayingChanged: isPlaying ? mediaProgressBar.resume() : mediaProgressBar.stop()
width: maxWidth < 0 || maxWidth > fitWidth ? fitWidth : maxWidth
width: availableWidth < 0 || availableWidth > fitWidth ? fitWidth : availableWidth
height: mainItem.fitHeight
clip: false

View file

@ -25,9 +25,9 @@ Loader{
id: mainItem
property ContentModel contentModel
property ConferenceInfoModel conferenceInfoModel: contentModel ? contentModel.conferenceInfoModel : null
property int maxWidth : parent.width
property int availableWidth : parent.width
property int fitHeight: active && item ? item.fitHeight : 0
property int fitWidth: active && item ? maxWidth/2 + ChatCalendarMessageStyle.widthMargin*2 : 0
property int fitWidth: active && item ? availableWidth/2 + ChatCalendarMessageStyle.widthMargin*2 : 0
property bool containsMouse: false
property int gotoButtonMode: -1 //-1: hide, 0:goto, 1:MoreInfo
property bool isExpanded : false

View file

@ -25,9 +25,9 @@ Loader{
id: mainItem
property ContentModel contentModel
property ConferenceInfoModel conferenceInfoModel: contentModel ? contentModel.conferenceInfoModel : null
property int maxWidth : parent.width
property int availableWidth : parent.width
property int fitHeight: active && item ? item.fitHeight : 0 // + (isExpanded? 200 : 0): 0
property int fitWidth: active && item ? Math.min(maxWidth > 0 ? maxWidth : 9999999, item.fitWidth + ChatCalendarMessageStyle.widthMargin*2) : 0
property int fitWidth: active && item ? Math.min(availableWidth > 0 ? availableWidth : 9999999, item.fitWidth + ChatCalendarMessageStyle.widthMargin*2) : 0
property bool containsMouse: false
property int gotoButtonMode: -1 //-1: hide, 0:goto, 1:MoreInfo
property bool isExpanded : false
@ -56,7 +56,12 @@ Loader{
hoverEnabled: true
onClicked: CallsListModel.prepareConferenceCall(mainItem.conferenceInfoModel)
onHoveredChanged: mainItem.containsMouse = loadedItem.containsMouse
onHoveredChanged: mainItem.containsMouse = loadedItem.containsMouse
Rectangle{
anchors.fill: parent
color: ChatCalendarMessageStyle.backgroundColor.normal.color
radius: 5
}
ColumnLayout{
id: layout
@ -104,7 +109,6 @@ Loader{
elide: Text.ElideRight
color: ChatCalendarMessageStyle.subject.colorModel.color
font.pointSize: ChatCalendarMessageStyle.subject.pointSize
font.weight: Font.Bold
text: mainItem.conferenceInfoModel.subject
}
RowLayout {

View file

@ -16,58 +16,199 @@ import LinphoneEnums 1.0
import ColorsList 1.0
// =============================================================================
Column{
// Simple content display without reply and forward. These modules need to be splitted because of cyclic dependencies.
// See ChatFullContent
Loader{// Use of Loader because of Repeater (items cannot be loaded dynamically)
id: mainItem
property ContentModel contentModel
property ChatMessageModel chatMessageModel: null
property int availableWidth //const
property int fileWidth: ChatStyle.entry.message.file.height * 4 / 3 + 2*ChatStyle.entry.message.file.margins
property int fitHeight: calendarMessage.fitHeight + message.fitHeight + fileMessage.fitHeight + audioMessage.fitHeight
property int fitWidth: calendarMessage.fitWidth + message.fitWidth + fileMessage.fitWidth + audioMessage.fitWidth
property color backgroundColor
property string lastTextSelected
property alias textColor: message.color
property alias textFont: message.font
property alias fileIsHovering: fileMessage.isHovering
// Readonly
property int bestWidth: Math.min(availableWidth, Math.max(filesBestWidth, conferencesBestWidth, textsBestWidth, voicesBestWidth))
property int filesBestWidth: 0
property int filesCount: 0
property int conferencesCount: 0
property int conferencesBestWidth: 0
property int textsBestWidth: 0
property int textsCount: 0
property int voicesBestWidth: 0
property int voicesCount: 0
signal isFileHoveringChanged(bool isFileHovering)
signal lastTextSelectedChanged(string lastTextSelected)
signal rightClicked()
signal conferenceIcsCopied()
property int maxWidth
height: fitHeight
anchors.left: parent ? parent.left : undefined
anchors.right: parent ? parent.right : undefined
property bool useTextColor: false
property color textColor
spacing: 0
property int fileBorderWidth : 0
property color fileBackgroundColor: ChatStyle.entry.message.file.extension.background.colorModel.color
property int fileBackgroundRadius: ChatStyle.entry.message.file.extension.radius
property bool isOutgoing : contentModel && contentModel.chatMessageModel && (contentModel.chatMessageModel.isOutgoing || contentModel.chatMessageModel.state == LinphoneEnums.ChatMessageStateIdle);
z: message.visible ? 0 : 1
ChatConferenceInvitationMessage{
id: calendarMessage
contentModel: mainItem.contentModel
width: parent.width
maxWidth: mainItem.maxWidth
gotoButtonMode: 1
onExpandToggle: isExpanded=!isExpanded
height: fitHeight
z: 1
onConferenceIcsCopied:mainItem.conferenceIcsCopied()
}
ChatAudioMessage{
id: audioMessage
contentModel: mainItem.contentModel
visible: contentModel
z: 1
}
ChatFileMessage{
id: fileMessage
contentModel: mainItem.contentModel
width: parent.width
z: 2
}
ChatTextMessage {
id: message
contentModel: mainItem.contentModel
onLastTextSelectedChanged: mainItem.lastTextSelected = lastTextSelected
color: isOutgoing ? ChatStyle.entry.message.outgoing.text.colorModel.color : ChatStyle.entry.message.incoming.text.colorModel.color
onRightClicked: mainItem.rightClicked()
active: chatMessageModel
sourceComponent: Component{
Column{
id: mainComponent
spacing: 0
function updateFilesBestWidth(){
var newBestWidth = 0
var count = 0
for(var child in messageFilesList.children) {
var item = messageFilesList.children[child]
if(item){
var a = item.fitWidth
if(a) {
++count
newBestWidth = Math.max(newBestWidth,a)
}
}
}
if(count > 1){
newBestWidth = Math.max(newBestWidth, mainItem.fileWidth*count)
}
mainItem.filesCount = count
mainItem.filesBestWidth = newBestWidth
}
function updateListBestWidth(listView){
var newBestWidth = 0
var count = 0
for(var child in listView.contentItem.children) {
var a = listView.contentItem.children[child].fitWidth
if(a) {
++count
newBestWidth = Math.max(newBestWidth,a)
}
}
return [count, newBestWidth];
}
ListView {
id: messagesVoicesList
width: parent.width
visible: count > 0
spacing: 0
clip: false
model: ContentProxyModel{
filter: ContentProxyModel.ContentType.Voice
chatMessageModel: mainItem.chatMessageModel
}
height: contentHeight
boundsBehavior: Flickable.StopAtBounds
interactive: false
function updateBestWidth(){
var newWidth = mainComponent.updateListBestWidth(messagesVoicesList)
mainItem.voicesCount = newWidth[0]
mainItem.voicesBestWidth = newWidth[1]
}
delegate: ChatAudioMessage{
id: audioMessage
contentModel: $modelData
visible: contentModel
z: 1
Component.onCompleted: messagesVoicesList.updateBestWidth()
}
Component.onCompleted: messagesVoicesList.updateBestWidth
}
// CONFERENCE
ListView {
id: messagesConferencesList
width: parent.width
visible: count > 0
spacing: 0
clip: false
model: ContentProxyModel{
filter: ContentProxyModel.ContentType.Conference
chatMessageModel: mainItem.chatMessageModel
}
height: contentHeight
boundsBehavior: Flickable.StopAtBounds
interactive: false
function updateBestWidth(){
var newWidth = mainComponent.updateListBestWidth(messagesConferencesList)
mainItem.conferencesCount = newWidth[0]
mainItem.conferencesBestWidth = newWidth[1]
}
Component.onCompleted: messagesConferencesList.updateBestWidth()
delegate: ChatConferenceInvitationMessage{
id: calendarMessage
contentModel: $modelData
width: parent.width
availableWidth: mainItem.availableWidth
gotoButtonMode: 1
onExpandToggle: isExpanded=!isExpanded
height: fitHeight
z: 1
onConferenceIcsCopied:mainItem.conferenceIcsCopied()
onFitWidthChanged: messagesConferencesList.updateBestWidth()
Component.onCompleted: messagesConferencesList.updateBestWidth()
}
}
// FILES
GridLayout {
id: messageFilesList
property alias count: repeater.count
visible: count > 0
clip: false
property int availableSection: mainItem.availableWidth / mainItem.fileWidth
property int bestFitSection: mainItem.bestWidth / mainItem.fileWidth
columns: Math.max(1, Math.min(availableSection , bestFitSection))
columnSpacing: 0
rowSpacing: 0
width: parent.width
Repeater{
id: repeater
model: ContentProxyModel{
filter: ContentProxyModel.ContentType.File
chatMessageModel: mainItem.chatMessageModel
}
ChatFileMessage{
contentModel: $modelData
onIsHoveringChanged: mainItem.isFileHoveringChanged(isHovering)
borderWidth: mainItem.fileBorderWidth
backgroundColor: mainItem.fileBackgroundColor
backgroundRadius: mainItem.fileBackgroundRadius
Component.onCompleted: mainComponent.updateFilesBestWidth()
}
}
}
// TEXTS
ListView {
id: messagesTextsList
width: parent.width
visible: count > 0
spacing: 0
clip: false
model: ContentProxyModel{
filter: ContentProxyModel.ContentType.Text
chatMessageModel: mainItem.chatMessageModel
}
height: contentHeight
boundsBehavior: Flickable.StopAtBounds
interactive: false
function updateBestWidth(){
var newWidth = mainComponent.updateListBestWidth(messagesTextsList)
mainItem.textsCount = newWidth[0]
mainItem.textsBestWidth = newWidth[1]
}
Component.onCompleted: messagesTextsList.updateBestWidth()
delegate:
ChatTextMessage {
contentModel: $modelData
onLastTextSelectedChanged: mainItem.lastTextSelectedChanged(lastTextSelected)
color: mainItem.useTextColor
? mainItem.textColor
: $modelData.isOutgoing
? ChatStyle.entry.message.outgoing.text.colorModel.color
: ChatStyle.entry.message.incoming.text.colorModel.color
onRightClicked: mainItem.rightClicked()
onFitWidthChanged: messagesTextsList.updateBestWidth()
Component.onCompleted: messagesTextsList.updateBestWidth()
}
}
}
}
}

View file

@ -19,11 +19,21 @@ Row {
property ChatMessageModel chatMessageModel: contentModel && contentModel.chatMessageModel
property ContentModel contentModel
property bool isOutgoing : chatMessageModel && ( chatMessageModel.isOutgoing || chatMessageModel.state == LinphoneEnums.ChatMessageStateIdle);
property int fitWidth: visible ? Math.max( Math.max((thumbnailProvider.sourceComponent == extension ? thumbnailProvider.item.fitWidth : 0)
property int fitHeight: ChatStyle.entry.message.file.height
property int fitWidth: ChatStyle.entry.message.file.height * 4 / 3 + 2*ChatStyle.entry.message.file.margins
property int borderWidth : 0
property color backgroundColor: ChatStyle.entry.message.file.extension.background.colorModel.color
property int backgroundRadius: ChatStyle.entry.message.file.extension.radius
/*
property int fitWidth: visible
? Math.max( Math.max((thumbnailProvider.sourceComponent == extension
? thumbnailProvider.item.fitWidth
: 0)
, thumbnailProvider.width + 3*ChatStyle.entry.message.file.margins)
, Math.max(ChatStyle.entry.message.file.width, ChatStyle.entry.message.outgoing.areaSize)) : 0
, Math.max(ChatStyle.entry.message.file.width, ChatStyle.entry.message.outgoing.areaSize))
: 0
property int fitHeight: visible ? rectangle.height : 0
*/
property bool isAnimatedImage : mainRow.contentModel && mainRow.contentModel.wasDownloaded && UtilsCpp.isAnimatedImage(mainRow.contentModel.filePath)
property bool haveThumbnail: mainRow.contentModel && mainRow.contentModel.thumbnail
property bool isHovering: thumbnailProvider.state == 'hovered'
@ -32,7 +42,8 @@ Row {
signal copySelectionDone()
signal forwardClicked()
height: fitHeight
visible: contentModel && !contentModel.isIcalendar() && (contentModel.isFile() || contentModel.isFileTransfer()) && !contentModel.isVoiceRecording()
width: fitWidth
visible: true
// ---------------------------------------------------------------------------
// File message.
// ---------------------------------------------------------------------------
@ -56,15 +67,14 @@ Row {
property string thumbnail : mainRow.contentModel ? mainRow.contentModel.thumbnail : ''
color: 'transparent'
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.leftMargin: ChatStyle.entry.message.file.margins
anchors.topMargin: ChatStyle.entry.message.file.margins
height: 2*ChatStyle.entry.message.file.margins + (mainRow.isAnimatedImage ? ChatStyle.entry.message.file.heightbetter
: thumbnailProvider.sourceComponent == extension ? thumbnailProvider.item.fitHeight
: ChatStyle.entry.message.file.height
height: 2*ChatStyle.entry.message.file.margins + (mainRow.isAnimatedImage
? ChatStyle.entry.message.file.heightbetter
: thumbnailProvider.sourceComponent == extension
? ChatStyle.entry.message.file.height
: ChatStyle.entry.message.file.height
)
width: mainRow.width
radius: ChatStyle.entry.message.radius
// ---------------------------------------------------------------------
@ -77,12 +87,14 @@ Row {
Image {
id: thumbnailImageSource
property real scaleAnimatorTo : ChatStyle.entry.message.file.animation.thumbnailTo
anchors.centerIn: parent
mipmap: SettingsModel.mipmapEnabled
source: mainRow.contentModel.thumbnail
autoTransform: true
fillMode: Image.PreserveAspectFit
height: ChatStyle.entry.message.file.height
width: height*4/3
Loader{
anchors.fill: parent
sourceComponent: Image{// Better quality on zoom
@ -117,14 +129,18 @@ Row {
Rectangle {
property int fitWidth: Math.max(downloadText.implicitWidth, Math.max(fileName.visible ? fileName.implicitWidth : 0, fileIcon.iconSize)) + 20
property int fitHeight: fileIcon.iconSize + (fileName.visible ? fileName.implicitHeight + ChatStyle.entry.message.file.spacing : 0 )
+ (downloadText.visible? downloadText.implicitHeight + ChatStyle.entry.message.file.spacing : 0) + 2*ChatStyle.entry.message.file.margins
//property int fitHeight: fileIcon.iconSize + (fileName.visible ? fileName.implicitHeight + ChatStyle.entry.message.file.spacing : 0 )
// + (downloadText.visible? downloadText.implicitHeight + ChatStyle.entry.message.file.spacing : 0) + 2*ChatStyle.entry.message.file.margins
property real scaleAnimatorTo : ChatStyle.entry.message.file.animation.to
height: fitHeight
width: fitWidth
color: ChatStyle.entry.message.file.extension.background.colorModel.color
radius: ChatStyle.entry.message.file.extension.radius
anchors.centerIn: parent
height: ChatStyle.entry.message.file.height
width: height*4/3
color: mainRow.backgroundColor
radius: mainRow.backgroundRadius
border.width: mainRow.borderWidth
border.color: ChatStyle.entry.message.file.extension.background.borderColorModel.color
ColumnLayout{
anchors.fill: parent
anchors.topMargin: ChatStyle.entry.message.file.margins
@ -135,6 +151,8 @@ Row {
Layout.alignment: Qt.AlignCenter
icon: extensionText.text != '' ? ChatStyle.entry.message.file.extension.icon : ChatStyle.entry.message.file.extension.unknownIcon
iconSize: ChatStyle.entry.message.file.extension.iconSize
Layout.fillHeight: true
Layout.fillWidth: true
Layout.preferredHeight: iconSize
Layout.preferredWidth: iconSize
Text {
@ -176,10 +194,10 @@ Row {
visible: mainRow.contentModel && !mainRow.isAnimatedImage && !mainRow.haveThumbnail
color: ChatStyle.entry.message.file.extension.text.colorModel.color
elide: Text.ElideRight
font.pointSize: ChatStyle.entry.message.file.name.pointSize
wrapMode: Text.WrapAnywhere
horizontalAlignment: Qt.AlignCenter
horizontalAlignment: Text.AlignHCenter
maximumLineCount: 2
text: (mainRow.contentModel ? mainRow.contentModel.name : '')
}
@ -187,7 +205,7 @@ Row {
id: downloadText
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: visible ? ChatStyle.entry.message.file.download.height : 0
Layout.preferredHeight: visible ? contentHeight : 0
//: 'Cancel' : Message link to cancel a transfer (upload/download)
text: mainRow.contentModel ? rectangle.isTransferring ? qsTr('fileTransferCancel')
//: 'Download' : Message link to download a file
@ -197,8 +215,8 @@ Row {
font.pointSize: ChatStyle.entry.message.file.download.pointSize
color:ChatStyle.entry.message.file.extension.text.colorModel.color
visible: (mainRow.contentModel? (!mainItem.isOutgoing && !mainRow.contentModel.wasDownloaded) || rectangle.isTransferring : false)
horizontalAlignment: Qt.AlignCenter
verticalAlignment: Qt.AlignCenter
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
@ -206,7 +224,7 @@ Row {
}
Loader {
id: thumbnailProvider
anchors.centerIn: parent
sourceComponent: (mainRow.contentModel ?
(mainRow.isAnimatedImage ? animatedImage
: (mainRow.haveThumbnail ? thumbnailImage : extension )

View file

@ -22,13 +22,13 @@ import 'Message.js' as Logic
Item {
id: mainItem
property ChatMessageModel mainChatMessageModel
property int maxWidth : parent.width
property int availableWidth : parent.width
property int fitWidth: visible ? headerArea.fitWidth + 7 + ChatForwardMessageStyle.padding * 2 : 0
property int fitHeight: visible ? icon.height : 0
property int fitHeight: visible ? icon.height + 5 : 0
property font customFont : SettingsModel.textMessageFont
visible: mainChatMessageModel && mainChatMessageModel.isForward
width: maxWidth > fitWidth ? fitWidth : maxWidth
width: availableWidth > fitWidth ? fitWidth : availableWidth
height: fitHeight
ColumnLayout{

View file

@ -0,0 +1,66 @@
import QtQuick 2.7
import QtQuick.Layouts 1.3
import Clipboard 1.0
import Common 1.0
import Linphone 1.0
import Common.Styles 1.0
import Linphone.Styles 1.0
import TextToSpeech 1.0
import Utils 1.0
import Units 1.0
import UtilsCpp 1.0
import LinphoneEnums 1.0
import ColorsList 1.0
// =============================================================================
// Full content display with reply and forward. These modules need to be splitted because of cyclic dependencies.
// See ChatContent
Column{
id: mainItem
property ChatMessageModel chatMessageModel: null
property int availableWidth //const
// Readonly
property int bestWidth: Math.min(availableWidth, Math.max(forwardMessage.fitWidth, replyMessage.fitWidth, chatContent.bestWidth ))
property alias filesBestWidth: chatContent.filesBestWidth
property alias filesCount: chatContent.filesCount
property alias textsBestWidth: chatContent.textsBestWidth
property alias textsCount: chatContent.textsCount
signal isFileHoveringChanged(bool isFileHovering)
signal lastTextSelectedChanged(string lastTextSelected)
signal rightClicked()
signal conferenceIcsCopied()
signal goToMessage(var message)
spacing: 0
ChatForwardMessage{
id: forwardMessage
mainChatMessageModel: mainItem.chatMessageModel
visible: mainChatMessageModel && mainChatMessageModel.isForward
availableWidth: mainItem.availableWidth
}
ChatReplyMessage{
id: replyMessage
z: 1
mainChatMessageModel: mainItem.chatMessageModel
visible: mainChatMessageModel && mainChatMessageModel.isReply
availableWidth: mainItem.availableWidth
onGoToMessage: mainItem.goToMessage(message)
}
ChatContent{
id: chatContent
chatMessageModel: mainItem.chatMessageModel
availableWidth: mainItem.availableWidth
width: parent.width
onIsFileHoveringChanged: mainItem.isFileHoveringChanged(isFileHovering)
onLastTextSelectedChanged: mainItem.lastTextSelectedChanged(lastTextSelected)
onRightClicked: mainItem.rightClicked()
onConferenceIcsCopied: mainItem.conferenceIcsCopied()
}
}

View file

@ -23,16 +23,18 @@ Item {
id: mainItem
property ChatMessageModel chatMessageModel
property ChatMessageModel mainChatMessageModel
property int maxWidth : parent.width
property int availableWidth : parent.width
property int headerHeight: ChatReplyMessageStyle.header.replyIcon.iconSize
property int replyHeight: (chatMessageModel ? replyMessage.height + usernameReplied.implicitHeight + ChatStyle.entry.message.padding * 3 + 3 : 0)
property int fitWidth: visible ? Math.max(usernameReplied.implicitWidth + replyMessage.fitWidth , headerArea.fitWidth) + 7 + ChatReplyMessageStyle.padding * 2 : 0
//property int replyHeight: (chatMessageModel ? replyMessage.height + usernameReplied.implicitHeight + ChatStyle.entry.message.padding * 3 + 3 : 0)
//property int fitWidth: visible ? Math.max(usernameReplied.implicitWidth + replyMessage.fitWidth , headerArea.fitWidth) + 7 + ChatReplyMessageStyle.padding * 2 : 0
property int replyHeight: (chatMessageModel ? chatContent.height + usernameReplied.implicitHeight + ChatStyle.entry.message.padding * 3 + 3 : 0)
property int fitWidth: visible ? Math.max(usernameReplied.implicitWidth, chatContent.bestWidth , headerArea.fitWidth) + 7 + ChatReplyMessageStyle.padding * 2 : 0
property int fitHeight: visible ? headerHeight + replyHeight : 0
property font customFont : SettingsModel.textMessageFont
visible: mainChatMessageModel && mainChatMessageModel.isReply
width: maxWidth < 0 || maxWidth > fitWidth ? fitWidth : maxWidth
width: availableWidth < 0 || availableWidth > fitWidth ? fitWidth : availableWidth
height: fitHeight
onMainChatMessageModelChanged: if( mainChatMessageModel && mainChatMessageModel.replyChatMessageModel) chatMessageModel = mainChatMessageModel.replyChatMessageModel
@ -80,15 +82,25 @@ Item {
Layout.bottomMargin: ChatStyle.entry.message.padding
Layout.leftMargin: 10
Layout.rightMargin: 10
clip: true
Rectangle{
id: colorBar
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
width: 7
width: 15
radius: 8
color: chatMessageModel && chatMessageModel.isOutgoing ? ChatReplyMessageStyle.replyArea.outgoingMarkColor.color : ChatReplyMessageStyle.replyArea.incomingMarkColor.color
Rectangle{
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
width: 5
color: ChatReplyMessageStyle.replyArea.backgroundColor.color
}
}
radius: 5
radius: 8
color: ChatReplyMessageStyle.replyArea.backgroundColor.color
visible: chatMessageModel != undefined
Text{
@ -98,7 +110,6 @@ Item {
anchors.right: parent.right
anchors.topMargin: 3
leftPadding: 2 * ChatStyle.entry.message.padding
text: mainChatMessageModel && mainChatMessageModel.fromDisplayNameReplyMessage
@ -108,48 +119,20 @@ Item {
color: ChatReplyMessageStyle.replyArea.foregroundColor.color
}
ScrollableListView {
id: replyMessage
property int fitWidth : 0
hideScrollBars: true
ChatContent{
id: chatContent
anchors.top: usernameReplied.bottom
anchors.left: parent.left
chatMessageModel: mainItem.chatMessageModel
availableWidth: mainItem.availableWidth
anchors.left: colorBar.right
anchors.right: parent.right
anchors.topMargin: 3
anchors.leftMargin: 5
interactive: false
clip: false
useTextColor: true
textColor: ChatReplyMessageStyle.replyArea.foregroundColor.color
function updateWidth(){
var maxWidth = 0
for(var child in replyMessage.contentItem.children) {
var a = replyMessage.contentItem.children[child].fitWidth
if(a)
maxWidth = Math.max(maxWidth,a)
}
fitWidth = maxWidth
}
model: ContentProxyModel{
chatMessageModel: mainItem.chatMessageModel
}
onContentHeightChanged: Qt.callLater( function(){replyMessage.height = replyMessage.contentHeight})
delegate: ChatContent{
contentModel: $modelData
textColor: ChatReplyMessageStyle.replyArea.foregroundColor.color
onFitWidthChanged:{
replyMessage.updateWidth()
}
Rectangle{
anchors.left: parent.left
anchors.right: parent.right
color: ChatStyle.entry.separator.colorModel.color
height: visible ? ChatStyle.entry.separator.width : 0
visible: (index !== (replyMessage.count - 1))
}
}
fileBackgroundRadius:5
fileBackgroundColor: ChatReplyMessageStyle.replyArea.fileBackgroundColor.color
fileBorderWidth:1
}
}
}

View file

@ -17,8 +17,9 @@ import 'Chat.js' as Logic
Rectangle{
id: replyPreviewBlock
property ChatRoomModel chatRoomModel
property ChatMessageModel replyModel: chatRoomModel ? chatRoomModel.reply : null
property int maxHeight : parent.maxHeight
Layout.preferredHeight: visible ? Math.min(messageContentsList.height + replyPreviewHeaderArea.implicitHeight + 15, replyPreviewBlock.maxHeight) : 0
Layout.preferredHeight: visible ? Math.min(messageContents.height + replyPreviewHeaderArea.implicitHeight + 15, replyPreviewBlock.maxHeight) : 0
property int leftMargin: 10
property int rightMargin: 10
@ -71,33 +72,19 @@ Rectangle{
Flickable {
id: replyPreviewTextArea
ScrollBar.vertical: ForceScrollBar {visible: replyPreviewTextArea.height < messageContentsList.height}
ScrollBar.vertical: ForceScrollBar {visible: replyPreviewTextArea.height < messageContents.height}
boundsBehavior: Flickable.StopAtBounds
contentHeight: messageContentsList.height
contentHeight: messageContents.height
contentWidth: width - ScrollBar.vertical.width
flickableDirection: Flickable.VerticalFlick
clip: true
Layout.fillHeight: true
Layout.fillWidth: true
ListView {
id: messageContentsList
anchors.left: parent.left
anchors.right: parent.right
model: ContentProxyModel{
chatMessageModel: replyPreviewBlock.chatRoomModel && replyPreviewBlock.chatRoomModel.reply
}
height: contentHeight
clip: true
delegate: ChatContent{
contentModel: $modelData
Rectangle{
anchors.left: parent.left
anchors.right: parent.right
color: ChatStyle.entry.separator.colorModel.color
height: visible ? ChatStyle.entry.separator.width : 0
visible: (index !== (messageContentsList.count - 1))
}
}
ChatContent{
id: messageContents
width: replyPreviewTextArea.contentWidth
chatMessageModel: replyPreviewBlock.replyModel
availableWidth: parent.width
}
}
}

View file

@ -23,8 +23,8 @@ TextEdit {
property ContentModel contentModel
property string lastTextSelected : ''
property font customFont : SettingsModel.textMessageFont
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
property int fitHeight: contentHeight + padding + 8
property int fitWidth: implicitWidth + 2 // add 2 because there is a bug on border that lead to not fit text exactly
signal rightClicked()
@ -32,8 +32,8 @@ TextEdit {
property int removeWarningFromBindingLoop : implicitWidth // Just a dummy variable to remove meaningless binding loop on implicitWidth
height: fitHeight
width: parent.width
visible: contentModel && contentModel.isText()
width: parent && parent.width || 1
visible: contentModel// && contentModel.isText()
clip: false
padding: ChatStyle.entry.message.padding
textMargin: 0

View file

@ -12,6 +12,9 @@ Row {
id: mainItem
property QtObject iconData
property string translation
property bool isHovering : false
property bool isTopGrouped: false
property bool isBottomGrouped: false
Component.onCompleted: {
if ($chatEntry.status == LinphoneEnums.CallStatusSuccess) {
if(!$chatEntry.isStart){

View file

@ -12,6 +12,10 @@ RowLayout {
Layout.fillWidth: true
property alias isHovering: message.isHovering
property alias isTopGrouped: message.isTopGrouped
property alias isBottomGrouped: message.isBottomGrouped
signal copyAllDone()
signal copySelectionDone()
signal replyClicked()
@ -66,16 +70,17 @@ RowLayout {
Message {
id: message
onCopyAllDone: parent.copyAllDone()
onCopySelectionDone: parent.copySelectionDone()
onReplyClicked: parent.replyClicked()
onForwardClicked: parent.forwardClicked()
onGoToMessage: parent.goToMessage(message)
onConferenceIcsCopied: parent.conferenceIcsCopied()
onAddContactClicked: parent.addContactClicked(contactAddress)
onViewContactClicked: parent.viewContactClicked(contactAddress)
onCopyAllDone: mainRow.copyAllDone()
onCopySelectionDone: mainRow.copySelectionDone()
onReplyClicked: mainRow.replyClicked()
onForwardClicked: mainRow.forwardClicked()
onGoToMessage: mainRow.goToMessage(message)
onConferenceIcsCopied: mainRow.conferenceIcsCopied()
onAddContactClicked: mainRow.addContactClicked(contactAddress)
onViewContactClicked: mainRow.viewContactClicked(contactAddress)
Layout.fillWidth: true
Layout.rightMargin: 10
// Not a style. Workaround to avoid a 0 width.
// Arbitrary value.

View file

@ -1,5 +1,6 @@
import QtQuick 2.7
import QtQuick.Layouts 1.3
import QtQml.Models 2.15
import Clipboard 1.0
import Common 1.0
@ -16,6 +17,7 @@ import LinphoneEnums 1.0
import ColorsList 1.0
import 'Message.js' as Logic
import 'qrc:/ui/scripts/Utils/utils.js' as Utils
// =============================================================================
@ -25,8 +27,10 @@ Item {
// ---------------------------------------------------------------------------
property alias backgroundColorModel: rectangle.colorModel
property bool isHovering : false
default property alias _content: content.data
property bool isTopGrouped: false
property bool isBottomGrouped: false
// ---------------------------------------------------------------------------
@ -41,94 +45,52 @@ Item {
// ---------------------------------------------------------------------------
property string lastTextSelected
implicitHeight: (deliveryLayout.visible? deliveryLayout.height : 0) +(ephemeralTimerRow.visible? 16 : 0) + messageData.height
implicitHeight: (deliveryLayout.visible? deliveryLayout.height : 0) +(ephemeralTimerRow.visible? 16 : 0) + chatContent.height
Rectangle {
id: rectangle
property int maxWidth: parent.width
property int dataWidth: maxWidth
property int availableWidth: parent.width
property bool ephemeral : $chatEntry.isEphemeral
property var colorModel:{'color': 'transparent'}
function updateWidth(){
var maxWidth = Math.max(forwardMessage.fitWidth, replyMessage.fitWidth)
for(var child in messageContentsList.contentItem.children) {
var a = messageContentsList.contentItem.children[child].fitWidth
if(a)
maxWidth = Math.max(maxWidth,a)
}
rectangle.dataWidth = maxWidth
}
anchors.left: !$chatEntry.isOutgoing ? parent.left : undefined
anchors.right: $chatEntry.isOutgoing ? parent.right : undefined
height: parent.height - (deliveryLayout.visible? deliveryLayout.height : 0)
radius: ChatStyle.entry.message.radius
clip: false
color: colorModel.color
width: (
ephemeralTimerRow.visible && dataWidth < ephemeralTimerRow.width + 2*ChatStyle.entry.message.padding
width: (//implicitWidth
ephemeralTimerRow.visible && (chatContent.bestWidth < ephemeralTimerRow.width + 2*ChatStyle.entry.message.padding)
? ephemeralTimerRow.width + 2*ChatStyle.entry.message.padding
: Math.min(dataWidth, maxWidth)
: Math.min(chatContent.bestWidth, availableWidth)
)
// ---------------------------------------------------------------------------
// Message.
// ---------------------------------------------------------------------------
Column{
id: messageData
Rectangle{
visible: container.isTopGrouped || container.isBottomGrouped
color: parent.color
anchors.left: !$chatEntry.isOutgoing ? parent.left : undefined
anchors.right: $chatEntry.isOutgoing ? parent.right : undefined
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.topMargin: container.isTopGrouped ? 0 : parent.radius
anchors.bottomMargin: container.isBottomGrouped ? 0 : parent.radius
width: parent.radius
}
ChatFullContent{
id: chatContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
ChatForwardMessage{
id: forwardMessage
mainChatMessageModel: $chatEntry
visible: $chatEntry.isForward
maxWidth: container.width
onFitWidthChanged:{
rectangle.updateWidth()
}
}
ChatReplyMessage{
id: replyMessage
z: 1
mainChatMessageModel: $chatEntry
visible: $chatEntry.isReply
maxWidth: container.width
onFitWidthChanged:{
rectangle.updateWidth()
}
onGoToMessage: container.goToMessage(message)
}
ListView {
id: messageContentsList
anchors.left: parent.left
anchors.right: parent.right
visible: count > 0
spacing: 0
clip: false
model: ContentProxyModel{
chatMessageModel: $chatEntry
}
height: contentHeight
boundsBehavior: Flickable.StopAtBounds
interactive: false
delegate:
ChatContent{
maxWidth: container.width
contentModel: $modelData
onFitWidthChanged:{
rectangle.updateWidth()
}
onLastTextSelectedChanged: container.lastTextSelected= lastTextSelected
onRightClicked: chatMenu.open()
onConferenceIcsCopied: container.conferenceIcsCopied()
onFileIsHoveringChanged: menuButton.visible = !fileIsHovering
Rectangle{
anchors.left: parent.left
anchors.right: parent.right
color: ChatStyle.entry.separator.colorModel.color
height: visible ? ChatStyle.entry.separator.width : 0
visible: (index !== (messageContentsList.count - 1))
}
}
}
chatMessageModel: $chatEntry
availableWidth: rectangle.availableWidth
onLastTextSelectedChanged: container.lastTextSelected= lastTextSelected
onGoToMessage: container.goToMessage(message)
onRightClicked: chatMenu.open()
onConferenceIcsCopied: container.conferenceIcsCopied()
onIsFileHoveringChanged: menuButton.visible = !isFileHovering
}
Row{
id:ephemeralTimerRow
@ -158,7 +120,6 @@ Item {
}
}
}
// ---------------------------------------------------------------------------
// Extra content.
// ---------------------------------------------------------------------------
@ -168,8 +129,10 @@ Item {
anchors {
left: rectangle.right
bottom: rectangle.bottom
leftMargin: ChatStyle.entry.message.extraContent.leftMargin
}
}
ChatDeliveries{
id: deliveryLayout
@ -193,7 +156,7 @@ Item {
backgroundRadius: 8
colorSet : ChatStyle.entry.menu
visible: isHoverEntry()
visible: container.isHovering
onClicked: chatMenu.open()
}

View file

@ -95,6 +95,9 @@ RowLayout{
property color eventColor : (isError ? ChatStyle.entry.event.notice.errorColor.color
: ( isImportant ? ChatStyle.entry.event.notice.importantColor.color
: ChatStyle.entry.event.notice.colorModel.color ))
property bool isHovering : false
property bool isTopGrouped: false
property bool isBottomGrouped: false
Layout.preferredHeight: ChatStyle.entry.lineHeight
spacing: ChatStyle.entry.message.extraContent.spacing

View file

@ -11,9 +11,15 @@ import Utils 1.0
// =============================================================================
Item {
id: mainItem
implicitHeight: message.height
//width: parent.width
Layout.fillWidth: true
//onWidthChanged: console.log(width)
property alias isHovering: message.isHovering
property alias isTopGrouped: message.isTopGrouped
property alias isBottomGrouped: message.isBottomGrouped
signal copyAllDone()
signal copySelectionDone()
@ -23,64 +29,91 @@ Item {
signal conferenceIcsCopied()
signal addContactClicked(string contactAddress)
signal viewContactClicked(string contactAddress)
Message {
id: message
onCopyAllDone: parent.copyAllDone()
onCopySelectionDone: parent.copySelectionDone()
onReplyClicked: parent.replyClicked()
onForwardClicked: parent.forwardClicked()
onGoToMessage: parent.goToMessage(message)
onConferenceIcsCopied: parent.conferenceIcsCopied()
onAddContactClicked: parent.addContactClicked(contactAddress)
onViewContactClicked: parent.viewContactClicked(contactAddress)
RowLayout{
/*
anchors {
left: parent.left
leftMargin: ChatStyle.entry.metaWidth
right: parent.right
left: parent.left
//leftMargin: ChatStyle.entry.metaWidth
right: parent.right
}
*/
//width: parent.width
anchors.fill: parent
//onWidthChanged: console.log(width)
spacing: 0
//spacing: ChatStyle.entry.message.extraContent.spacing
Message {
id: message
onCopyAllDone: mainItem.copyAllDone()
onCopySelectionDone: mainItem.copySelectionDone()
onReplyClicked: mainItem.replyClicked()
onForwardClicked: mainItem.forwardClicked()
onGoToMessage: mainItem.goToMessage(message)
onConferenceIcsCopied: mainItem.conferenceIcsCopied()
onAddContactClicked: mainItem.addContactClicked(contactAddress)
onViewContactClicked: mainItem.viewContactClicked(contactAddress)
/*
anchors {
left: parent.left
leftMargin: ChatStyle.entry.metaWidth
right: parent.right
}*/
backgroundColorModel: ChatStyle.entry.message.outgoing.backgroundColor
Layout.fillWidth: true
//Layout.fillHeight: true
Layout.leftMargin: 10
//onImplicitHeightChanged: Layout.preferredHeight= implicitHeight
Layout.minimumHeight: implicitHeight // Avoid bug where UI is not computed by Qt
Layout.preferredHeight: implicitHeight
//Layout.preferredWidth: parent.width
//width: parent.width
// Not a style. Workaround to avoid a 0 width.
// Arbitrary value.
Layout.minimumWidth: 1
//onWidthChanged: console.log(width)
}
backgroundColorModel: ChatStyle.entry.message.outgoing.backgroundColor
width: parent.width
Row {
/*
RowLayout {
anchors.fill: parent
anchors.leftMargin: ChatStyle.entry.message.extraContent.leftMargin
spacing: ChatStyle.entry.message.extraContent.spacing
*/
Component {
id: iconComponent
Icon {
id: iconId
readonly property var isError: Utils.includes([
LinphoneEnums.ChatMessageStateFileTransferError,
LinphoneEnums.ChatMessageStateNotDelivered,
], $chatEntry.state)
readonly property bool isUploaded: $chatEntry.state == LinphoneEnums.ChatMessageStateDelivered
readonly property bool isDelivered: $chatEntry.state == LinphoneEnums.ChatMessageStateDeliveredToUser
readonly property bool isRead: $chatEntry.state == LinphoneEnums.ChatMessageStateDisplayed
icon: iconId.isError
? 'chat_error'
: (iconId.isRead ? 'chat_read' : (iconId.isDelivered ? 'chat_delivered' : '' ) )
iconSize: ChatStyle.entry.message.outgoing.sendIconSize
MouseArea {
id:retryAction
anchors.fill: parent
visible: iconId.isError || $chatEntry.state == LinphoneEnums.ChatMessageStateIdle
onClicked: $chatEntry.resendMessage()
}
TooltipArea {
id:tooltip
visible: text != ''
text: iconId.isError
? qsTr('messageError')
: (iconId.isRead ? qsTr('messageRead') : (isDelivered ? qsTr('messageDelivered') : ''))
hoveringCursor : retryAction.visible?Qt.PointingHandCursor:Qt.ArrowCursor
Item{
Icon {
id: iconId
readonly property var isError: Utils.includes([
LinphoneEnums.ChatMessageStateFileTransferError,
LinphoneEnums.ChatMessageStateNotDelivered,
], $chatEntry.state)
readonly property bool isUploaded: $chatEntry.state == LinphoneEnums.ChatMessageStateDelivered
readonly property bool isDelivered: $chatEntry.state == LinphoneEnums.ChatMessageStateDeliveredToUser
readonly property bool isRead: $chatEntry.state == LinphoneEnums.ChatMessageStateDisplayed
icon: iconId.isError
? 'chat_error'
: (iconId.isRead ? 'chat_read' : (iconId.isDelivered ? 'chat_delivered' : '' ) )
iconSize: ChatStyle.entry.message.outgoing.sendIconSize
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
MouseArea {
id:retryAction
anchors.fill: parent
visible: iconId.isError || $chatEntry.state == LinphoneEnums.ChatMessageStateIdle
onClicked: $chatEntry.resendMessage()
}
TooltipArea {
id:tooltip
visible: text != ''
text: iconId.isError
? qsTr('messageError')
: (iconId.isRead ? qsTr('messageRead') : (iconId.isDelivered ? qsTr('messageDelivered') : ''))
hoveringCursor : retryAction.visible?Qt.PointingHandCursor:Qt.ArrowCursor
}
}
}
}
@ -89,8 +122,6 @@ Item {
id: indicator
Item {
anchors.fill: parent
BusyIndicator {
anchors.centerIn: parent
@ -101,13 +132,23 @@ Item {
}
Loader {
height: ChatStyle.entry.lineHeight
width: ChatStyle.entry.message.outgoing.areaSize
//height: ChatStyle.entry.lineHeight
//anchors.bottom: parent.bottom
//Layout.alignment: Qt.AlignBottom | Qt.AlignHCenter
Layout.preferredWidth: ChatStyle.entry.message.outgoing.areaSize
Layout.fillHeight: true
//Layout.rightMargin: 10
sourceComponent: $chatEntry.state == LinphoneEnums.ChatMessageStateInProgress || $chatEntry.state == LinphoneEnums.ChatMessageStateFileTransferInProgress
? indicator
: iconComponent
}
}
//}
}
/*
Rectangle{
anchors.fill: parent
color: 'yellow'
}
*/
}

View file

@ -76,12 +76,7 @@ Column {
}
Text{
id:status
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
//anchors.top:parent.top
//anchors.bottom : parent.bottom
//anchors.left:parent.right
//anchors.leftMargin:5
verticalAlignment: Text.AlignVCenter
visible: text != ''
text : ''

View file

@ -18,12 +18,14 @@ QtObject {
}
}
property QtObject replyArea: QtObject{
property var outgoingMarkColor: ColorsList.add(sectionName+'_reply_outgoing_mark', 'm')
property var incomingMarkColor: ColorsList.add(sectionName+'_reply_incoming_mark', 'r')
property var outgoingMarkColor: ColorsList.add(sectionName+'_reply_outgoing_mark', 'outgoing_reply_mark_bg')
property var incomingMarkColor: ColorsList.add(sectionName+'_reply_incoming_mark', 'incoming_reply_mark_bg')
property var backgroundColor: ColorsList.add(sectionName+'_reply_bg', 'q')
property var foregroundColor: ColorsList.add(sectionName+'_reply_fg', 'h')
property var fileBackgroundColor: ColorsList.add(sectionName+'_reply_file_bg', 'reply_file_bg')
property int usernamePointSizeOffset: -2
property int pointSizeOffset: -2
}
property int padding: 8

View file

@ -173,7 +173,7 @@ QtObject {
property QtObject message: QtObject {
property int padding: 8
property int radius: 4
property int radius: 8
property QtObject extraContent: QtObject {
property int leftMargin: 10
@ -182,7 +182,7 @@ QtObject {
}
property QtObject file: QtObject {
property int height: 80
property int height: 120
property int heightbetter: 200
property int iconSize: 18
property int margins: 8
@ -212,10 +212,11 @@ QtObject {
property string icon: 'file_extension_custom'
property string unknownIcon: 'file_unknown_custom'
property int iconSize: 60
property int radius: 5
property int radius: 0
property QtObject background: QtObject {
property var colorModel: ColorsList.add(sectionName+'_file_extension_bg', 'q')
property var borderColorModel: ColorsList.add(sectionName+'_file_extension_border', 'extension_file_border')
}
property QtObject text: QtObject {
@ -249,7 +250,7 @@ QtObject {
property QtObject incoming: QtObject {
property var backgroundColor: ColorsList.add(sectionName+'_incoming_bg', 'incoming_bg')
property int avatarSize: 20
property int avatarSize: 30
property QtObject text: QtObject {
property var colorModel: ColorsList.add(sectionName+'_incoming_text', 'd')
@ -259,9 +260,9 @@ QtObject {
property QtObject outgoing: QtObject {
property var backgroundColor: ColorsList.add(sectionName+'_outgoing_bg', 'outgoing_bg')
property int areaSize: 12
property int areaSize: 32
property int busyIndicatorSize: 12
property int sendIconSize: 60
property int sendIconSize: 12
property QtObject text: QtObject {
property var colorModel: ColorsList.add(sectionName+'_outgoing_text', 'd')

View file

@ -18,11 +18,14 @@ IncallAvatar 1.0 Calls/IncallAvatar.qml
CameraItem 1.0 Camera/CameraItem.qml
CameraView 1.0 Camera/CameraView.qml
Chat 1.0 Chat/Chat.qml
ChatAudioMessage 1.0 Chat/ChatAudioMessage.qml
ChatAudioPreview 1.0 Chat/ChatAudioPreview.qml
ChatCalendarMessage 1.0 Chat/ChatCalendarMessage.qml
ChatConferenceInvitationMessage 1.0 Chat/ChatConferenceInvitationMessage.qml
ChatContent 1.0 Chat/ChatContent.qml
ChatFullContent 1.0 Chat/ChatFullContent.qml
ChatMessagePreview 1.0 Chat/ChatMessagePreview.qml
ChatForwardMessage 1.0 Chat/ChatForwardMessage.qml
ChatReplyMessage 1.0 Chat/ChatReplyMessage.qml

View file

@ -722,5 +722,8 @@ function printObject(o) {
for (var p in o) {
out += p + ': ' + o[p] + '\n';
}
return out;
if(!o)
return 'Empty'
else
return out;
}