mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-25 20:48:52 +03:00
WIP Qml completer
This commit is contained in:
parent
9d68d59465
commit
cabeb1464c
5 changed files with 174 additions and 14 deletions
107
resources/qml/Completer.qml
Normal file
107
resources/qml/Completer.qml
Normal 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
|
||||
}
|
||||
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -163,6 +163,12 @@ InputBar::nextText()
|
|||
return text();
|
||||
}
|
||||
|
||||
QObject *
|
||||
InputBar::completerFor(QString completerName)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
InputBar::send()
|
||||
{
|
||||
|
|
|
@ -41,6 +41,8 @@ public slots:
|
|||
bool uploading() const { return uploading_; }
|
||||
void callButton();
|
||||
|
||||
QObject *completerFor(QString completerName);
|
||||
|
||||
private slots:
|
||||
void startTyping();
|
||||
void stopTyping();
|
||||
|
|
Loading…
Reference in a new issue