Init emoji auto completion. #106
This commit is contained in:
parent
87103fcb19
commit
db6c814e26
|
@ -1,20 +0,0 @@
|
||||||
import QtQuick 2.9
|
|
||||||
import QtQuick.Controls 2.2
|
|
||||||
|
|
||||||
Text {
|
|
||||||
property string category
|
|
||||||
|
|
||||||
width: 36
|
|
||||||
height: 36
|
|
||||||
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
|
|
||||||
font.pointSize: 20
|
|
||||||
font.family: "Emoji"
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: emojiCategory = category
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,14 +6,10 @@ import QtQuick.Controls.Material 2.2
|
||||||
import Spectral 0.1
|
import Spectral 0.1
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
|
property var emojiModel
|
||||||
property var textArea
|
property var textArea
|
||||||
property string emojiCategory: "people"
|
property string emojiCategory: "people"
|
||||||
|
|
||||||
EmojiModel {
|
|
||||||
id: emojiModel
|
|
||||||
category: emojiCategory
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
|
@ -28,23 +24,26 @@ Popup {
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
model: emojiModel.model
|
model: emojiModel.model[emojiCategory]
|
||||||
|
|
||||||
delegate: Text {
|
delegate: ItemDelegate {
|
||||||
width: 36
|
width: 36
|
||||||
height: 36
|
height: 36
|
||||||
|
|
||||||
|
contentItem: Text {
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
font.pointSize: 20
|
font.pointSize: 20
|
||||||
font.family: "Emoji"
|
font.family: "Emoji"
|
||||||
text: modelData
|
text: modelData.unicode
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: textArea.insert(textArea.cursorPosition, modelData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
|
ToolTip.text: modelData.shortname
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
|
||||||
|
onClicked: textArea.insert(textArea.cursorPosition, modelData.unicode)
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {}
|
ScrollBar.vertical: ScrollBar {}
|
||||||
|
@ -58,14 +57,38 @@ Popup {
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
EmojiButton { text: "😏"; category: "people" }
|
Repeater {
|
||||||
EmojiButton { text: "🌲"; category: "nature" }
|
model: ListModel {
|
||||||
EmojiButton { text: "🍛"; category: "food"}
|
ListElement { label: "😏"; category: "people" }
|
||||||
EmojiButton { text: "🚁"; category: "activity" }
|
ListElement { label: "🌲"; category: "nature" }
|
||||||
EmojiButton { text: "🚅"; category: "travel" }
|
ListElement { label: "🍛"; category: "food"}
|
||||||
EmojiButton { text: "💡"; category: "objects" }
|
ListElement { label: "🚁"; category: "activity" }
|
||||||
EmojiButton { text: "🔣"; category: "symbols" }
|
ListElement { label: "🚅"; category: "travel" }
|
||||||
EmojiButton { text: "🏁"; category: "flags" }
|
ListElement { label: "💡"; category: "objects" }
|
||||||
|
ListElement { label: "🔣"; category: "symbols" }
|
||||||
|
ListElement { label: "🏁"; category: "flags" }
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: ItemDelegate {
|
||||||
|
width: 36
|
||||||
|
height: 36
|
||||||
|
|
||||||
|
contentItem: Text {
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
|
font.pointSize: 20
|
||||||
|
font.family: "Emoji"
|
||||||
|
text: label
|
||||||
|
}
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
|
ToolTip.text: category
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
|
||||||
|
onClicked: emojiCategory = category
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,10 @@ Rectangle {
|
||||||
property string replyEventID
|
property string replyEventID
|
||||||
property string replyContent
|
property string replyContent
|
||||||
|
|
||||||
property bool isAutoCompleting
|
property bool isEmojiAutoCompleting
|
||||||
property var autoCompleteModel
|
property bool isUserAutoCompleting
|
||||||
|
property var userAutoCompleteModel
|
||||||
|
property var emojiAutoCompleteModel
|
||||||
property int autoCompleteBeginPosition
|
property int autoCompleteBeginPosition
|
||||||
property int autoCompleteEndPosition
|
property int autoCompleteEndPosition
|
||||||
|
|
||||||
|
@ -41,12 +43,12 @@ Rectangle {
|
||||||
|
|
||||||
id: userAutoComplete
|
id: userAutoComplete
|
||||||
|
|
||||||
visible: isAutoCompleting && autoCompleteModel.length !== 0
|
visible: isUserAutoCompleting && userAutoCompleteModel.length !== 0
|
||||||
|
|
||||||
contentItem: ListView {
|
contentItem: ListView {
|
||||||
id: userAutoCompleteListView
|
id: userAutoCompleteListView
|
||||||
|
|
||||||
model: autoCompleteModel
|
model: userAutoCompleteModel
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
|
@ -89,6 +91,58 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
x: 0
|
||||||
|
y: -height - 10
|
||||||
|
width: Math.min(emojiAutoCompleteListView.contentWidth, parent.width)
|
||||||
|
height: 36
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
Material.elevation: 2
|
||||||
|
|
||||||
|
id: emojiAutoComplete
|
||||||
|
|
||||||
|
visible: isEmojiAutoCompleting && emojiAutoCompleteModel.length !== 0
|
||||||
|
|
||||||
|
contentItem: ListView {
|
||||||
|
id: emojiAutoCompleteListView
|
||||||
|
|
||||||
|
model: emojiAutoCompleteModel
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
orientation: ListView.Horizontal
|
||||||
|
|
||||||
|
highlightFollowsCurrentItem: true
|
||||||
|
|
||||||
|
highlight: Rectangle {
|
||||||
|
color: Material.accent
|
||||||
|
opacity: 0.4
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: ItemDelegate {
|
||||||
|
property string unicode: modelData.unicode
|
||||||
|
|
||||||
|
width: 36
|
||||||
|
height: 36
|
||||||
|
|
||||||
|
contentItem: Text {
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
|
font.pointSize: 20
|
||||||
|
font.family: "Emoji"
|
||||||
|
text: modelData.unicode
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
emojiAutoCompleteListView.currentIndex = index
|
||||||
|
inputField.replaceAutoComplete(modelData.unicode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: currentRoom && currentRoom.hasFileUploading ? parent.width * currentRoom.fileUploadingProgress / 100 : 0
|
width: currentRoom && currentRoom.hasFileUploading ? parent.width * currentRoom.fileUploadingProgress / 100 : 0
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
@ -196,28 +250,38 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onBacktabPressed: {
|
Keys.onBacktabPressed: {
|
||||||
if (isAutoCompleting) {
|
if (isUserAutoCompleting) {
|
||||||
if (userAutoCompleteListView.currentIndex == 0) userAutoCompleteListView.currentIndex = userAutoCompleteListView.count - 1
|
if (userAutoCompleteListView.currentIndex == 0) userAutoCompleteListView.currentIndex = userAutoCompleteListView.count - 1
|
||||||
else userAutoCompleteListView.currentIndex--
|
else userAutoCompleteListView.currentIndex--
|
||||||
}
|
}
|
||||||
|
if (isEmojiAutoCompleting) {
|
||||||
|
if (emojiAutoCompleteListView.currentIndex == 0) emojiAutoCompleteListView.currentIndex = emojiAutoCompleteListView.count - 1
|
||||||
|
else emojiAutoCompleteListView.currentIndex--
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onTabPressed: {
|
Keys.onTabPressed: {
|
||||||
if (isAutoCompleting) {
|
if (isEmojiAutoCompleting) {
|
||||||
|
if (emojiAutoCompleteListView.currentIndex + 1 == emojiAutoCompleteListView.count) emojiAutoCompleteListView.currentIndex = 0
|
||||||
|
else emojiAutoCompleteListView.currentIndex++
|
||||||
|
|
||||||
|
replaceAutoComplete(emojiAutoCompleteListView.currentItem.unicode)
|
||||||
|
} else {
|
||||||
|
if (isUserAutoCompleting) {
|
||||||
if (userAutoCompleteListView.currentIndex + 1 == userAutoCompleteListView.count) userAutoCompleteListView.currentIndex = 0
|
if (userAutoCompleteListView.currentIndex + 1 == userAutoCompleteListView.count) userAutoCompleteListView.currentIndex = 0
|
||||||
else userAutoCompleteListView.currentIndex++
|
else userAutoCompleteListView.currentIndex++
|
||||||
} else {
|
} else {
|
||||||
autoCompleteBeginPosition = text.substring(0, cursorPosition).lastIndexOf(" ") + 1
|
autoCompleteBeginPosition = text.substring(0, cursorPosition).lastIndexOf(" ") + 1
|
||||||
var autoCompletePrefix = text.substring(0, cursorPosition).split(" ").pop()
|
var autoCompletePrefix = text.substring(0, cursorPosition).split(" ").pop()
|
||||||
if (!autoCompletePrefix) return
|
if (!autoCompletePrefix) return
|
||||||
autoCompleteModel = currentRoom.getUsers(autoCompletePrefix)
|
userAutoCompleteModel = currentRoom.getUsers(autoCompletePrefix)
|
||||||
if (autoCompleteModel.length === 0) return
|
if (userAutoCompleteModel.length === 0) return
|
||||||
isAutoCompleting = true
|
isUserAutoCompleting = true
|
||||||
autoCompleteEndPosition = cursorPosition
|
autoCompleteEndPosition = cursorPosition
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceAutoComplete(userAutoCompleteListView.currentItem.displayName)
|
replaceAutoComplete(userAutoCompleteListView.currentItem.displayName)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
timeoutTimer.restart()
|
timeoutTimer.restart()
|
||||||
|
@ -225,8 +289,21 @@ Rectangle {
|
||||||
currentRoom.cachedInput = text
|
currentRoom.cachedInput = text
|
||||||
|
|
||||||
if (cursorPosition !== autoCompleteBeginPosition && cursorPosition !== autoCompleteEndPosition) {
|
if (cursorPosition !== autoCompleteBeginPosition && cursorPosition !== autoCompleteEndPosition) {
|
||||||
isAutoCompleting = false
|
isUserAutoCompleting = false
|
||||||
|
isEmojiAutoCompleting = false
|
||||||
userAutoCompleteListView.currentIndex = 0
|
userAutoCompleteListView.currentIndex = 0
|
||||||
|
emojiAutoCompleteListView.currentIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var autoCompletePrefix = text.substring(0, cursorPosition).split(" ").pop()
|
||||||
|
if (autoCompletePrefix.startsWith(":")) {
|
||||||
|
if (autoCompletePrefix.length < 3) return
|
||||||
|
autoCompleteBeginPosition = text.substring(0, cursorPosition).lastIndexOf(" ") + 1
|
||||||
|
emojiAutoCompleteModel = emojiModel.filterModel(autoCompletePrefix)
|
||||||
|
if (emojiAutoCompleteModel.length === 0) return
|
||||||
|
console.log("Auto completing emoji.")
|
||||||
|
isEmojiAutoCompleting = true
|
||||||
|
autoCompleteEndPosition = cursorPosition
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,6 +390,10 @@ Rectangle {
|
||||||
|
|
||||||
id: emojiPicker
|
id: emojiPicker
|
||||||
|
|
||||||
|
emojiModel: EmojiModel {
|
||||||
|
id: emojiModel
|
||||||
|
}
|
||||||
|
|
||||||
Material.elevation: 2
|
Material.elevation: 2
|
||||||
|
|
||||||
textArea: inputField
|
textArea: inputField
|
||||||
|
|
1
res.qrc
1
res.qrc
|
@ -4,7 +4,6 @@
|
||||||
<file>qml/main.qml</file>
|
<file>qml/main.qml</file>
|
||||||
<file>js/md.js</file>
|
<file>js/md.js</file>
|
||||||
<file>js/util.js</file>
|
<file>js/util.js</file>
|
||||||
<file>imports/Spectral/Component/Emoji/EmojiButton.qml</file>
|
|
||||||
<file>imports/Spectral/Component/Emoji/EmojiPicker.qml</file>
|
<file>imports/Spectral/Component/Emoji/EmojiPicker.qml</file>
|
||||||
<file>imports/Spectral/Component/Emoji/qmldir</file>
|
<file>imports/Spectral/Component/Emoji/qmldir</file>
|
||||||
<file>imports/Spectral/Component/Timeline/DownloadableContent.qml</file>
|
<file>imports/Spectral/Component/Timeline/DownloadableContent.qml</file>
|
||||||
|
|
4201
src/emojimodel.cpp
4201
src/emojimodel.cpp
File diff suppressed because it is too large
Load Diff
|
@ -3,41 +3,38 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
struct Emoji {
|
||||||
|
Emoji(const QString& u, const QString& s) : unicode(u), shortname(s) {}
|
||||||
|
Emoji() {}
|
||||||
|
|
||||||
|
QString unicode;
|
||||||
|
QString shortname;
|
||||||
|
|
||||||
|
Q_GADGET
|
||||||
|
Q_PROPERTY(QString unicode MEMBER unicode)
|
||||||
|
Q_PROPERTY(QString shortname MEMBER shortname)
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(Emoji)
|
||||||
|
|
||||||
class EmojiModel : public QObject {
|
class EmojiModel : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QVariant model READ getModel NOTIFY categoryChanged)
|
Q_PROPERTY(QVariantMap model READ getModel CONSTANT)
|
||||||
Q_PROPERTY(QString category READ getCategory WRITE setCategory NOTIFY
|
|
||||||
categoryChanged)
|
|
||||||
public:
|
public:
|
||||||
explicit EmojiModel(QObject *parent = nullptr);
|
Q_INVOKABLE QVariantMap getModel();
|
||||||
|
Q_INVOKABLE QVariantList filterModel(const QString& filter);
|
||||||
QVariant getModel();
|
|
||||||
|
|
||||||
QString getCategory() { return m_category; }
|
|
||||||
void setCategory(QString category) {
|
|
||||||
if (category != m_category) {
|
|
||||||
m_category = category;
|
|
||||||
emit categoryChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const QStringList people;
|
static const QVariantList people;
|
||||||
static const QStringList nature;
|
static const QVariantList nature;
|
||||||
static const QStringList food;
|
static const QVariantList food;
|
||||||
static const QStringList activity;
|
static const QVariantList activity;
|
||||||
static const QStringList travel;
|
static const QVariantList travel;
|
||||||
static const QStringList objects;
|
static const QVariantList objects;
|
||||||
static const QStringList symbols;
|
static const QVariantList symbols;
|
||||||
static const QStringList flags;
|
static const QVariantList flags;
|
||||||
|
|
||||||
QString m_category = "people";
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void categoryChanged();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // EMOJIMODEL_H
|
#endif // EMOJIMODEL_H
|
||||||
|
|
Loading…
Reference in New Issue