2018-10-23 18:52:13 +00:00
|
|
|
import QtGraphicalEffects 1.0
|
|
|
|
import QtQuick 2.7
|
|
|
|
import QtQuick.Controls 2.4
|
|
|
|
import QtQuick.Controls.Material 2.0
|
|
|
|
import QtQuick.Layouts 1.3
|
|
|
|
|
2018-10-28 02:49:14 +00:00
|
|
|
import "controls" as Awesome
|
|
|
|
import "../fonts/Twemoji.js" as T
|
|
|
|
|
2018-10-23 18:52:13 +00:00
|
|
|
ColumnLayout {
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
|
|
|
|
2018-10-29 18:00:21 +00:00
|
|
|
StackToolbar {
|
2018-10-31 05:43:22 +00:00
|
|
|
text: "open privacy exec"
|
2018-10-29 18:00:21 +00:00
|
|
|
|
|
|
|
aux.onClicked: theStack.pane = theStack.userProfilePane
|
|
|
|
}
|
|
|
|
|
|
|
|
Flickable { // THE MESSAGE LIST ITSELF
|
2018-10-23 18:52:13 +00:00
|
|
|
id: sv
|
|
|
|
clip: true
|
|
|
|
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
|
|
|
Layout.fillHeight: true
|
|
|
|
Layout.minimumWidth: parent.width
|
|
|
|
Layout.maximumWidth: parent.width
|
|
|
|
Layout.fillWidth: true
|
|
|
|
contentWidth: colMessages.width
|
|
|
|
contentHeight: colMessages.height
|
|
|
|
boundsBehavior: Flickable.StopAtBounds
|
2018-10-28 02:49:14 +00:00
|
|
|
maximumFlickVelocity: 800
|
2018-10-23 18:52:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
Connections {
|
|
|
|
target: gcd
|
|
|
|
|
|
|
|
onClearMessages: function() {
|
|
|
|
messagesModel.clear()
|
|
|
|
}
|
|
|
|
|
2018-10-29 18:00:21 +00:00
|
|
|
onAppendMessage: function(from, message, displayname, mid, ts, source) {
|
2018-10-23 18:52:13 +00:00
|
|
|
messagesModel.append({
|
|
|
|
"f": from,
|
2018-10-28 02:49:14 +00:00
|
|
|
"m": parse(message, 12),
|
2018-10-29 18:00:21 +00:00
|
|
|
"d": displayname,
|
2018-10-28 02:49:14 +00:00
|
|
|
"i": mid,
|
2018-10-29 18:00:21 +00:00
|
|
|
"t": ts,
|
|
|
|
"src": source
|
2018-10-23 18:52:13 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
if (sv.contentY + sv.height >= sv.contentHeight - colMessages.height && sv.contentHeight > sv.height) {
|
|
|
|
sv.contentY = sv.contentHeight - sv.height
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-10-28 02:49:14 +00:00
|
|
|
ScrollBar.vertical: ScrollBar{
|
|
|
|
policy: ScrollBar.AlwaysOn
|
|
|
|
}
|
|
|
|
|
|
|
|
ColumnLayout {
|
2018-10-23 18:52:13 +00:00
|
|
|
id: colMessages
|
|
|
|
width: sv.width
|
|
|
|
|
|
|
|
|
|
|
|
ListModel { // MESSAGE OBJECTS ARE STORED HERE ...
|
|
|
|
id: messagesModel
|
|
|
|
}
|
|
|
|
|
|
|
|
Item { height: 6 }
|
|
|
|
|
|
|
|
Repeater { // ... AND DISPLAYED HERE
|
|
|
|
model: messagesModel
|
|
|
|
delegate: Message {
|
|
|
|
from: f
|
|
|
|
message: m
|
2018-10-29 18:00:21 +00:00
|
|
|
displayname: d
|
2018-10-28 02:49:14 +00:00
|
|
|
messageID: i
|
|
|
|
timestamp: t
|
2018-10-29 18:00:21 +00:00
|
|
|
source: src
|
2018-10-23 18:52:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RowLayout { // THE BOTTOM DRAWER
|
|
|
|
Rectangle { // MESSAGE ENTRY TEXTFIELD
|
|
|
|
id: rectMessage
|
2018-10-31 05:43:22 +00:00
|
|
|
Layout.fillWidth: true
|
2018-10-30 19:48:37 +00:00
|
|
|
Layout.minimumHeight: 40 * zoomSlider.value
|
2018-10-31 05:43:22 +00:00
|
|
|
Layout.maximumHeight: 40 * zoomSlider.value
|
2018-10-23 18:52:13 +00:00
|
|
|
color: "#EDEDED"
|
|
|
|
border.color: "#AAAAAA"
|
|
|
|
radius: 10
|
|
|
|
|
2018-10-28 02:49:14 +00:00
|
|
|
|
2018-10-23 18:52:13 +00:00
|
|
|
Flickable {
|
|
|
|
id: flkMessage
|
2018-10-28 02:49:14 +00:00
|
|
|
anchors.fill: parent//this does nothing! bug in qt
|
2018-10-23 18:52:13 +00:00
|
|
|
Layout.minimumWidth: parent.width
|
|
|
|
Layout.maximumWidth: parent.width
|
|
|
|
Layout.minimumHeight: rectMessage.height
|
|
|
|
Layout.maximumHeight: rectMessage.height
|
|
|
|
contentWidth: txtMessage.width
|
|
|
|
contentHeight: txtMessage.height
|
|
|
|
boundsBehavior: Flickable.StopAtBounds
|
|
|
|
clip:true
|
2018-10-28 02:49:14 +00:00
|
|
|
maximumFlickVelocity: 300
|
|
|
|
|
|
|
|
|
2018-10-29 18:00:21 +00:00
|
|
|
ScrollBar.vertical: ScrollBar{}
|
2018-10-23 18:52:13 +00:00
|
|
|
|
|
|
|
TextEdit {
|
|
|
|
id: txtMessage
|
|
|
|
font.pixelSize: 10
|
|
|
|
text: ""
|
|
|
|
padding: 6
|
|
|
|
wrapMode: TextEdit.Wrap
|
2018-10-28 02:49:14 +00:00
|
|
|
textFormat: Text.RichText
|
2018-10-23 18:52:13 +00:00
|
|
|
width: rectMessage.width
|
|
|
|
//height: parent.height
|
|
|
|
|
2018-10-28 02:49:14 +00:00
|
|
|
property bool skipOneUpdate: false
|
|
|
|
|
2018-10-23 18:52:13 +00:00
|
|
|
Keys.onReturnPressed: { // CTRL+ENTER = LINEBREAK
|
|
|
|
if (event.modifiers & Qt.ControlModifier) {
|
2018-10-29 18:00:21 +00:00
|
|
|
txtMessage.insert(txtMessage.cursorPosition, "<br>")
|
2018-10-23 18:52:13 +00:00
|
|
|
} else if (event.modifiers == Qt.NoModifier) {
|
|
|
|
btnSend.clicked()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-28 02:49:14 +00:00
|
|
|
// welcome to the emoji parser! it is horrifying code that needs to leave in <img src="[emoji]">
|
|
|
|
// while also stripping any other tag, including other images.
|
|
|
|
// TODO: this probably breaks if people actually do want to paste html
|
2018-10-23 18:52:13 +00:00
|
|
|
onTextChanged: {
|
2018-10-29 18:00:21 +00:00
|
|
|
//console.log("onTextChanged()")
|
2018-10-28 02:49:14 +00:00
|
|
|
|
|
|
|
// we're taking advantage of TextEdit.getText()'s parsing capability, which means occasionally
|
|
|
|
// passing text into it to be filtered. this prevents recursive calls putting us into an
|
|
|
|
// infinite loop
|
|
|
|
if (skipOneUpdate) {
|
|
|
|
console.log("skipping one update")
|
|
|
|
skipOneUpdate = false
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-10-29 18:00:21 +00:00
|
|
|
//console.log("1: " + txtMessage.getText(0, txtMessage.text.length))
|
2018-10-28 02:49:14 +00:00
|
|
|
|
|
|
|
// convert <img> tags back to their emoji form
|
|
|
|
var nt = restoreEmoji(txtMessage.text)
|
|
|
|
if (nt != txtMessage.text) {
|
|
|
|
skipOneUpdate = true
|
|
|
|
txtMessage.text = nt
|
|
|
|
}
|
|
|
|
|
2018-10-29 18:00:21 +00:00
|
|
|
//console.log("2: " + txtMessage.getText(0, txtMessage.text.length))
|
2018-10-28 02:49:14 +00:00
|
|
|
|
|
|
|
// strip all HTML tags
|
|
|
|
var theText = txtMessage.getText(0, txtMessage.text.length)
|
2018-10-29 18:00:21 +00:00
|
|
|
//console.log("3: " + theText)
|
2018-10-28 02:49:14 +00:00
|
|
|
|
|
|
|
// convert emoji back to <img> tags
|
|
|
|
nt = parse(theText, 10)
|
2018-10-29 18:00:21 +00:00
|
|
|
//console.log("4: " + nt)
|
2018-10-28 02:49:14 +00:00
|
|
|
|
|
|
|
// if there were changes...
|
|
|
|
if (nt != txtMessage.getText(0, txtMessage.text.length)) {
|
|
|
|
// first we need to update the cursor position to be the same distance from the end
|
|
|
|
var oldcursor = txtMessage.cursorPosition
|
|
|
|
var oldlen = txtMessage.getText(0, txtMessage.text.length).length
|
|
|
|
|
|
|
|
// then we actually put the updated text in
|
|
|
|
skipOneUpdate = true
|
|
|
|
txtMessage.text = nt
|
|
|
|
|
|
|
|
// and then restore the cursor
|
|
|
|
var newlen = txtMessage.getText(0, txtMessage.text.length).length
|
|
|
|
txtMessage.cursorPosition = newlen - (oldlen - oldcursor)
|
|
|
|
}
|
|
|
|
|
|
|
|
// autoscroll down only when the scrollbar is already all the way down
|
2018-10-23 18:52:13 +00:00
|
|
|
if (flkMessage.contentY + flkMessage.height >= flkMessage.contentHeight - txtMessage.height && flkMessage.contentHeight > flkMessage.height) {
|
|
|
|
flkMessage.contentY = flkMessage.contentHeight - flkMessage.height
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-28 02:49:14 +00:00
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
anchors.fill: parent
|
|
|
|
onClicked: txtMessage.focus = true
|
|
|
|
}
|
2018-10-23 18:52:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ColumnLayout {
|
2018-10-30 19:48:37 +00:00
|
|
|
id: colRight
|
2018-10-28 02:49:14 +00:00
|
|
|
spacing: 1
|
2018-10-23 18:52:13 +00:00
|
|
|
|
|
|
|
|
2018-10-30 19:48:37 +00:00
|
|
|
SimpleButton { // SEND MESSAGE BUTTON
|
2018-10-23 18:52:13 +00:00
|
|
|
id: btnSend
|
2018-10-30 19:48:37 +00:00
|
|
|
icon: "regular/paper-plane"
|
|
|
|
text: "send"
|
2018-10-31 05:43:22 +00:00
|
|
|
Layout.minimumWidth: btnEmoji.width + btnAttach.width + 2
|
|
|
|
Layout.maximumWidth: btnEmoji.width + btnAttach.width + 2
|
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.rightMargin: 2
|
2018-10-30 19:48:37 +00:00
|
|
|
|
2018-10-28 02:49:14 +00:00
|
|
|
property int nextMessageID: 1
|
|
|
|
|
|
|
|
TextEdit {
|
|
|
|
id: txtHidden
|
|
|
|
visible: false
|
|
|
|
textFormat: Text.RichText
|
|
|
|
}
|
2018-10-23 18:52:13 +00:00
|
|
|
|
|
|
|
onClicked: {
|
|
|
|
if (txtMessage.text != "") {
|
2018-10-28 02:49:14 +00:00
|
|
|
txtHidden.text = restoreEmoji(txtMessage.text)
|
|
|
|
gcd.sendMessage(txtHidden.getText(0, txtHidden.text.length), nextMessageID++)
|
2018-10-23 18:52:13 +00:00
|
|
|
}
|
|
|
|
txtMessage.text = ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RowLayout {
|
2018-10-28 02:49:14 +00:00
|
|
|
spacing: 1
|
2018-10-23 18:52:13 +00:00
|
|
|
|
|
|
|
|
2018-10-28 02:49:14 +00:00
|
|
|
SimpleButton { // EMOJI DRAWER BUTTON
|
2018-10-23 18:52:13 +00:00
|
|
|
id: btnEmoji
|
2018-10-30 19:48:37 +00:00
|
|
|
icon: "regular/smile"
|
2018-10-31 05:43:22 +00:00
|
|
|
anchors.right: btnAttach.left
|
|
|
|
anchors.rightMargin: 2
|
2018-10-23 18:52:13 +00:00
|
|
|
|
|
|
|
onClicked: gcd.popup("emoji not yet implemented, sorry")
|
|
|
|
}
|
|
|
|
|
2018-10-30 19:48:37 +00:00
|
|
|
SimpleButton {
|
2018-10-23 18:52:13 +00:00
|
|
|
id: btnAttach
|
2018-10-30 19:48:37 +00:00
|
|
|
icon: "solid/paperclip"
|
2018-10-31 05:43:22 +00:00
|
|
|
anchors.right: parent.right
|
|
|
|
anchors.rightMargin: 2
|
2018-10-23 18:52:13 +00:00
|
|
|
|
2018-10-30 19:48:37 +00:00
|
|
|
onClicked: {
|
|
|
|
gcd.popup("attachments not yet implemented, sorry")
|
|
|
|
}
|
2018-10-23 18:52:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|