Newer
Older
// SPDX-FileCopyrightText: Nheko Contributors
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.13
property bool showAllButtons: width > 450 || (messageInput.length == 0 && !messageInput.inputMethodComposing)
readonly property string text: messageInput.text
Layout.fillWidth: true
Layout.minimumHeight: 40
Layout.preferredHeight: row.implicitHeight
color: palette.window
Component {
id: placeCallDialog
PlaceCall {
}
}
Component {
id: screenShareDialog
ScreenShare {
}
}
visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false
ImageButton {
Layout.alignment: Qt.AlignBottom
Layout.margins: 8
ToolTip.text: CallManager.isOnCall ? qsTr("Hang up") : (CallManager.isOnCallOnOtherDevice ? qsTr("Already on a call") : qsTr("Place a call"))
ToolTip.visible: hovered
image: CallManager.isOnCall ? ":/icons/icons/ui/end-call.svg" : ":/icons/icons/ui/place-call.svg"
opacity: (CallManager.haveCallInvite || CallManager.isOnCallOnOtherDevice) ? 0.3 : 1
visible: CallManager.callsSupported && showAllButtons
var dialog = placeCallDialog.createObject(timelineRoot);
}
ImageButton {
Layout.alignment: Qt.AlignBottom
Layout.margins: 8
ToolTip.text: qsTr("Send a file")
ToolTip.visible: hovered
image: ":/icons/icons/ui/attach.svg"
onClicked: room.input.openFileSelection()
visible: room && room.input.uploading
Spinner {
anchors.centerIn: parent
height: parent.height / 2
Layout.maximumHeight: Window.height / 4
Layout.minimumHeight: fontMetrics.lineSpacing
Layout.preferredHeight: contentHeight
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
contentWidth: availableWidth
id: messageInput
property int completerTriggeredAt: 0
messageInput.remove(completerTriggeredAt, cursorPosition);
messageInput.insert(cursorPosition, completion);
completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition) + messageInput.preeditText);
function positionCursorAtEnd() {
cursorPosition = messageInput.length;
}
function positionCursorAtStart() {
cursorPosition = 0;
}
background: null
bottomPadding: 8
color: palette.text
focus: true
leftPadding: inputBar.showAllButtons ? 0 : 8
padding: 0
placeholderText: qsTr("Write a message...")
placeholderTextColor: palette.buttonText
verticalAlignment: TextEdit.AlignVCenter
event.accepted = room.input.tryPasteAttachment(false);
} else if (event.key == Qt.Key_Space) {
// close popup if user enters space after colon
if (cursorPosition == completerTriggeredAt + 1)
popup.close();
if (popup.opened && completer.count <= 0)
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_U) {
messageInput.clear();
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_P) {
messageInput.text = room.input.previousText();
} else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_N) {
messageInput.text = room.input.nextText();
} else if (event.key == Qt.Key_Escape && popup.opened) {
} else if (event.matches(StandardKey.SelectAll) && popup.opened) {
} else if (event.matches(StandardKey.InsertLineSeparator)) {
if (Settings.invertEnterKey && (!Qt.inputMethod.visible || Qt.platform.os === "windows")) {
room.input.send();
event.accepted = true;
}
} else if (event.matches(StandardKey.InsertParagraphSeparator)) {
if (popup.opened) {
var currentCompletion = completer.currentCompletion();
completer.completerName = "";
popup.close();
if (currentCompletion) {
messageInput.insertCompletion(currentCompletion);
if (!Settings.invertEnterKey && (!Qt.inputMethod.visible || Qt.platform.os === "windows")) {
room.input.send();
event.accepted = true;
}
} else if (event.key == Qt.Key_Tab && (event.modifiers == Qt.NoModifier || event.modifiers == Qt.ShiftModifier)) {
if (event.modifiers & Qt.ShiftModifier)
else
} else {
var pos = cursorPosition - 1;
while (pos > -1) {
var t = messageInput.getText(pos, pos + 1);
messageInput.openCompleter(pos, "user");
} else if (t == ' ' || t == '\t') {
messageInput.openCompleter(pos + 1, "user");
messageInput.openCompleter(pos, "emoji");
} else if (t == '~') {
messageInput.openCompleter(pos, "customEmoji");
}
pos = pos - 1;
}
// At start of input
messageInput.openCompleter(0, "user");
} else if (event.key == Qt.Key_Up && popup.opened) {
event.accepted = true;
} else if ((event.key == Qt.Key_Down || event.key == Qt.Key_Backtab) && popup.opened) {
} else if (event.key == Qt.Key_Up && (event.modifiers == Qt.NoModifier || event.modifiers == Qt.KeypadModifier)) {
if (cursorPosition == 0) {
event.accepted = true;
var idx = room.edit ? room.idToIndex(room.edit) + 1 : 0;
var id = room.indexToId(idx);
if (!id || room.getDump(id, "").isEditable) {
room.edit = id;
} else if (positionAt(0, cursorRectangle.y + cursorRectangle.height / 2) === 0) {
} else if (event.key == Qt.Key_Down && (event.modifiers == Qt.NoModifier || event.modifiers == Qt.KeypadModifier)) {
if (cursorPosition == messageInput.length && room.edit) {
var idx = room.idToIndex(room.edit) - 1;
var id = room.indexToId(idx);
if (!id || room.getDump(id, "").isEditable) {
room.edit = id;
} else if (positionAt(width, cursorRectangle.y + cursorRectangle.height / 2) === messageInput.length) {
event.accepted = true;
positionCursorAtEnd();
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
// Ensure that we get escape key press events first.
Keys.onShortcutOverride: event => event.accepted = (popup.opened && (event.key === Qt.Key_Escape || event.key === Qt.Key_Tab || event.key === Qt.Key_Enter || event.key === Qt.Key_Space))
onCursorPositionChanged: {
if (!room)
return;
room.input.updateState(selectionStart, selectionEnd, cursorPosition, text);
if (popup.opened && cursorPosition <= completerTriggeredAt)
popup.close();
if (popup.opened)
completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition) + messageInput.preeditText);
}
onPreeditTextChanged: {
if (popup.opened)
completer.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition) + messageInput.preeditText);
}
onSelectionEndChanged: room.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
onSelectionStartChanged: room.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
onTextChanged: {
if (room)
room.input.updateState(selectionStart, selectionEnd, cursorPosition, text);
forceActiveFocus();
if (cursorPosition > 0)
lastChar = text.charAt(cursorPosition - 1);
else
lastChar = '';
if (lastChar == '@') {
messageInput.openCompleter(selectionStart - 1, "user");
} else if (lastChar == ':') {
messageInput.openCompleter(selectionStart - 1, "emoji");
} else if (lastChar == '#') {
messageInput.openCompleter(selectionStart - 1, "roomAliases");
} else if (lastChar == "/" && cursorPosition == 1) {
messageInput.openCompleter(selectionStart - 1, "command");
}
}
messageInput.clear();
messageInput.append(room.input.text);
messageInput.forceActiveFocus();
function onCompletionClicked(completion) {
messageInput.insertCompletion(completion);
}
x: messageInput.positionToRectangle(messageInput.completerTriggeredAt).x
y: messageInput.positionToRectangle(messageInput.completerTriggeredAt).y - height
to: 1
}
}
exit: Transition {
NumberAnimation {
id: completer
rowMargin: 2
rowSpacing: 0
}
}
messageInput.text = newText;
messageInput.cursorPosition = newText.length;
}
target: room ? room.input : null
function onThreadChanged() {
messageInput.forceActiveFocus();
}
ignoreUnknownSignals: true
function onFocusInput() {
messageInput.forceActiveFocus();
}
target: TimelineManager
}
// workaround for wrong cursor shape on some platforms
anchors.fill: parent
cursorShape: Qt.IBeamCursor
onPressed: mouse => mouse.accepted = room.input.tryPasteAttachment(true)
}
ImageButton {
id: stickerButton
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
Layout.margins: 8
onClicked: stickerPopup.visible ? stickerPopup.close() : stickerPopup.show(stickerButton, room.roomId, function (row) {
room.input.sticker(row);
TimelineManager.focusMessageInput();
})
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
image: ":/icons/icons/ui/smile.svg"
onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(emojiButton, room.roomId, function (plaintext, markdown) {
messageInput.insert(messageInput.cursorPosition, markdown);
TimelineManager.focusMessageInput();
})
}
ImageButton {
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
anchors.centerIn: parent
color: palette.placeholderText
text: qsTr("You don't have permission to send messages in this room")
visible: room ? (!room.permissions.canSend(MtxEvent.TextMessage)) : false