Init emoji auto completion. #106

This commit is contained in:
Black Hat 2018-11-03 22:22:55 +08:00
parent 87103fcb19
commit db6c814e26
6 changed files with 2934 additions and 1533 deletions

View File

@ -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
}
}

View File

@ -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
}
}
} }
} }
} }

View File

@ -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

View File

@ -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>

File diff suppressed because it is too large Load Diff

View File

@ -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