WIP Qml completer

This commit is contained in:
Nicolas Werner 2020-11-20 01:22:36 +01:00
parent 9d68d59465
commit cabeb1464c
5 changed files with 174 additions and 14 deletions

107
resources/qml/Completer.qml Normal file
View file

@ -0,0 +1,107 @@
import QtQuick 2.9
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import im.nheko 1.0
Popup {
id: popup
property int currentIndex: -1
property string completerName
property var completer
function up() {
currentIndex = currentIndex - 1;
if (currentIndex == -2)
currentIndex = repeater.count - 1;
}
function down() {
currentIndex = currentIndex + 1;
if (currentIndex >= repeater.count)
currentIndex = -1;
}
function currentCompletion() {
if (currentIndex > -1 && currentIndex < repeater.count)
return completer.completionAt(currentIndex);
else
return null;
}
onCompleterNameChanged: {
if (completerName)
completer = TimelineManager.timeline.input.completerFor(completerName);
}
padding: 0
onAboutToShow: currentIndex = -1
ColumnLayout {
anchors.fill: parent
spacing: 0
Repeater {
id: repeater
model: completer
delegate: Rectangle {
color: model.index == popup.currentIndex ? colors.window : colors.base
height: del.implicitHeight + 4
width: del.implicitWidth + 4
RowLayout {
id: del
anchors.centerIn: parent
Avatar {
height: 24
width: 24
displayName: model.displayName
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
}
Label {
text: model.displayName
color: colors.text
}
}
}
}
}
enter: Transition {
NumberAnimation {
property: "opacity"
from: 0
to: 1
duration: 100
}
}
exit: Transition {
NumberAnimation {
property: "opacity"
from: 1
to: 0
duration: 100
}
}
background: Rectangle {
color: colors.base
implicitHeight: popup.contentHeight
implicitWidth: popup.contentWidth
}
}

View file

@ -68,28 +68,72 @@ Rectangle {
TextArea {
id: textArea
property int completerTriggeredAt: -1
placeholderText: qsTr("Write a message...")
placeholderTextColor: colors.buttonText
color: colors.text
wrapMode: TextEdit.Wrap
onTextChanged: TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
onCursorPositionChanged: TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
onCursorPositionChanged: {
TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text);
if (cursorPosition < completerTriggeredAt) {
completerTriggeredAt = -1;
popup.close();
}
}
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.
Keys.onShortcutOverride: event.accepted = (completerTriggeredAt != -1 && (event.key === Qt.Key_Escape || event.key === Qt.Key_Tab || event.key === Qt.Key_Enter))
Keys.onPressed: {
if (event.matches(StandardKey.Paste)) {
TimelineManager.timeline.input.paste(false);
event.accepted = true;
} 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) {
completerTriggeredAt = cursorPosition + 1;
popup.completerName = "user";
popup.open();
} else if (event.key == Qt.Key_Escape && popup.opened) {
completerTriggeredAt = -1;
event.accepted = true;
popup.close();
} else if (event.matches(StandardKey.InsertParagraphSeparator) && popup.opened) {
var currentCompletion = popup.currentCompletion();
popup.close();
if (currentCompletion) {
textArea.remove(completerTriggeredAt - 1, cursorPosition);
textArea.insert(cursorPosition, currentCompletion);
event.accepted = true;
return ;
}
} else if (event.key == Qt.Key_Tab && popup.opened) {
event.accepted = true;
popup.down();
} 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();
} else if (event.matches(StandardKey.InsertParagraphSeparator)) {
TimelineManager.timeline.input.send();
textArea.clear();
event.accepted = true;
} 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();
}
}
Completer {
id: popup
x: textArea.positionToRectangle(textArea.completerTriggeredAt).x
y: textArea.positionToRectangle(textArea.completerTriggeredAt).y - height
}
Connections {

View file

@ -123,21 +123,22 @@
<file>qtquickcontrols2.conf</file>
<file>qml/TimelineView.qml</file>
<file>qml/TopBar.qml</file>
<file>qml/MessageView.qml</file>
<file>qml/MessageInput.qml</file>
<file>qml/TypingIndicator.qml</file>
<file>qml/ReplyPopup.qml</file>
<file>qml/ActiveCallBar.qml</file>
<file>qml/Avatar.qml</file>
<file>qml/Completer.qml</file>
<file>qml/EncryptionIndicator.qml</file>
<file>qml/ImageButton.qml</file>
<file>qml/MatrixText.qml</file>
<file>qml/MessageInput.qml</file>
<file>qml/MessageView.qml</file>
<file>qml/NhekoBusyIndicator.qml</file>
<file>qml/StatusIndicator.qml</file>
<file>qml/EncryptionIndicator.qml</file>
<file>qml/Reactions.qml</file>
<file>qml/ReplyPopup.qml</file>
<file>qml/ScrollHelper.qml</file>
<file>qml/StatusIndicator.qml</file>
<file>qml/TimelineRow.qml</file>
<file>qml/TopBar.qml</file>
<file>qml/TypingIndicator.qml</file>
<file>qml/VideoCall.qml</file>
<file>qml/emoji/EmojiButton.qml</file>
<file>qml/emoji/EmojiPicker.qml</file>

View file

@ -163,6 +163,12 @@ InputBar::nextText()
return text();
}
QObject *
InputBar::completerFor(QString completerName)
{
return nullptr;
}
void
InputBar::send()
{

View file

@ -41,6 +41,8 @@ public slots:
bool uploading() const { return uploading_; }
void callButton();
QObject *completerFor(QString completerName);
private slots:
void startTyping();
void stopTyping();