2020-08-20 12:21:47 -04:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
|
|
|
|
|
|
import QtQuick 2.12
|
|
|
|
import QtQuick.Layouts 1.12
|
|
|
|
import "../../.."
|
|
|
|
import "../../../Base"
|
|
|
|
import "../../../Base/HTile"
|
|
|
|
|
|
|
|
HListView {
|
2020-08-21 01:17:29 -04:00
|
|
|
id: root
|
2020-08-20 12:21:47 -04:00
|
|
|
|
|
|
|
property HTextArea textArea
|
|
|
|
property bool open: false
|
|
|
|
|
2020-08-21 04:44:55 -04:00
|
|
|
property var originalWord: null
|
2020-08-21 11:07:23 -04:00
|
|
|
property int replacementStart: -1
|
|
|
|
property int replacementEnd: -1
|
2020-08-20 12:21:47 -04:00
|
|
|
property bool autoOpenCompleted: false
|
2020-08-21 01:17:29 -04:00
|
|
|
property var usersCompleted: ({}) // {displayName: userId}
|
2020-08-20 12:21:47 -04:00
|
|
|
|
2020-08-21 04:44:55 -04:00
|
|
|
readonly property bool autoOpen: {
|
|
|
|
if (autoOpenCompleted) return true
|
|
|
|
const current = textArea.getWordBehindCursor()
|
|
|
|
return current ? /^@.+/.test(current.word) : false
|
|
|
|
}
|
|
|
|
|
|
|
|
readonly property var wordToComplete:
|
|
|
|
open ? originalWord || textArea.getWordBehindCursor() : null
|
2020-08-20 12:21:47 -04:00
|
|
|
|
2020-08-21 04:44:55 -04:00
|
|
|
readonly property string modelFilter:
|
|
|
|
autoOpen && wordToComplete ? wordToComplete.word.replace(/^@/, "") :
|
|
|
|
open && wordToComplete ? wordToComplete.word :
|
2020-08-20 12:21:47 -04:00
|
|
|
""
|
|
|
|
|
2020-08-21 10:42:57 -04:00
|
|
|
function replaceCompletionOrCurrentWord(withText) {
|
2020-08-23 09:14:45 -04:00
|
|
|
Qt.inputMethod.reset()
|
|
|
|
|
2020-08-21 04:44:55 -04:00
|
|
|
const current = textArea.getWordBehindCursor()
|
2020-08-21 10:42:57 -04:00
|
|
|
if (! current) return
|
|
|
|
|
2020-08-21 11:07:23 -04:00
|
|
|
replacementStart === -1 || replacementEnd === -1 ?
|
|
|
|
textArea.remove(current.start, current.end + 1) :
|
|
|
|
textArea.remove(replacementStart, replacementEnd)
|
2020-08-21 10:42:57 -04:00
|
|
|
|
|
|
|
textArea.insertAtCursor(withText)
|
2020-08-20 12:21:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function previous() {
|
|
|
|
if (open) {
|
|
|
|
decrementCurrentIndex()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
open = true
|
2020-08-21 04:44:55 -04:00
|
|
|
const args = [model.modelId, modelFilter]
|
2020-08-20 12:21:47 -04:00
|
|
|
py.callCoro("set_string_filter", args, decrementCurrentIndex)
|
|
|
|
}
|
|
|
|
|
|
|
|
function next() {
|
|
|
|
if (open) {
|
|
|
|
incrementCurrentIndex()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
open = true
|
2020-08-21 04:44:55 -04:00
|
|
|
const args = [model.modelId, modelFilter]
|
2020-08-20 12:21:47 -04:00
|
|
|
py.callCoro("set_string_filter", args, incrementCurrentIndex)
|
|
|
|
}
|
|
|
|
|
2020-08-21 01:17:29 -04:00
|
|
|
function accept() {
|
|
|
|
if (currentIndex !== -1) {
|
|
|
|
const member = model.get(currentIndex)
|
|
|
|
usersCompleted[member.display_name] = member.id
|
|
|
|
usersCompletedChanged()
|
|
|
|
}
|
|
|
|
|
|
|
|
open = false
|
|
|
|
}
|
|
|
|
|
2020-08-20 12:21:47 -04:00
|
|
|
function cancel() {
|
2020-08-21 10:42:57 -04:00
|
|
|
if (originalWord) replaceCompletionOrCurrentWord(originalWord.word)
|
2020-08-20 12:21:47 -04:00
|
|
|
|
2020-08-21 01:17:29 -04:00
|
|
|
currentIndex = -1
|
|
|
|
open = false
|
2020-08-20 12:21:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
visible: opacity > 0
|
|
|
|
opacity: open && count ? 1 : 0
|
2020-08-21 11:14:35 -04:00
|
|
|
bottomMargin: theme.spacing / 2
|
|
|
|
implicitHeight:
|
|
|
|
open && count ?
|
|
|
|
Math.min(window.height, contentHeight + topMargin + bottomMargin) :
|
|
|
|
0
|
|
|
|
|
2020-08-20 12:21:47 -04:00
|
|
|
model: ModelStore.get(chat.userId, chat.roomId, "autocompleted_members")
|
|
|
|
|
2020-08-21 09:27:01 -04:00
|
|
|
delegate: CompletableUserDelegate {
|
2020-08-21 01:17:29 -04:00
|
|
|
width: root.width
|
2020-08-21 11:19:42 -04:00
|
|
|
colorName: hovered || root.currentIndex === model.index
|
2020-08-20 12:21:47 -04:00
|
|
|
onClicked: {
|
2020-08-23 09:23:34 -04:00
|
|
|
root.currentIndex = model.index
|
|
|
|
root.accept()
|
|
|
|
root.open = false
|
2020-08-20 12:21:47 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onAutoOpenChanged: open = autoOpen
|
|
|
|
onOpenChanged: if (! open) {
|
2020-08-21 04:44:55 -04:00
|
|
|
originalWord = null
|
2020-08-21 11:07:23 -04:00
|
|
|
replacementStart = -1
|
|
|
|
replacementEnd = -1
|
2020-08-20 12:21:47 -04:00
|
|
|
currentIndex = -1
|
|
|
|
autoOpenCompleted = false
|
|
|
|
py.callCoro("set_string_filter", [model.modelId, ""])
|
|
|
|
}
|
|
|
|
|
2020-08-21 04:44:55 -04:00
|
|
|
onModelFilterChanged: {
|
2020-08-20 12:21:47 -04:00
|
|
|
if (! open) return
|
2020-08-21 04:44:55 -04:00
|
|
|
py.callCoro("set_string_filter", [model.modelId, modelFilter])
|
2020-08-20 12:21:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
onCurrentIndexChanged: {
|
|
|
|
if (currentIndex === -1) return
|
2020-08-21 04:44:55 -04:00
|
|
|
if (! originalWord) originalWord = textArea.getWordBehindCursor()
|
2020-08-20 12:21:47 -04:00
|
|
|
if (autoOpen) autoOpenCompleted = true
|
|
|
|
|
2020-08-21 10:46:18 -04:00
|
|
|
const member = model.get(currentIndex)
|
|
|
|
const replacement = member.display_name || member.id
|
|
|
|
|
|
|
|
replaceCompletionOrCurrentWord(replacement)
|
2020-08-21 11:07:23 -04:00
|
|
|
replacementStart = textArea.cursorPosition - replacement.length
|
|
|
|
replacementEnd = textArea.cursorPosition
|
2020-08-20 12:21:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
Behavior on opacity { HNumberAnimation {} }
|
|
|
|
Behavior on implicitHeight { HNumberAnimation {} }
|
2020-08-21 01:17:29 -04:00
|
|
|
|
2020-08-21 09:27:01 -04:00
|
|
|
Rectangle {
|
|
|
|
anchors.fill: parent
|
|
|
|
z: -1
|
2020-08-21 11:35:03 -04:00
|
|
|
color: theme.chat.userAutoCompletion.background
|
2020-08-21 09:27:01 -04:00
|
|
|
}
|
|
|
|
|
2020-08-21 01:17:29 -04:00
|
|
|
Connections {
|
|
|
|
target: root.textArea
|
|
|
|
|
2020-08-21 01:46:07 -04:00
|
|
|
function onCursorPositionChanged() {
|
2020-08-21 04:44:55 -04:00
|
|
|
if (! root.open) return
|
|
|
|
|
|
|
|
const pos = root.textArea.cursorPosition
|
|
|
|
const start = root.wordToComplete.start
|
2020-08-21 10:46:18 -04:00
|
|
|
let end = root.wordToComplete.end + 1
|
|
|
|
|
2020-08-22 01:20:55 -04:00
|
|
|
if (root.currentIndex !== -1) {
|
|
|
|
const member = root.model.get(root.currentIndex)
|
2020-08-21 10:46:18 -04:00
|
|
|
const repl = member.display_name || member.id
|
|
|
|
end = root.wordToComplete.start + repl.length
|
|
|
|
}
|
2020-08-21 04:44:55 -04:00
|
|
|
|
2020-08-22 01:20:55 -04:00
|
|
|
if (pos === root.textArea.length) return
|
2020-08-21 04:44:55 -04:00
|
|
|
if (pos < start || pos > end) root.accept()
|
2020-08-21 01:46:07 -04:00
|
|
|
}
|
|
|
|
|
2020-08-21 01:17:29 -04:00
|
|
|
function onTextChanged() {
|
|
|
|
let changed = false
|
|
|
|
|
|
|
|
for (const displayName of Object.keys(root.usersCompleted)) {
|
|
|
|
if (! root.textArea.text.includes(displayName)) {
|
|
|
|
delete root.usersCompleted[displayName]
|
|
|
|
changed = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (changed) root.usersCompletedChanged()
|
|
|
|
}
|
|
|
|
}
|
2020-08-20 12:21:47 -04:00
|
|
|
}
|