Init emoji auto completion. #106

square-messages
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
Popup {
property var emojiModel
property var textArea
property string emojiCategory: "people"
EmojiModel {
id: emojiModel
category: emojiCategory
}
ColumnLayout {
anchors.fill: parent
@ -28,23 +24,26 @@ Popup {
clip: true
model: emojiModel.model
model: emojiModel.model[emojiCategory]
delegate: Text {
delegate: ItemDelegate {
width: 36
height: 36
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
contentItem: Text {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pointSize: 20
font.family: "Emoji"
text: modelData
MouseArea {
anchors.fill: parent
onClicked: textArea.insert(textArea.cursorPosition, modelData)
font.pointSize: 20
font.family: "Emoji"
text: modelData.unicode
}
hoverEnabled: true
ToolTip.text: modelData.shortname
ToolTip.visible: hovered
onClicked: textArea.insert(textArea.cursorPosition, modelData.unicode)
}
ScrollBar.vertical: ScrollBar {}
@ -58,14 +57,38 @@ Popup {
}
Row {
EmojiButton { text: "😏"; category: "people" }
EmojiButton { text: "🌲"; category: "nature" }
EmojiButton { text: "🍛"; category: "food"}
EmojiButton { text: "🚁"; category: "activity" }
EmojiButton { text: "🚅"; category: "travel" }
EmojiButton { text: "💡"; category: "objects" }
EmojiButton { text: "🔣"; category: "symbols" }
EmojiButton { text: "🏁"; category: "flags" }
Repeater {
model: ListModel {
ListElement { label: "😏"; category: "people" }
ListElement { label: "🌲"; category: "nature" }
ListElement { label: "🍛"; category: "food"}
ListElement { label: "🚁"; category: "activity" }
ListElement { label: "🚅"; category: "travel" }
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 replyContent
property bool isAutoCompleting
property var autoCompleteModel
property bool isEmojiAutoCompleting
property bool isUserAutoCompleting
property var userAutoCompleteModel
property var emojiAutoCompleteModel
property int autoCompleteBeginPosition
property int autoCompleteEndPosition
@ -41,12 +43,12 @@ Rectangle {
id: userAutoComplete
visible: isAutoCompleting && autoCompleteModel.length !== 0
visible: isUserAutoCompleting && userAutoCompleteModel.length !== 0
contentItem: ListView {
id: userAutoCompleteListView
model: autoCompleteModel
model: userAutoCompleteModel
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 {
width: currentRoom && currentRoom.hasFileUploading ? parent.width * currentRoom.fileUploadingProgress / 100 : 0
height: parent.height
@ -196,27 +250,37 @@ Rectangle {
}
Keys.onBacktabPressed: {
if (isAutoCompleting) {
if (isUserAutoCompleting) {
if (userAutoCompleteListView.currentIndex == 0) userAutoCompleteListView.currentIndex = userAutoCompleteListView.count - 1
else userAutoCompleteListView.currentIndex--
}
if (isEmojiAutoCompleting) {
if (emojiAutoCompleteListView.currentIndex == 0) emojiAutoCompleteListView.currentIndex = emojiAutoCompleteListView.count - 1
else emojiAutoCompleteListView.currentIndex--
}
}
Keys.onTabPressed: {
if (isAutoCompleting) {
if (userAutoCompleteListView.currentIndex + 1 == userAutoCompleteListView.count) userAutoCompleteListView.currentIndex = 0
else userAutoCompleteListView.currentIndex++
} else {
autoCompleteBeginPosition = text.substring(0, cursorPosition).lastIndexOf(" ") + 1
var autoCompletePrefix = text.substring(0, cursorPosition).split(" ").pop()
if (!autoCompletePrefix) return
autoCompleteModel = currentRoom.getUsers(autoCompletePrefix)
if (autoCompleteModel.length === 0) return
isAutoCompleting = true
autoCompleteEndPosition = cursorPosition
}
if (isEmojiAutoCompleting) {
if (emojiAutoCompleteListView.currentIndex + 1 == emojiAutoCompleteListView.count) emojiAutoCompleteListView.currentIndex = 0
else emojiAutoCompleteListView.currentIndex++
replaceAutoComplete(userAutoCompleteListView.currentItem.displayName)
replaceAutoComplete(emojiAutoCompleteListView.currentItem.unicode)
} else {
if (isUserAutoCompleting) {
if (userAutoCompleteListView.currentIndex + 1 == userAutoCompleteListView.count) userAutoCompleteListView.currentIndex = 0
else userAutoCompleteListView.currentIndex++
} else {
autoCompleteBeginPosition = text.substring(0, cursorPosition).lastIndexOf(" ") + 1
var autoCompletePrefix = text.substring(0, cursorPosition).split(" ").pop()
if (!autoCompletePrefix) return
userAutoCompleteModel = currentRoom.getUsers(autoCompletePrefix)
if (userAutoCompleteModel.length === 0) return
isUserAutoCompleting = true
autoCompleteEndPosition = cursorPosition
}
replaceAutoComplete(userAutoCompleteListView.currentItem.displayName)
}
}
onTextChanged: {
@ -225,8 +289,21 @@ Rectangle {
currentRoom.cachedInput = text
if (cursorPosition !== autoCompleteBeginPosition && cursorPosition !== autoCompleteEndPosition) {
isAutoCompleting = false
isUserAutoCompleting = false
isEmojiAutoCompleting = false
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
emojiModel: EmojiModel {
id: emojiModel
}
Material.elevation: 2
textArea: inputField

View File

@ -4,7 +4,6 @@
<file>qml/main.qml</file>
<file>js/md.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/qmldir</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 <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 {
Q_OBJECT
Q_PROPERTY(QVariant model READ getModel NOTIFY categoryChanged)
Q_PROPERTY(QString category READ getCategory WRITE setCategory NOTIFY
categoryChanged)
Q_PROPERTY(QVariantMap model READ getModel CONSTANT)
public:
explicit EmojiModel(QObject *parent = nullptr);
QVariant getModel();
QString getCategory() { return m_category; }
void setCategory(QString category) {
if (category != m_category) {
m_category = category;
emit categoryChanged();
}
}
Q_INVOKABLE QVariantMap getModel();
Q_INVOKABLE QVariantList filterModel(const QString& filter);
private:
static const QStringList people;
static const QStringList nature;
static const QStringList food;
static const QStringList activity;
static const QStringList travel;
static const QStringList objects;
static const QStringList symbols;
static const QStringList flags;
QString m_category = "people";
signals:
void categoryChanged();
public slots:
static const QVariantList people;
static const QVariantList nature;
static const QVariantList food;
static const QVariantList activity;
static const QVariantList travel;
static const QVariantList objects;
static const QVariantList symbols;
static const QVariantList flags;
};
#endif // EMOJIMODEL_H