Skip to content
Snippets Groups Projects
MessageInput.qml 9.99 KiB
Newer Older
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.2
Nicolas Werner's avatar
Nicolas Werner committed
import im.nheko 1.0

Rectangle {
    color: colors.window
    Layout.fillWidth: true
    Layout.preferredHeight: textInput.height
    Layout.minimumHeight: 40

    RowLayout {
        id: inputBar

        anchors.fill: parent
        spacing: 16

        ImageButton {
            visible: TimelineManager.callsSupported
            Layout.alignment: Qt.AlignBottom
            hoverEnabled: true
            width: 22
            height: 22
            image: TimelineManager.isOnCall ? ":/icons/icons/ui/end-call.png" : ":/icons/icons/ui/place-call.png"
            ToolTip.visible: hovered
            ToolTip.text: TimelineManager.isOnCall ? qsTr("Hang up") : qsTr("Place a call")
            Layout.topMargin: 8
            Layout.bottomMargin: 8
            Layout.leftMargin: 16
            onClicked: TimelineManager.timeline.input.callButton()
        }

        ImageButton {
            Layout.alignment: Qt.AlignBottom
            hoverEnabled: true
            width: 22
            height: 22
            image: ":/icons/icons/ui/paper-clip-outline.png"
            Layout.topMargin: 8
            Layout.bottomMargin: 8
            Layout.leftMargin: TimelineManager.callsSupported ? 0 : 16
Nicolas Werner's avatar
Nicolas Werner committed
            onClicked: TimelineManager.timeline.input.openFileSelection()
            ToolTip.visible: hovered
            ToolTip.text: qsTr("Send a file")
Nicolas Werner's avatar
Nicolas Werner committed

            Rectangle {
                anchors.fill: parent
                color: colors.window
                visible: TimelineManager.timeline.input.uploading

                NhekoBusyIndicator {
                    anchors.fill: parent
                    running: parent.visible
                }

            }

        }

        ScrollView {
            id: textInput

            Layout.alignment: Qt.AlignBottom
            Layout.maximumHeight: Window.height / 4
            Layout.fillWidth: true

            TextArea {
Nicolas Werner's avatar
Nicolas Werner committed
                id: textArea

Nicolas Werner's avatar
Nicolas Werner committed
                property int completerTriggeredAt: -1

                function insertCompletion(completion) {
                    textArea.remove(completerTriggeredAt, cursorPosition);
                    textArea.insert(cursorPosition, completion);
                }

Nicolas Werner's avatar
Nicolas Werner committed
                function openCompleter(pos, type) {
                    completerTriggeredAt = pos;
                    popup.completerName = type;
                    popup.open();
                    popup.completer.setSearchString(textArea.getText(completerTriggeredAt, cursorPosition));
                }

                selectByMouse: true
                placeholderText: qsTr("Write a message...")
                placeholderTextColor: colors.buttonText
                color: colors.text
                wrapMode: TextEdit.Wrap
Nicolas Werner's avatar
Nicolas Werner committed
                onTextChanged: TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
Nicolas Werner's avatar
Nicolas Werner committed
                onCursorPositionChanged: {
                    TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text);
Nicolas Werner's avatar
Nicolas Werner committed
                    if (cursorPosition <= completerTriggeredAt) {
Nicolas Werner's avatar
Nicolas Werner committed
                        completerTriggeredAt = -1;
                        popup.close();
                    }
                    if (popup.opened)
                        popup.completer.setSearchString(textArea.getText(completerTriggeredAt, cursorPosition));

Nicolas Werner's avatar
Nicolas Werner committed
                }
Nicolas Werner's avatar
Nicolas Werner committed
                onSelectionStartChanged: TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
                onSelectionEndChanged: TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
                // Ensure that we get escape key press events first.
Nicolas Werner's avatar
Nicolas Werner committed
                Keys.onShortcutOverride: event.accepted = (completerTriggeredAt != -1 && (event.key === Qt.Key_Escape || event.key === Qt.Key_Tab || event.key === Qt.Key_Enter))
Nicolas Werner's avatar
Nicolas Werner committed
                Keys.onPressed: {
                    if (event.matches(StandardKey.Paste)) {
Nicolas Werner's avatar
Nicolas Werner committed
                        TimelineManager.timeline.input.paste(false);
                        event.accepted = true;
Nicolas Werner's avatar
Nicolas Werner committed
                    } else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_U) {
                        textArea.clear();
                    } else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_P) {
                        textArea.text = TimelineManager.timeline.input.previousText();
                    } else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_N) {
                        textArea.text = TimelineManager.timeline.input.nextText();
                    } else if (event.key == Qt.Key_At) {
Nicolas Werner's avatar
Nicolas Werner committed
                        textArea.openCompleter(cursorPosition, "user");
Nicolas Werner's avatar
Nicolas Werner committed
                        popup.open();
Nicolas Werner's avatar
Nicolas Werner committed
                    } else if (event.key == Qt.Key_Colon) {
Nicolas Werner's avatar
Nicolas Werner committed
                        textArea.openCompleter(cursorPosition, "emoji");
Nicolas Werner's avatar
Nicolas Werner committed
                        popup.open();
Nicolas Werner's avatar
Nicolas Werner committed
                    } else if (event.key == Qt.Key_Escape && popup.opened) {
                        completerTriggeredAt = -1;
Nicolas Werner's avatar
Nicolas Werner committed
                        popup.completerName = "";
Nicolas Werner's avatar
Nicolas Werner committed
                        event.accepted = true;
                        popup.close();
                    } else if (event.matches(StandardKey.InsertParagraphSeparator)) {
                        if (popup.opened) {
                            var currentCompletion = popup.currentCompletion();
                            popup.completerName = "";
                            popup.close();
                            if (currentCompletion) {
                                textArea.insertCompletion(currentCompletion);
                                event.accepted = true;
                                return ;
                            }
Nicolas Werner's avatar
Nicolas Werner committed
                        }
                        TimelineManager.timeline.input.send();
                        textArea.clear();
                        event.accepted = true;
Nicolas Werner's avatar
Nicolas Werner committed
                    } else if (event.key == Qt.Key_Tab) {
Nicolas Werner's avatar
Nicolas Werner committed
                        event.accepted = true;
Nicolas Werner's avatar
Nicolas Werner committed
                        if (popup.opened) {
                            popup.up();
                        } else {
                            var pos = cursorPosition - 1;
                            while (pos > -1) {
                                var t = textArea.getText(pos, pos + 1);
                                console.log('"' + t + '"');
                                if (t == '@' || t == ' ' || t == '\t') {
                                    textArea.openCompleter(pos, "user");
                                    return ;
                                } else if (t == ':') {
                                    textArea.openCompleter(pos, "emoji");
                                    return ;
                                }
                                pos = pos - 1;
                            }
                            // At start of input
                            textArea.openCompleter(0, "user");
                        }
Nicolas Werner's avatar
Nicolas Werner committed
                    } else if (event.key == Qt.Key_Up && popup.opened) {
                        event.accepted = true;
                        popup.up();
                    } else if (event.key == Qt.Key_Down && popup.opened) {
                        event.accepted = true;
                        popup.down();
                    }
                }

