diff --git a/resources/qml/ActiveCallBar.qml b/resources/qml/ActiveCallBar.qml
index cbe36e6d..49c0e255 100644
--- a/resources/qml/ActiveCallBar.qml
+++ b/resources/qml/ActiveCallBar.qml
@@ -53,7 +53,6 @@ Rectangle {
Connections {
target: TimelineManager
-
onCallStateChanged: {
switch (state) {
case WebRTCState.INITIATING:
diff --git a/resources/qml/Completer.qml b/resources/qml/Completer.qml
index dda5b896..7074de81 100644
--- a/resources/qml/Completer.qml
+++ b/resources/qml/Completer.qml
@@ -9,105 +9,120 @@ Popup {
property int currentIndex: -1
property string completerName
property var completer
+ property bool bottomToTop: true
function up() {
+ if (bottomToTop)
+ down_();
+ else
+ up_();
+ }
+
+ function down() {
+ if (bottomToTop)
+ up_();
+ else
+ down_();
+ }
+
+ function up_() {
currentIndex = currentIndex - 1;
if (currentIndex == -2)
- currentIndex = repeater.count - 1;
+ currentIndex = listView.count - 1;
}
- function down() {
+ function down_() {
currentIndex = currentIndex + 1;
- if (currentIndex >= repeater.count)
+ if (currentIndex >= listView.count)
currentIndex = -1;
}
function currentCompletion() {
- if (currentIndex > -1 && currentIndex < repeater.count)
+ if (currentIndex > -1 && currentIndex < listView.count)
return completer.completionAt(currentIndex);
else
return null;
}
onCompleterNameChanged: {
- if (completerName)
+ if (completerName) {
completer = TimelineManager.timeline.input.completerFor(completerName);
- else
+ completer.setSearchString("");
+ } else {
completer = undefined;
+ }
}
padding: 0
onAboutToShow: currentIndex = -1
+ height: listView.contentHeight
Connections {
onTimelineChanged: completer = null
target: TimelineManager
}
- ColumnLayout {
- anchors.fill: parent
- spacing: 0
-
- Repeater {
- id: repeater
-
- model: completer
+ ListView {
+ id: listView
- delegate: Rectangle {
- color: model.index == popup.currentIndex ? colors.window : colors.alternateBase
- height: chooser.childrenRect.height + 4
- width: chooser.childrenRect.width + 4
+ anchors.fill: parent
+ implicitWidth: contentItem.childrenRect.width
+ model: completer
+ verticalLayoutDirection: popup.bottomToTop ? ListView.BottomToTop : ListView.TopToBottom
- DelegateChooser {
- id: chooser
+ delegate: Rectangle {
+ color: model.index == popup.currentIndex ? colors.highlight : colors.base
+ height: chooser.childrenRect.height + 4
+ implicitWidth: chooser.childrenRect.width + 4
- roleValue: popup.completerName
- anchors.centerIn: parent
+ DelegateChooser {
+ id: chooser
- DelegateChoice {
- roleValue: "user"
+ roleValue: popup.completerName
+ anchors.centerIn: parent
- RowLayout {
- id: del
+ DelegateChoice {
+ roleValue: "user"
- anchors.centerIn: parent
+ RowLayout {
+ id: del
- Avatar {
- height: 24
- width: 24
- displayName: model.displayName
- url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
- }
+ anchors.centerIn: parent
- Label {
- text: model.displayName
- color: colors.text
- }
+ Avatar {
+ height: 24
+ width: 24
+ displayName: model.displayName
+ url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
+ }
+ Label {
+ text: model.displayName
+ color: model.index == popup.currentIndex ? colors.highlightedText : colors.text
}
}
- DelegateChoice {
- roleValue: "emoji"
+ }
- RowLayout {
- id: del
+ DelegateChoice {
+ roleValue: "emoji"
- anchors.centerIn: parent
+ RowLayout {
+ id: del
- Label {
- text: model.unicode
- color: colors.text
- font: Settings.emojiFont
- }
+ anchors.centerIn: parent
- Label {
- text: model.shortName
- color: colors.text
- }
+ Label {
+ text: model.unicode
+ color: model.index == popup.currentIndex ? colors.highlightedText : colors.text
+ font: Settings.emojiFont
+ }
+ Label {
+ text: model.shortName
+ color: model.index == popup.currentIndex ? colors.highlightedText : colors.text
}
}
@@ -141,7 +156,7 @@ Popup {
}
background: Rectangle {
- color: colors.alternateBase
+ color: colors.base
implicitHeight: popup.contentHeight
implicitWidth: popup.contentWidth
}
diff --git a/resources/qml/MessageView.qml b/resources/qml/MessageView.qml
index 71c85276..679c1f50 100644
--- a/resources/qml/MessageView.qml
+++ b/resources/qml/MessageView.qml
@@ -182,7 +182,6 @@ ListView {
Connections {
target: chat
-
onMovementEnded: {
if (y + height + 2 * chat.spacing > chat.contentY + chat.height && y < chat.contentY + chat.height)
chat.model.currentIndex = index;
diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index c23564c1..ac998a81 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -131,7 +131,6 @@ Page {
Connections {
target: TimelineManager
-
onNewDeviceVerificationRequest: {
var dialog = deviceVerificationDialog.createObject(timelineRoot, {
"flow": flow
@@ -142,7 +141,6 @@ Page {
Connections {
target: TimelineManager.timeline
-
onOpenProfile: {
var userProfile = userProfileComponent.createObject(timelineRoot, {
"profile": profile
diff --git a/src/CompletionProxyModel.h b/src/CompletionProxyModel.h
index fa22c61e..ba28e84c 100644
--- a/src/CompletionProxyModel.h
+++ b/src/CompletionProxyModel.h
@@ -43,29 +43,94 @@ struct trie
return ret;
else {
auto temp = t.valuesAndSubvalues(limit - ret.size());
- ret.insert(ret.end(), temp.begin(), temp.end());
+ for (auto &&v : temp) {
+ if (ret.size() >= limit)
+ return ret;
+
+ if (std::find(ret.begin(), ret.end(), v) == ret.end()) {
+ ret.push_back(std::move(v));
+ }
+ }
}
}
return ret;
}
- std::vector<Value> search(const QVector<Key> &keys, size_t limit) const
+ std::vector<Value> search(const QVector<Key> &keys,
+ size_t limit,
+ size_t max_distance = 2) const
{
std::vector<Value> ret;
- auto t = this;
- int i = 0;
- for (; i < (int)keys.size(); i++) {
- if (auto e = t->next.find(keys[i]); e != t->next.end()) {
- t = &e->second;
- } else {
- t = nullptr;
- break;
+ if (!limit)
+ return ret;
+
+ auto append = [&ret, limit](std::vector<Value> &&in) {
+ for (auto &&v : in) {
+ if (ret.size() >= limit)
+ return;
+
+ if (std::find(ret.begin(), ret.end(), v) == ret.end()) {
+ ret.push_back(std::move(v));
+ }
+ }
+ };
+
+ {
+ auto t = this;
+ int i = 0;
+ for (; i < (int)keys.size(); i++) {
+ if (auto e = t->next.find(keys[i]); e != t->next.end()) {
+ t = &e->second;
+ } else {
+ t = nullptr;
+ break;
+ }
+ }
+
+ if (t) {
+ ret = t->valuesAndSubvalues(limit);
}
}
- if (t) {
- ret = t->valuesAndSubvalues(limit);
+ if (max_distance && keys.size() < static_cast<int>(limit) && keys.size() > 1) {
+ max_distance -= 1;
+
+ // swap chars case
+ if (keys.size() >= 2) {
+ auto t = this;
+ for (int i = 1; i >= 0; i--) {
+ if (auto e = t->next.find(keys[i]); e != t->next.end()) {
+ t = &e->second;
+ } else {
+ t = nullptr;
+ break;
+ }
+ }
+
+ if (t) {
+ append(t->search(
+ keys.mid(2), (limit - ret.size()) * 2, max_distance));
+ }
+ }
+
+ // delete character case
+ append(this->search(keys.mid(1), (limit - ret.size()) * 2, max_distance));
+
+ // substitute and insert cases
+ for (const auto &[k, t] : this->next) {
+ if (k == keys[0] || ret.size() >= limit)
+ break;
+
+ // substitute
+ append(this->search(keys.mid(1), limit - ret.size(), max_distance));
+
+ if (ret.size() >= limit)
+ break;
+
+ // insert
+ append(this->search(keys, limit - ret.size(), max_distance));
+ }
}
return ret;
|