diff --git a/linphone-desktop/resources.qrc b/linphone-desktop/resources.qrc
index 0f6448c77..ec9e890cb 100644
--- a/linphone-desktop/resources.qrc
+++ b/linphone-desktop/resources.qrc
@@ -267,6 +267,7 @@
ui/modules/Linphone/Calls/CallControls.qml
ui/modules/Linphone/Calls/Calls.qml
ui/modules/Linphone/CardBlock.qml
+ ui/modules/Linphone/Chat/Chat.js
ui/modules/Linphone/Chat/Chat.qml
ui/modules/Linphone/Chat/Event.qml
ui/modules/Linphone/Chat/FileMessage.qml
diff --git a/linphone-desktop/ui/modules/Linphone/Chat/Chat.js b/linphone-desktop/ui/modules/Linphone/Chat/Chat.js
new file mode 100644
index 000000000..95a76ece2
--- /dev/null
+++ b/linphone-desktop/ui/modules/Linphone/Chat/Chat.js
@@ -0,0 +1,80 @@
+// =============================================================================
+// `Chat.qml` Logic.
+// =============================================================================
+
+function initView () {
+ chat.tryToLoadMoreEntries = false
+ chat.bindToEnd = true
+ chat.positionViewAtEnd()
+}
+
+function loadMoreEntries () {
+ if (chat.atYBeginning && !chat.tryToLoadMoreEntries) {
+ chat.tryToLoadMoreEntries = true
+ chat.positionViewAtBeginning()
+ proxyModel.loadMoreEntries()
+ }
+}
+
+function getComponentFromEntry (chatEntry) {
+ if (chatEntry.fileName) {
+ return 'FileMessage.qml'
+ }
+
+ if (chatEntry.type === ChatModel.CallEntry) {
+ return 'Event.qml'
+ }
+
+ return chatEntry.isOutgoing ? 'OutgoingMessage.qml' : 'IncomingMessage.qml'
+}
+
+function handleCurrentItemChanged (currentItem) {
+ // Go to last item only!
+ if (!chat.bindToEnd || !currentItem || chat.currentIndex + 1 !== chat.count) {
+ return
+ }
+
+ var chatHeight = chat.height
+ var messageY = currentItem.mapToItem(chat.contentItem, 0, 0).y
+ var messageHeight = currentItem.height
+ console.log(chat.contentY, chatHeight, messageY)
+ if (chat.contentY <= messageY) {
+ chat.contentY = messageY
+ }
+ chat.currentIndex = -1
+}
+
+function handleFilesDropped (files) {
+ chat.bindToEnd = true
+ files.forEach(proxyModel.sendFileMessage)
+}
+
+function handleMoreEntriesLoaded (n) {
+ chat.positionViewAtIndex(n - 1, ListView.Beginning)
+ chat.tryToLoadMoreEntries = false
+}
+
+function handleMovementEnded () {
+ if (chat.atYEnd) {
+ chat.bindToEnd = true
+ }
+}
+
+function handleMovementStarted () {
+ chat.bindToEnd = false
+}
+
+function handleDataChanged (_, bottomRight) {
+ var n = chat.count
+ var index = bottomRight.row
+
+ if (chat.bindToEnd && index + 1 === n) {
+ chat.currentIndex = index
+ }
+}
+
+function sendMessage (text) {
+ textArea.text = ''
+ chat.bindToEnd = true
+ proxyModel.sendMessage(text)
+}
diff --git a/linphone-desktop/ui/modules/Linphone/Chat/Chat.qml b/linphone-desktop/ui/modules/Linphone/Chat/Chat.qml
index 1a1dc63b7..728021ffd 100644
--- a/linphone-desktop/ui/modules/Linphone/Chat/Chat.qml
+++ b/linphone-desktop/ui/modules/Linphone/Chat/Chat.qml
@@ -5,16 +5,14 @@ import QtQuick.Layouts 1.3
import Common 1.0
import Linphone 1.0
import Linphone.Styles 1.0
-import Utils 1.0
+
+import 'Chat.js' as Logic
// =============================================================================
Rectangle {
property alias proxyModel: chat.model
- property bool _bindToEnd: false
- property var _contactObserver: SipAddressesModel.getContactObserver(proxyModel.sipAddress)
-
// ---------------------------------------------------------------------------
signal messageToSend (string text)
@@ -32,30 +30,17 @@ Rectangle {
// -----------------------------------------------------------------------
- property bool _tryToLoadMoreEntries: true
-
- // -----------------------------------------------------------------------
-
- function _loadMoreEntries () {
- if (atYBeginning && !_tryToLoadMoreEntries) {
- _tryToLoadMoreEntries = true
- positionViewAtBeginning()
- proxyModel.loadMoreEntries()
- }
- }
-
- function _initView () {
- _tryToLoadMoreEntries = false
- _bindToEnd = true
-
- positionViewAtEnd()
- }
+ property bool bindToEnd: false
+ property bool tryToLoadMoreEntries: true
+ property var contactObserver: SipAddressesModel.getContactObserver(proxyModel.sipAddress)
// -----------------------------------------------------------------------
Layout.fillHeight: true
Layout.fillWidth: true
+ highlightFollowsCurrentItem: false
+
section {
criteria: ViewSection.FullString
delegate: sectionHeading
@@ -64,32 +49,12 @@ Rectangle {
// -----------------------------------------------------------------------
- Component.onCompleted: {
- function goToEnd () {
- return Utils.setTimeout(chat, 100, function () {
- if (_bindToEnd) {
- positionViewAtEnd()
- }
+ Component.onCompleted: Logic.initView()
- return goToEnd()
- })
- }
- goToEnd()
-
- // First render.
- _initView()
- }
-
- // -----------------------------------------------------------------------
-
- onMovementStarted: _bindToEnd = false
- onMovementEnded: {
- if (atYEnd) {
- _bindToEnd = true
- }
- }
-
- onContentYChanged: _loadMoreEntries()
+ onContentYChanged: Logic.loadMoreEntries()
+ onCurrentItemChanged: Logic.handleCurrentItemChanged(currentItem)
+ onMovementEnded: Logic.handleMovementEnded()
+ onMovementStarted: Logic.handleMovementStarted()
// -----------------------------------------------------------------------
@@ -99,12 +64,9 @@ Rectangle {
// When the view is changed (for example `Calls` -> `Messages`),
// the position is set at end and it can be possible to load
// more entries.
- onEntryTypeFilterChanged: chat._initView()
-
- onMoreEntriesLoaded: {
- chat.positionViewAtIndex(n - 1, ListView.Beginning)
- chat._tryToLoadMoreEntries = false
- }
+ onEntryTypeFilterChanged: Logic.initView()
+ onMoreEntriesLoaded: Logic.handleMoreEntriesLoaded(n)
+ onDataChanged: Logic.handleDataChanged(topLeft, bottomRight)
}
// -----------------------------------------------------------------------
@@ -171,45 +133,18 @@ Rectangle {
leftMargin: ChatStyle.entry.leftMargin
right: parent ? parent.right : undefined
- // Ugly. I admit it, but it exists a problem, without these
- // lines the extra content message is truncated.
- // I have no other solution at this moment with `anchors`
- // properties... The messages use the `implicitWidth/Height`
- // and `width/Height` attrs and is not easy to found a fix.
rightMargin: ChatStyle.entry.deleteIconSize +
ChatStyle.entry.message.extraContent.spacing +
ChatStyle.entry.message.extraContent.rightMargin +
ChatStyle.entry.message.extraContent.leftMargin +
ChatStyle.entry.message.outgoing.sendIconSize
}
+
color: ChatStyle.color
implicitHeight: layout.height + ChatStyle.entry.bottomMargin
// ---------------------------------------------------------------------
- // Avoid the use of explicit qrc paths.
- Component {
- id: event
- Event {}
- }
-
- Component {
- id: incomingMessage
- IncomingMessage {}
- }
-
- Component {
- id: outgoingMessage
- OutgoingMessage {}
- }
-
- Component {
- id: fileMessage
- FileMessage {}
- }
-
- // ---------------------------------------------------------------------
-
MouseArea {
id: mouseArea
@@ -228,29 +163,22 @@ Rectangle {
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: ChatStyle.entry.lineHeight
Layout.preferredWidth: ChatStyle.entry.time.width
+
color: ChatStyle.entry.time.color
font.pointSize: ChatStyle.entry.time.fontSize
+
text: $chatEntry.timestamp.toLocaleString(
Qt.locale(App.locale),
'hh:mm'
)
+
verticalAlignment: Text.AlignVCenter
}
// Display content.
Loader {
Layout.fillWidth: true
- sourceComponent: {
- if ($chatEntry.fileName) {
- return fileMessage
- }
-
- if ($chatEntry.type === ChatModel.CallEntry) {
- return event
- }
-
- return $chatEntry.isOutgoing ? outgoingMessage : incomingMessage
- }
+ source: Logic.getComponentFromEntry($chatEntry)
}
}
}
@@ -263,27 +191,22 @@ Rectangle {
Borders {
Layout.fillWidth: true
- Layout.preferredHeight: ChatStyle.sendArea.height +
- ChatStyle.sendArea.border.width
+ Layout.preferredHeight: ChatStyle.sendArea.height + ChatStyle.sendArea.border.width
+
borderColor: ChatStyle.sendArea.border.color
topWidth: ChatStyle.sendArea.border.width
DroppableTextArea {
+ id: textArea
+
anchors.fill: parent
+
dropEnabled: SettingsModel.fileTransferUrl.length > 0
dropDisabledReason: qsTr('noFileTransferUrl')
placeholderText: qsTr('newMessagePlaceholder')
- onDropped: {
- _bindToEnd = true
- files.forEach(proxyModel.sendFileMessage)
- }
-
- onValidText: {
- this.text = ''
- _bindToEnd = true
- proxyModel.sendMessage(text)
- }
+ onDropped: Logic.handleFilesDropped(files)
+ onValidText: Logic.sendMessage(text)
}
}
}
diff --git a/linphone-desktop/ui/modules/Linphone/Chat/FileMessage.qml b/linphone-desktop/ui/modules/Linphone/Chat/FileMessage.qml
index c86569c9d..cad3ad97e 100644
--- a/linphone-desktop/ui/modules/Linphone/Chat/FileMessage.qml
+++ b/linphone-desktop/ui/modules/Linphone/Chat/FileMessage.qml
@@ -27,8 +27,8 @@ Row {
height: ChatStyle.entry.message.incoming.avatarSize
width: ChatStyle.entry.message.incoming.avatarSize
- image: _contactObserver.contact ? _contactObserver.contact.avatar : ''
- username: LinphoneUtils.getContactUsername(_contactObserver.contact || proxyModel.sipAddress)
+ image: chat.contactObserver.contact ? chat.contactObserver.contact.avatar : ''
+ username: LinphoneUtils.getContactUsername(chat.contactObserver.contact || proxyModel.sipAddress)
}
}
diff --git a/linphone-desktop/ui/modules/Linphone/Chat/IncomingMessage.qml b/linphone-desktop/ui/modules/Linphone/Chat/IncomingMessage.qml
index d33d343a0..e5a4ce76a 100644
--- a/linphone-desktop/ui/modules/Linphone/Chat/IncomingMessage.qml
+++ b/linphone-desktop/ui/modules/Linphone/Chat/IncomingMessage.qml
@@ -20,8 +20,8 @@ RowLayout {
Avatar {
anchors.centerIn: parent
height: ChatStyle.entry.message.incoming.avatarSize
- image: _contactObserver.contact ? _contactObserver.contact.avatar : ''
- username: LinphoneUtils.getContactUsername(_contactObserver.contact || proxyModel.sipAddress)
+ image: chat.contactObserver.contact ? chat.contactObserver.contact.avatar : ''
+ username: LinphoneUtils.getContactUsername(chat.contactObserver.contact || proxyModel.sipAddress)
width: ChatStyle.entry.message.incoming.avatarSize
// The avatar is only visible for the first message of a incoming messages sequence.
diff --git a/linphone-desktop/ui/modules/Linphone/Chat/Message.js b/linphone-desktop/ui/modules/Linphone/Chat/Message.js
index cc5f2b032..e4d41cc40 100644
--- a/linphone-desktop/ui/modules/Linphone/Chat/Message.js
+++ b/linphone-desktop/ui/modules/Linphone/Chat/Message.js
@@ -1,25 +1,29 @@
+// =============================================================================
+// `Message.qml` Logic.
+// =============================================================================
+
// See: `ensureVisible` on http://doc.qt.io/qt-5/qml-qtquick-textedit.html
function ensureVisible (cursor) {
- // Case 1: No focused.
- if (!message.activeFocus) {
- return
- }
+ // Case 1: No focused.
+ if (!message.activeFocus) {
+ return
+ }
- // Case 2: Scroll up.
- var contentItem = chat.contentItem
- var contentY = chat.contentY
- var messageY = message.mapToItem(contentItem, 0, 0).y + cursor.y
+ // Case 2: Scroll up.
+ var contentItem = chat.contentItem
+ var contentY = chat.contentY
+ var messageY = message.mapToItem(contentItem, 0, 0).y + cursor.y
- if (contentY >= messageY) {
- chat.contentY = messageY
- return
- }
+ if (contentY >= messageY) {
+ chat.contentY = messageY
+ return
+ }
- // Case 3: Scroll down.
- var chatHeight = chat.height
- var cursorHeight = cursor.height
+ // Case 3: Scroll down.
+ var chatHeight = chat.height
+ var cursorHeight = cursor.height
- if (contentY + chatHeight <= messageY + cursorHeight) {
- chat.contentY = messageY + cursorHeight - chatHeight
- }
+ if (contentY + chatHeight <= messageY + cursorHeight) {
+ chat.contentY = messageY + cursorHeight - chatHeight
+ }
}
diff --git a/linphone-desktop/ui/modules/Linphone/Chat/Message.qml b/linphone-desktop/ui/modules/Linphone/Chat/Message.qml
index cad6d2135..64a138649 100644
--- a/linphone-desktop/ui/modules/Linphone/Chat/Message.qml
+++ b/linphone-desktop/ui/modules/Linphone/Chat/Message.qml
@@ -59,7 +59,7 @@ Item {
// See http://doc.qt.io/qt-5/qml-qtquick-text.html#textFormat-prop
// and http://doc.qt.io/qt-5/richtext-html-subset.html
textFormat: Text.RichText // To supports links and imgs.
- wrapMode: TextEdit.WordWrap
+ wrapMode: TextEdit.Wrap
onCursorRectangleChanged: Logic.ensureVisible(cursorRectangle)
onLinkActivated: Qt.openUrlExternally(link)
diff --git a/linphone-desktop/ui/views/App/Main/Assistant.qml b/linphone-desktop/ui/views/App/Main/Assistant.qml
index 34956c9da..7dccc13ce 100644
--- a/linphone-desktop/ui/views/App/Main/Assistant.qml
+++ b/linphone-desktop/ui/views/App/Main/Assistant.qml
@@ -9,7 +9,7 @@ import App.Styles 1.0
// =============================================================================
Item {
- id: assistant
+ id: assistant
readonly property string viewsPath: 'qrc:/ui/views/App/Main/Assistant/'
@@ -54,8 +54,8 @@ Item {
YAnimator {
duration: AssistantStyle.stackAnimation.duration
easing.type: Easing.OutBack
- from: stack.height + AssistantStyle.bottomMargin
- to: 0
+ from: stack.height + AssistantStyle.bottomMargin
+ to: 0
}
}
@@ -66,7 +66,7 @@ Item {
from: 0
to: stack.width + AssistantStyle.rightMargin
}
- }
+ }
pushEnter: Transition {
XAnimator {
@@ -81,9 +81,9 @@ Item {
YAnimator {
duration: AssistantStyle.stackAnimation.duration
easing.type: Easing.OutBack
- from: 0
- to: stack.height + AssistantStyle.bottomMargin
+ from: 0
+ to: stack.height + AssistantStyle.bottomMargin
}
- }
+ }
}
}