Nicolas Werner's avatar
Nicolas Werner committed
                Connections {
                    onTimelineChanged: {
                        textArea.clear();
                        textArea.append(TimelineManager.timeline.input.text());
                        textArea.completerTriggeredAt = -1;
                        popup.completerName = "";
                    }
                    target: TimelineManager
                }

                Connections {
                    onCompletionClicked: textArea.insertCompletion(completion)
                    target: popup
                }
Nicolas Werner's avatar
Nicolas Werner committed
                Completer {
                    id: popup

                    x: textArea.positionToRectangle(textArea.completerTriggeredAt).x
                    y: textArea.positionToRectangle(textArea.completerTriggeredAt).y - height
Nicolas Werner's avatar
Nicolas Werner committed
                }

                Connections {
                    onInsertText: textArea.insert(textArea.cursorPosition, text)
Nicolas Werner's avatar
Nicolas Werner committed
                    target: TimelineManager.timeline.input
                MouseArea {
                    // workaround for wrong cursor shape on some platforms
                    anchors.fill: parent
Nicolas Werner's avatar
Nicolas Werner committed
                    acceptedButtons: Qt.MiddleButton
                    cursorShape: Qt.IBeamCursor
Nicolas Werner's avatar
Nicolas Werner committed
                    onClicked: TimelineManager.timeline.input.paste(true)
                NhekoDropArea {
                    anchors.fill: parent
                    roomid: TimelineManager.timeline.roomId()
                }

                background: Rectangle {
                    color: colors.window
                }

            }

        }

        ImageButton {
            id: emojiButton

            Layout.alignment: Qt.AlignRight | Qt.AlignBottom
            hoverEnabled: true
            width: 22
            height: 22
            image: ":/icons/icons/ui/smile.png"
            Layout.topMargin: 8
            Layout.bottomMargin: 8
            ToolTip.visible: hovered
            ToolTip.text: qsTr("Emoji")
            onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(emojiButton, function(emoji) {
                textArea.insert(textArea.cursorPosition, emoji);
            })
        }

        ImageButton {
            Layout.alignment: Qt.AlignRight | Qt.AlignBottom
            hoverEnabled: true
            width: 22
            height: 22
            image: ":/icons/icons/ui/cursor.png"
            Layout.topMargin: 8
            Layout.bottomMargin: 8
            Layout.rightMargin: 16
            ToolTip.visible: hovered
            ToolTip.text: qsTr("Send")
Nicolas Werner's avatar
Nicolas Werner committed
            onClicked: {
                TimelineManager.timeline.input.send();
                textArea.clear();
            }