mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-26 04:58:49 +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 {
|
TextArea {
|
||||||
id: textArea
|
id: textArea
|
||||||
|
|
||||||
|
property int completerTriggeredAt: -1
|
||||||
|
|
||||||
placeholderText: qsTr("Write a message...")
|
placeholderText: qsTr("Write a message...")
|
||||||
placeholderTextColor: colors.buttonText
|
placeholderTextColor: colors.buttonText
|
||||||
color: colors.text
|
color: colors.text
|
||||||
wrapMode: TextEdit.Wrap
|
wrapMode: TextEdit.Wrap
|
||||||
onTextChanged: TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
|
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)
|
onSelectionStartChanged: TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
|
||||||
onSelectionEndChanged: 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: {
|
Keys.onPressed: {
|
||||||
if (event.matches(StandardKey.Paste)) {
|
if (event.matches(StandardKey.Paste)) {
|
||||||
TimelineManager.timeline.input.paste(false);
|
TimelineManager.timeline.input.paste(false);
|
||||||
event.accepted = true;
|
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)) {
|
} else if (event.matches(StandardKey.InsertParagraphSeparator)) {
|
||||||
TimelineManager.timeline.input.send();
|
TimelineManager.timeline.input.send();
|
||||||
textArea.clear();
|
textArea.clear();
|
||||||
event.accepted = true;
|
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();
|
Completer {
|
||||||
else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_N)
|
id: popup
|
||||||
textArea.text = TimelineManager.timeline.input.nextText();
|
|
||||||
|
x: textArea.positionToRectangle(textArea.completerTriggeredAt).x
|
||||||
|
y: textArea.positionToRectangle(textArea.completerTriggeredAt).y - height
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
|
|
@ -123,21 +123,22 @@
|
||||||
<file>qtquickcontrols2.conf</file>
|
<file>qtquickcontrols2.conf</file>
|
||||||
|
|
||||||
<file>qml/TimelineView.qml</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/ActiveCallBar.qml</file>
|
||||||
<file>qml/Avatar.qml</file>
|
<file>qml/Avatar.qml</file>
|
||||||
|
<file>qml/Completer.qml</file>
|
||||||
|
<file>qml/EncryptionIndicator.qml</file>
|
||||||
<file>qml/ImageButton.qml</file>
|
<file>qml/ImageButton.qml</file>
|
||||||
<file>qml/MatrixText.qml</file>
|
<file>qml/MatrixText.qml</file>
|
||||||
|
<file>qml/MessageInput.qml</file>
|
||||||
|
<file>qml/MessageView.qml</file>
|
||||||
<file>qml/NhekoBusyIndicator.qml</file>
|
<file>qml/NhekoBusyIndicator.qml</file>
|
||||||
<file>qml/StatusIndicator.qml</file>
|
|
||||||
<file>qml/EncryptionIndicator.qml</file>
|
|
||||||
<file>qml/Reactions.qml</file>
|
<file>qml/Reactions.qml</file>
|
||||||
|
<file>qml/ReplyPopup.qml</file>
|
||||||
<file>qml/ScrollHelper.qml</file>
|
<file>qml/ScrollHelper.qml</file>
|
||||||
|
<file>qml/StatusIndicator.qml</file>
|
||||||
<file>qml/TimelineRow.qml</file>
|
<file>qml/TimelineRow.qml</file>
|
||||||
|
<file>qml/TopBar.qml</file>
|
||||||
|
<file>qml/TypingIndicator.qml</file>
|
||||||
<file>qml/VideoCall.qml</file>
|
<file>qml/VideoCall.qml</file>
|
||||||
<file>qml/emoji/EmojiButton.qml</file>
|
<file>qml/emoji/EmojiButton.qml</file>
|
||||||
<file>qml/emoji/EmojiPicker.qml</file>
|
<file>qml/emoji/EmojiPicker.qml</file>
|
||||||
|
|
|
@ -163,6 +163,12 @@ InputBar::nextText()
|
||||||
return text();
|
return text();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QObject *
|
||||||
|
InputBar::completerFor(QString completerName)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
InputBar::send()
|
InputBar::send()
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,6 +41,8 @@ public slots:
|
||||||
bool uploading() const { return uploading_; }
|
bool uploading() const { return uploading_; }
|
||||||
void callButton();
|
void callButton();
|
||||||
|
|
||||||
|
QObject *completerFor(QString completerName);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void startTyping();
|
void startTyping();
|
||||||
void stopTyping();
|
void stopTyping();
|
||||||
|
|
Loading…
Reference in a new issue