├── .gitignore ├── EmojiView.qml ├── LICENSE ├── README.md ├── assets ├── sheet_apple_32.png ├── sheet_emojione_32.png ├── sheet_google_32.png └── sheet_twitter_32.png ├── com_cutehacks_emooj.pri ├── com_cutehacks_emooj.qrc ├── com_cutehacks_emooj_apple.qrc ├── com_cutehacks_emooj_emojione.qrc ├── com_cutehacks_emooj_google.qrc ├── com_cutehacks_emooj_twitter.qrc ├── emoji-data.cpp ├── emoji-data.h ├── emojidatamodel.cpp ├── emojidatamodel.h ├── emojiimageprovider.cpp ├── emojiimageprovider.h ├── emooj.h ├── examples └── completion │ ├── completion.pro │ ├── main.cpp │ ├── main.qml │ └── qml.qrc ├── qmldir ├── qpm.json ├── tools └── gendata │ ├── emoji_pretty.json │ ├── gendata.pro │ └── main.cpp ├── unicodeutils.cpp ├── unicodeutils.h ├── utilswrapper.cpp └── utilswrapper.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.pro.user 2 | -------------------------------------------------------------------------------- /EmojiView.qml: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | import QtQuick 2.0 4 | 5 | import com.cutehacks.emooj 1.0 6 | 7 | Rectangle { 8 | property int cellWidth: 32 9 | property int columnCount: 10 10 | property int rowCount: 10 11 | width: cellWidth * columnCount + grid.anchors.margins * 2 12 | height: cellWidth * rowCount + grid.anchors.margins * 2 13 | radius: 5 14 | border.color: "black" 15 | 16 | GridView { 17 | id: grid 18 | anchors.fill: parent 19 | clip: true 20 | cellWidth: 32 21 | cellHeight: 32 22 | anchors.margins: 10 23 | 24 | model: EmojiDataModel { 25 | id: emojiModel 26 | } 27 | 28 | delegate: Rectangle { 29 | width: 32 30 | height: 32 31 | 32 | // Text { 33 | // anchors.fill: parent 34 | // text: unicode 35 | // } 36 | Image { 37 | anchors.fill: parent 38 | source: decoration 39 | } 40 | 41 | MouseArea { 42 | anchors.fill: parent 43 | onClicked: { 44 | emojiModel.emitIMEvent(index); 45 | } 46 | } 47 | } 48 | } 49 | 50 | } 51 | 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jason Barron 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Emooj 2 | 3 | An Emoji helper module for Qt and QML. 4 | 5 | This package uses data from the https://github.com/iamcal/emoji-data project. 6 | 7 | TODO: Write docs :) -------------------------------------------------------------------------------- /assets/sheet_apple_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cutehacks/Emooj/531fbd453c8e873c7041f1f227944bd5ccbdada3/assets/sheet_apple_32.png -------------------------------------------------------------------------------- /assets/sheet_emojione_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cutehacks/Emooj/531fbd453c8e873c7041f1f227944bd5ccbdada3/assets/sheet_emojione_32.png -------------------------------------------------------------------------------- /assets/sheet_google_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cutehacks/Emooj/531fbd453c8e873c7041f1f227944bd5ccbdada3/assets/sheet_google_32.png -------------------------------------------------------------------------------- /assets/sheet_twitter_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cutehacks/Emooj/531fbd453c8e873c7041f1f227944bd5ccbdada3/assets/sheet_twitter_32.png -------------------------------------------------------------------------------- /com_cutehacks_emooj.pri: -------------------------------------------------------------------------------- 1 | 2 | RESOURCES += \ 3 | $$PWD/com_cutehacks_emooj.qrc 4 | 5 | isEmpty(EMOOJ_SHEETS) { 6 | EMOOJ_SHEETS = twitter 7 | ios|macx: EMOOJ_SHEETS += apple 8 | android: EMOOJ_SHEETS += google 9 | } 10 | 11 | contains(EMOOJ_SHEETS, apple) { 12 | RESOURCES += $$PWD/com_cutehacks_emooj_apple.qrc 13 | DEFINES += EMOOJ_APPLE 14 | } 15 | contains(EMOOJ_SHEETS, google) { 16 | RESOURCES += $$PWD/com_cutehacks_emooj_google.qrc 17 | DEFINES += EMOOJ_GOOGLE 18 | } 19 | contains(EMOOJ_SHEETS, emojione) { 20 | RESOURCES += $$PWD/com_cutehacks_emooj_emojione.qrc 21 | DEFINES += EMOOJ_EMOJIONE 22 | } 23 | contains(EMOOJ_SHEETS, twitter) { 24 | RESOURCES += $$PWD/com_cutehacks_emooj_twitter.qrc 25 | DEFINES += EMOOJ_TWITTER 26 | } 27 | 28 | HEADERS += \ 29 | $$PWD/unicodeutils.h \ 30 | $$PWD/emooj.h \ 31 | $$PWD/emojidatamodel.h \ 32 | $$PWD/utilswrapper.h \ 33 | $$PWD/emoji-data.h \ 34 | $$PWD/emojiimageprovider.h 35 | 36 | SOURCES += \ 37 | $$PWD/unicodeutils.cpp \ 38 | $$PWD/emojidatamodel.cpp \ 39 | $$PWD/utilswrapper.cpp \ 40 | $$PWD/emoji-data.cpp \ 41 | $$PWD/emojiimageprovider.cpp 42 | 43 | INCLUDEPATH += $$PWD 44 | -------------------------------------------------------------------------------- /com_cutehacks_emooj.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | qmldir 4 | EmojiView.qml 5 | 6 | 7 | -------------------------------------------------------------------------------- /com_cutehacks_emooj_apple.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | assets/sheet_apple_32.png 4 | 5 | 6 | -------------------------------------------------------------------------------- /com_cutehacks_emooj_emojione.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | assets/sheet_emojione_32.png 4 | 5 | 6 | -------------------------------------------------------------------------------- /com_cutehacks_emooj_google.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | assets/sheet_google_32.png 4 | 5 | 6 | -------------------------------------------------------------------------------- /com_cutehacks_emooj_twitter.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | assets/sheet_twitter_32.png 4 | 5 | 6 | -------------------------------------------------------------------------------- /emoji-data.h: -------------------------------------------------------------------------------- 1 | #ifndef EMOJIDATA_H 2 | // Copyright 2016 Cutehacks AS. All rights reserved. 3 | // License can be found in the LICENSE file. 4 | #define EMOJIDATA_H 5 | 6 | #include 7 | #include 8 | 9 | namespace com { namespace cutehacks { namespace emooj { 10 | 11 | struct Emoji { 12 | QString unicode; 13 | QString name; 14 | QString shortName; 15 | QString category; 16 | int sheetX; 17 | int sheetY; 18 | }; 19 | 20 | extern QHash emoticons; 21 | extern QVector emojis; 22 | extern QHash emojiData; 23 | 24 | } } } 25 | 26 | #endif // EMOJIDATA_H 27 | 28 | -------------------------------------------------------------------------------- /emojidatamodel.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | #include 4 | #include 5 | 6 | #include "emojidatamodel.h" 7 | #include "emoji-data.h" 8 | #include "emojiimageprovider.h" 9 | 10 | namespace com { namespace cutehacks { namespace emooj { 11 | 12 | static QHash roles { 13 | {Qt::DisplayRole, "display"}, 14 | {Qt::DecorationRole, "decoration"}, 15 | {EmojiDataModel::UnicodeRole, "unicode"}, 16 | {EmojiDataModel::NameRole, "name"}, 17 | {EmojiDataModel::ShortNameRole, "shortName"}, 18 | {EmojiDataModel::CategoryRole, "category"}, 19 | {EmojiDataModel::SheetXRole, "sheetX"}, 20 | {EmojiDataModel::SheetYRole, "sheetY"} 21 | }; 22 | 23 | EmojiDataModel::EmojiDataModel(QObject *parent) : QAbstractItemModel(parent) 24 | { 25 | 26 | } 27 | 28 | QModelIndex EmojiDataModel::index(int row, int, const QModelIndex &) const 29 | { 30 | return createIndex(row, 0); 31 | } 32 | 33 | QModelIndex EmojiDataModel::parent(const QModelIndex &) const 34 | { 35 | return QModelIndex(); 36 | } 37 | 38 | int EmojiDataModel::rowCount(const QModelIndex &) const 39 | { 40 | return emojis.length(); 41 | } 42 | 43 | int EmojiDataModel::columnCount(const QModelIndex &) const 44 | { 45 | return 1; 46 | } 47 | 48 | QVariant EmojiDataModel::data(const QModelIndex &index, int role) const 49 | { 50 | QString key = emojis[index.row()]; 51 | if (role == UnicodeRole || role == Qt::DisplayRole) 52 | return key; 53 | 54 | Emoji data = emojiData[key]; 55 | switch (role) { 56 | case Qt::ToolTipRole: 57 | case NameRole: 58 | return data.name; 59 | case Qt::EditRole: // Used for completion for QCompleter 60 | case ShortNameRole: 61 | return data.shortName; 62 | case CategoryRole: 63 | return data.category; 64 | case SheetXRole: 65 | return data.sheetX; 66 | case SheetYRole: 67 | return data.sheetY; 68 | case Qt::DecorationRole: 69 | return EmojiImageProvider::urlForCoords(data.sheetX, data.sheetY); 70 | break; 71 | } 72 | return key; 73 | } 74 | 75 | 76 | QHash EmojiDataModel::roleNames() const 77 | { 78 | return roles; 79 | } 80 | 81 | void EmojiDataModel::emitIMEvent(int i) 82 | { 83 | if (i >= 0 && i < emojis.length()) { 84 | QString key = emojis[i]; 85 | Emoji data = emojiData[key]; 86 | QInputMethodEvent *e = new QInputMethodEvent( 87 | QString(), 88 | QList()); 89 | e->setCommitString(data.unicode); 90 | qApp->sendEvent(qApp->focusObject(), e); 91 | } 92 | } 93 | 94 | } } } 95 | -------------------------------------------------------------------------------- /emojidatamodel.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | #ifndef EMOJIDATAMODEL_H 4 | #define EMOJIDATAMODEL_H 5 | 6 | #include 7 | 8 | namespace com { namespace cutehacks { namespace emooj { 9 | 10 | class EmojiDataModel : public QAbstractItemModel 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | enum EmojiRoles { 16 | UnicodeRole = Qt::UserRole + 1000, 17 | NameRole, 18 | ShortNameRole, 19 | CategoryRole, 20 | SheetXRole, 21 | SheetYRole 22 | }; 23 | 24 | EmojiDataModel(QObject *parent = 0); 25 | QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; 26 | QModelIndex parent(const QModelIndex &child) const; 27 | int rowCount(const QModelIndex &parent) const; 28 | int columnCount(const QModelIndex &parent) const; 29 | QVariant data(const QModelIndex &index, int role) const; 30 | QHash roleNames() const; 31 | 32 | public slots: 33 | void emitIMEvent(int); 34 | }; 35 | 36 | } } } 37 | 38 | #endif // EMOJIDATAMODEL_H 39 | -------------------------------------------------------------------------------- /emojiimageprovider.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | #include 4 | 5 | #include "emojiimageprovider.h" 6 | 7 | namespace com { namespace cutehacks { namespace emooj { 8 | 9 | static const QString SHEET_BASE(QStringLiteral(":/com/cutehacks/emooj/assets/")); 10 | static const QString SHEET_APPLE(SHEET_BASE % QStringLiteral("sheet_apple_32.png")); 11 | static const QString SHEET_GOOGLE(SHEET_BASE % QStringLiteral("sheet_google_32.png")); 12 | static const QString SHEET_EMOJIONE(SHEET_BASE % QStringLiteral("sheet_emojione_32.png")); 13 | static const QString SHEET_TWITTER(SHEET_BASE % QStringLiteral("sheet_twitter_32.png")); 14 | 15 | EmojiImageProvider::EmojiImageProvider() : 16 | QQuickImageProvider(QQuickImageProvider::Pixmap) 17 | { 18 | QString sheet; 19 | #ifdef EMOOJ_APPLE 20 | sheet = SHEET_APPLE; 21 | #elif defined(EMOOJ_GOOGLE) 22 | sheet = SHEET_GOOGLE; 23 | #elif defined(EMOOJ_EMOJIONE) 24 | sheet = SHEET_EMOJIONE; 25 | #elif defined(EMOOJ_TWITTER) 26 | sheet = SHEET_TWITTER; 27 | #endif 28 | if (!m_src.load(sheet)) 29 | qWarning("Could not load emooj sheet."); 30 | } 31 | 32 | QPixmap EmojiImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &) 33 | { 34 | static const int SIZE = 32; 35 | QStringList parts = id.split("/"); 36 | int coords[2]; 37 | for (int i = 0; i < 2; i++) 38 | coords[i] = parts[i].toInt(); 39 | int x = coords[0] * SIZE; 40 | int y = coords[1] * SIZE; 41 | if (size) 42 | *size = QSize(SIZE, SIZE); 43 | return m_src.copy(x, y, SIZE, SIZE); 44 | } 45 | 46 | QString EmojiImageProvider::urlForCoords(int sheetX, int sheetY) 47 | { 48 | return QString("image://com.cutehacks.emooj/%1/%2").arg(sheetX).arg(sheetY); 49 | } 50 | 51 | } } } 52 | -------------------------------------------------------------------------------- /emojiimageprovider.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | #ifndef EMOJIIMAGEPROVIDER_H 4 | #define EMOJIIMAGEPROVIDER_H 5 | 6 | #include 7 | 8 | namespace com { namespace cutehacks { namespace emooj { 9 | 10 | class EmojiImageProvider : public QQuickImageProvider 11 | { 12 | public: 13 | EmojiImageProvider(); 14 | QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize); 15 | 16 | private: 17 | QPixmap m_src; 18 | 19 | public: 20 | static QString urlForCoords(int sheetX, int sheetY); 21 | }; 22 | 23 | } } } 24 | 25 | #endif // EMOJIIMAGEPROVIDER_H 26 | -------------------------------------------------------------------------------- /emooj.h: -------------------------------------------------------------------------------- 1 | #ifndef EMOOJ_H 2 | #define EMOOJ_H 3 | 4 | #include 5 | // Copyright 2016 Cutehacks AS. All rights reserved. 6 | // License can be found in the LICENSE file. 7 | #include 8 | 9 | #include "utilswrapper.h" 10 | #include "emojidatamodel.h" 11 | #include "emojiimageprovider.h" 12 | 13 | namespace com { namespace cutehacks { namespace emooj { 14 | 15 | static const char* EMOOJ_NS = "com.cutehacks.emooj"; 16 | 17 | static QObject *utilsWrapperFactory(QQmlEngine *engine, QJSEngine *) 18 | { 19 | return new UtilsWrapper(engine); 20 | } 21 | 22 | static void registerEngine(QQmlEngine *engine) 23 | { 24 | qmlRegisterSingletonType(EMOOJ_NS, 25 | 1, 0, "UnicodeUtils", com::cutehacks::emooj::utilsWrapperFactory); 26 | qmlRegisterType(EMOOJ_NS, 1, 0, "EmojiDataModel"); 27 | engine->addImageProvider(EMOOJ_NS, new EmojiImageProvider); 28 | } 29 | 30 | 31 | } } } 32 | 33 | #endif // EMOOJ_H 34 | 35 | -------------------------------------------------------------------------------- /examples/completion/completion.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | 3 | QT += qml quick 4 | CONFIG += c++11 5 | 6 | SOURCES += main.cpp 7 | 8 | RESOURCES += qml.qrc 9 | 10 | # Additional import path used to resolve QML modules in Qt Creator's code model 11 | QML_IMPORT_PATH = 12 | 13 | include(../../com_cutehacks_emooj.pri) 14 | -------------------------------------------------------------------------------- /examples/completion/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "emooj.h" 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | QGuiApplication app(argc, argv); 9 | 10 | QQmlApplicationEngine engine; 11 | engine.addImportPath("qrc:/"); 12 | com::cutehacks::emooj::registerEngine(&engine); 13 | engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 14 | 15 | return app.exec(); 16 | } 17 | 18 | -------------------------------------------------------------------------------- /examples/completion/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.3 2 | import QtQuick.Window 2.2 3 | import QtQuick.Controls 1.4 4 | 5 | import com.cutehacks.emooj 1.0 6 | 7 | Window { 8 | visible: true 9 | width: 480 10 | height: 640 11 | 12 | ListView { 13 | id: list 14 | 15 | anchors { 16 | top: parent.top 17 | topMargin: 10 18 | left: parent.left 19 | right: parent.right 20 | bottom: input.top 21 | } 22 | 23 | delegate: Item { 24 | height: childrenRect.height + 10 25 | anchors { 26 | left: parent.left 27 | right: parent.right 28 | } 29 | 30 | Rectangle { 31 | id: rect 32 | color: "lightblue" 33 | radius: 5 34 | y: 10 35 | height: message.height * 2 + 5 36 | width: Math.min(parent.width - 20, message.implicitWidth + 20) 37 | anchors { 38 | right: parent.right 39 | rightMargin: 10 40 | } 41 | } 42 | 43 | Text { 44 | y: 20 45 | id: message 46 | text: content 47 | anchors { 48 | right: parent.right 49 | rightMargin: 20 50 | } 51 | wrapMode: Text.WordWrap 52 | textFormat: Qt.RichText 53 | verticalAlignment: Text.AlignVCenter 54 | horizontalAlignment: Text.AlignRight 55 | } 56 | 57 | Text { 58 | text: "length: " + UnicodeUtils.length(original) 59 | anchors.top: rect.bottom 60 | anchors.right: message.right 61 | font.pixelSize: message.font.pixelSize * .75 62 | color: "darkgray" 63 | } 64 | 65 | } 66 | 67 | model: ListModel { id: model } 68 | } 69 | 70 | TextField { 71 | id: input 72 | anchors { 73 | bottom: parent.bottom 74 | left: parent.left 75 | right: sendButton.left 76 | } 77 | focus: true 78 | 79 | ToolButton { 80 | id: openEmojis 81 | anchors { 82 | right: parent.right 83 | verticalCenter: parent.verticalCenter 84 | } 85 | text: "😃" 86 | onClicked: emojiView.visible = !emojiView.visible 87 | } 88 | 89 | EmojiView { 90 | id: emojiView 91 | visible: false 92 | anchors.right: openEmojis.right 93 | anchors.bottom: openEmojis.top 94 | } 95 | } 96 | 97 | Button { 98 | id: sendButton 99 | text: "Send" 100 | anchors { 101 | bottom: parent.bottom 102 | right: parent.right 103 | } 104 | 105 | onClicked: { 106 | console.log(input.text) 107 | var item = { 108 | original: input.text, 109 | content: UnicodeUtils.replaceEmojis(input.text, function(emoji) { 110 | console.log(JSON.stringify(emoji, null, 4)); 111 | return "" 115 | }) 116 | }; 117 | model.append(item); 118 | input.text = ""; 119 | } 120 | } 121 | } 122 | 123 | -------------------------------------------------------------------------------- /examples/completion/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /qmldir: -------------------------------------------------------------------------------- 1 | module com.cutehacks.emooj 2 | EmojiView 1.0 EmojiView.qml 3 | 4 | -------------------------------------------------------------------------------- /qpm.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.cutehacks.emooj", 3 | "description": "Views, models and helpers for working with Emoji \uDCA9\uD83D", 4 | "author": { 5 | "name": "Jason Barron", 6 | "email": "jason@cutehacks.com" 7 | }, 8 | "repository": { 9 | "type": "GITHUB", 10 | "url": "https://github.com/Cutehacks/emooj.git" 11 | }, 12 | "version": { 13 | "label": "0.2.0" 14 | }, 15 | "dependencies": [ 16 | ], 17 | "license": "MIT", 18 | "pri_filename": "com_cutehacks_emooj.pri", 19 | "webpage": "https://github.com/Cutehacks/emooj" 20 | } 21 | -------------------------------------------------------------------------------- /tools/gendata/gendata.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | TARGET = gendata 3 | INCLUDEPATH += . 4 | 5 | SOURCES += main.cpp 6 | 7 | CONFIG += c++11 8 | 9 | DEFINES += SRCDIR=\\\"$$clean_path($$PWD/../..)\\\" 10 | 11 | macx:CONFIG -= app_bundle 12 | win32:CONFIG += console 13 | -------------------------------------------------------------------------------- /tools/gendata/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "../../unicodeutils.h" 14 | using namespace com::cutehacks::emooj; 15 | 16 | #ifndef SRCDIR 17 | #define SRCDIR "." 18 | #endif 19 | 20 | static const QString EMOJI_FILE(QStringLiteral(SRCDIR) % "/tools/gendata/emoji_pretty.json"); 21 | static const QString DEST_FILE(QStringLiteral(SRCDIR) % "/emoji-data.cpp"); 22 | static const QRegularExpression escapeRe("[ \\-]"); 23 | 24 | int main(int argc, char** argv) 25 | { 26 | QMap emoticons; 27 | QLinkedList > variables; 28 | QMap emojiData; 29 | 30 | QCoreApplication app(argc, argv); 31 | QFile src(EMOJI_FILE); 32 | if (!src.open(QIODevice::ReadOnly)) 33 | qFatal(QString("Could not open file: %1").arg(src.fileName()).toLatin1()); 34 | 35 | QJsonParseError err; 36 | QJsonDocument json = QJsonDocument::fromJson(src.readAll(), &err); 37 | if (err.error != QJsonParseError::NoError) 38 | qFatal(QString("JSON Parse Error: %1").arg(err.errorString()).toLatin1()); 39 | 40 | if (!json.isArray()) 41 | qFatal("JSON was not an array"); 42 | 43 | QFile dest(DEST_FILE); 44 | if (!dest.open(QIODevice::WriteOnly)) 45 | qFatal(QString("Could not open file: %1").arg(dest.fileName()).toLatin1()); 46 | 47 | QTextStream out(&dest); 48 | out << "// This file is generated. Don't bother editing it." << endl; 49 | out << endl; 50 | out << "#include \"emoji-data.h\"" << endl; 51 | out << endl; 52 | out << "namespace com { namespace cutehacks { namespace emooj {" << endl; 53 | 54 | QJsonArray jsa = json.array(); 55 | for (QJsonArray::const_iterator ita = jsa.constBegin(); ita != jsa.constEnd(); ita++) { 56 | QJsonObject emoji = (*ita).toObject(); 57 | 58 | QString unified = emoji["unified"].toString(); 59 | QStringList codePoints = unified.split("-"); 60 | QString encoded; 61 | QString unicode; 62 | foreach (QString cp, codePoints) { 63 | bool ok; 64 | uint ucs4 = cp.toUInt(&ok, 16); 65 | if (ok) { 66 | unicode = unicode % QString::fromUcs4(&ucs4, 1); 67 | if (ucs4 >= 0x100) { // Windows won't let us use universal characters for basic chars 68 | encoded = encoded % QString("\\U%1").arg(ucs4, 8, 16, QLatin1Char('0')); 69 | } else { 70 | encoded = encoded % QString::fromUcs4(&ucs4, 1); 71 | } 72 | } 73 | } 74 | 75 | QString variable = emoji["name"].toString().replace(escapeRe, "_"); 76 | if (variable.isEmpty()) 77 | variable = emoji["short_name"].toString().toUpper().replace(escapeRe, "_"); 78 | variables.append(QPair(variable, encoded)); 79 | 80 | Emoji data = { 81 | variable, 82 | emoji["name"].toString(), 83 | emoji["short_name"].toString(), 84 | emoji["category"].toString(), 85 | emoji["sheet_x"].toInt(), 86 | emoji["sheet_y"].toInt() 87 | }; 88 | 89 | emojiData.insert(variable, data); 90 | 91 | if (!emoji["skin-variations"].isUndefined()) { 92 | QJsonObject skinVariations = emoji["skin-variations"].toObject(); 93 | } 94 | 95 | QJsonValue text = emoji["text"]; 96 | if (!text.isUndefined() && !text.isNull()) { 97 | emoticons.insert(text.toString(), variable); 98 | } 99 | 100 | QJsonValue texts = emoji["texts"]; 101 | if (texts.isArray()) { 102 | QJsonArray textsArray = texts.toArray(); 103 | for (QJsonArray::const_iterator textsIt = textsArray.constBegin(); 104 | textsIt != textsArray.constEnd(); textsIt++) { 105 | 106 | QString ascii = (*textsIt).toString(); 107 | ascii.replace("\\", "\\\\"); 108 | 109 | emoticons.insert(ascii, variable); 110 | } 111 | } 112 | } 113 | 114 | // Generate output file 115 | 116 | // Declare static string variables to reduce duplication 117 | out << endl; 118 | for (QLinkedList >::const_iterator vIt = variables.constBegin(); 119 | vIt != variables.constEnd(); vIt++) { 120 | 121 | out << "static const QString " << (*vIt).first << "(QStringLiteral(\"" << (*vIt).second << "\"));" << endl; 122 | } 123 | 124 | // Declare a staticly initialized vector of all the strings 125 | out << endl << endl; 126 | out << "QVector emojis {" << endl; 127 | for (QLinkedList >::const_iterator vIt = variables.constBegin(); 128 | vIt != variables.constEnd(); ) { 129 | 130 | out << " " << (*vIt).first; 131 | vIt++; 132 | if (vIt != variables.constEnd()) 133 | out << ","; 134 | out << endl; 135 | } 136 | out << "};"; 137 | 138 | // Declare a staticly initialized hashmap of the emoji data 139 | out << endl << endl; 140 | out << "QHash emojiData {" << endl; 141 | for (QMap::const_iterator eIt = emojiData.constBegin(); 142 | eIt != emojiData.constEnd(); ) { 143 | 144 | Emoji data = eIt.value(); 145 | out << " " << "{" << eIt.key() << ", Emoji {" << endl; 146 | out << " " << data.unicode << "," << endl; 147 | out << " \"" << data.name << "\"," << endl; 148 | out << " \"" << data.shortName << "\"," << endl; 149 | out << " \"" << data.category << "\"," << endl; 150 | out << " " << data.sheetX << "," << endl; 151 | out << " " << data.sheetY << "}" << endl; 152 | out << " }"; 153 | 154 | eIt++; 155 | if (eIt != emojiData.constEnd()) 156 | out << ","; 157 | out << endl; 158 | } 159 | out << "};"; 160 | 161 | 162 | // Declare a static hash of ASCII emoticons to Unicode Emojis 163 | out << endl << endl; 164 | out << "QHash emoticons {" << endl; 165 | for (QMap::const_iterator eIt = emoticons.constBegin(); 166 | eIt != emoticons.constEnd(); ) { 167 | 168 | out << " " << "{\"" << eIt.key() << "\", " << eIt.value() << "}"; 169 | eIt++; 170 | if (eIt != emoticons.constEnd()) 171 | out << ","; 172 | out << endl; 173 | } 174 | out << "};"; 175 | 176 | out << endl; 177 | out << endl; 178 | out << "} } }" << endl; 179 | 180 | dest.close(); 181 | 182 | return 0; 183 | } 184 | -------------------------------------------------------------------------------- /unicodeutils.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "unicodeutils.h" 9 | #include "emoji-data.h" 10 | 11 | namespace com { namespace cutehacks { namespace emooj { 12 | 13 | // Following regex from Instagram: http://instagram-engineering.tumblr.com/post/118304328152/emojineering-part-2-implementing-hashtag-emoji 14 | static const QString pattern(QStringLiteral("[\U00002712\U00002714\U00002716\U0000271d\U00002721\U00002728\U00002733\U00002734\U00002744\U00002747\U0000274c\U0000274e\U00002753-\U00002755\U00002757\U00002763\U00002764\U00002795-\U00002797\U000027a1\U000027b0\U000027bf\U00002934\U00002935\U00002b05-\U00002b07\U00002b1b\U00002b1c\U00002b50\U00002b55\U00003030\U0000303d\U0001f004\U0001f0cf\U0001f170\U0001f171\U0001f17e\U0001f17f\U0001f18e\U0001f191-\U0001f19a\U0001f201\U0001f202\U0001f21a\U0001f22f\U0001f232-\U0001f23a\U0001f250\U0001f251\U0001f300-\U0001f321\U0001f324-\U0001f393\U0001f396\U0001f397\U0001f399-\U0001f39b\U0001f39e-\U0001f3f0\U0001f3f3-\U0001f3f5\U0001f3f7-\U0001f4fd\U0001f4ff-\U0001f53d\U0001f549-\U0001f54e\U0001f550-\U0001f567\U0001f56f\U0001f570\U0001f573-\U0001f579\U0001f587\U0001f58a-\U0001f58d\U0001f590\U0001f595\U0001f596\U0001f5a5\U0001f5a8\U0001f5b1\U0001f5b2\U0001f5bc\U0001f5c2-\U0001f5c4\U0001f5d1-\U0001f5d3\U0001f5dc-\U0001f5de\U0001f5e1\U0001f5e3\U0001f5ef\U0001f5f3\U0001f5fa-\U0001f64f\U0001f680-\U0001f6c5\U0001f6cb-\U0001f6d0\U0001f6e0-\U0001f6e5\U0001f6e9\U0001f6eb\U0001f6ec\U0001f6f0\U0001f6f3\U0001f910-\U0001f918\U0001f980-\U0001f984\U0001f9c0\U00003297\U00003299\U000000a9\U000000ae\U0000203c\U00002049\U00002122\U00002139\U00002194-\U00002199\U000021a9\U000021aa\U0000231a\U0000231b\U00002328\U00002388\U000023cf\U000023e9-\U000023f3\U000023f8-\U000023fa\U000024c2\U000025aa\U000025ab\U000025b6\U000025c0\U000025fb-\U000025fe\U00002600-\U00002604\U0000260e\U00002611\U00002614\U00002615\U00002618\U0000261d\U00002620\U00002622\U00002623\U00002626\U0000262a\U0000262e\U0000262f\U00002638-\U0000263a\U00002648-\U00002653\U00002660\U00002663\U00002665\U00002666\U00002668\U0000267b\U0000267f\U00002692-\U00002694\U00002696\U00002697\U00002699\U0000269b\U0000269c\U000026a0\U000026a1\U000026aa\U000026ab\U000026b0\U000026b1\U000026bd\U000026be\U000026c4\U000026c5\U000026c8\U000026ce\U000026cf\U000026d1\U000026d3\U000026d4\U000026e9\U000026ea\U000026f0-\U000026f5\U000026f7-\U000026fa\U000026fd\U00002702\U00002705\U00002708-\U0000270d\U0000270f]|[#]\U000020e3|[*]\U000020e3|[0]\U000020e3|[1]\U000020e3|[2]\U000020e3|[3]\U000020e3|[4]\U000020e3|[5]\U000020e3|[6]\U000020e3|[7]\U000020e3|[8]\U000020e3|[9]\U000020e3|\U0001f1e6[\U0001f1e8-\U0001f1ec\U0001f1ee\U0001f1f1\U0001f1f2\U0001f1f4\U0001f1f6-\U0001f1fa\U0001f1fc\U0001f1fd\U0001f1ff]|\U0001f1e7[\U0001f1e6\U0001f1e7\U0001f1e9-\U0001f1ef\U0001f1f1-\U0001f1f4\U0001f1f6-\U0001f1f9\U0001f1fb\U0001f1fc\U0001f1fe\U0001f1ff]|\U0001f1e8[\U0001f1e6\U0001f1e8\U0001f1e9\U0001f1eb-\U0001f1ee\U0001f1f0-\U0001f1f5\U0001f1f7\U0001f1fa-\U0001f1ff]|\U0001f1e9[\U0001f1ea\U0001f1ec\U0001f1ef\U0001f1f0\U0001f1f2\U0001f1f4\U0001f1ff]|\U0001f1ea[\U0001f1e6\U0001f1e8\U0001f1ea\U0001f1ec\U0001f1ed\U0001f1f7-\U0001f1fa]|\U0001f1eb[\U0001f1ee-\U0001f1f0\U0001f1f2\U0001f1f4\U0001f1f7]|\U0001f1ec[\U0001f1e6\U0001f1e7\U0001f1e9-\U0001f1ee\U0001f1f1-\U0001f1f3\U0001f1f5-\U0001f1fa\U0001f1fc\U0001f1fe]|\U0001f1ed[\U0001f1f0\U0001f1f2\U0001f1f3\U0001f1f7\U0001f1f9\U0001f1fa]|\U0001f1ee[\U0001f1e8-\U0001f1ea\U0001f1f1-\U0001f1f4\U0001f1f6-\U0001f1f9]|\U0001f1ef[\U0001f1ea\U0001f1f2\U0001f1f4\U0001f1f5]|\U0001f1f0[\U0001f1ea\U0001f1ec-\U0001f1ee\U0001f1f2\U0001f1f3\U0001f1f5\U0001f1f7\U0001f1fc\U0001f1fe\U0001f1ff]|\U0001f1f1[\U0001f1e6-\U0001f1e8\U0001f1ee\U0001f1f0\U0001f1f7-\U0001f1fb\U0001f1fe]|\U0001f1f2[\U0001f1e6\U0001f1e8-\U0001f1ed\U0001f1f0-\U0001f1ff]|\U0001f1f3[\U0001f1e6\U0001f1e8\U0001f1ea-\U0001f1ec\U0001f1ee\U0001f1f1\U0001f1f4\U0001f1f5\U0001f1f7\U0001f1fa\U0001f1ff]|\U0001f1f4\U0001f1f2|\U0001f1f5[\U0001f1e6\U0001f1ea-\U0001f1ed\U0001f1f0-\U0001f1f3\U0001f1f7-\U0001f1f9\U0001f1fc\U0001f1fe]|\U0001f1f6\U0001f1e6|\U0001f1f7[\U0001f1ea\U0001f1f4\U0001f1f8\U0001f1fa\U0001f1fc]|\U0001f1f8[\U0001f1e6-\U0001f1ea\U0001f1ec-\U0001f1f4\U0001f1f7-\U0001f1f9\U0001f1fb\U0001f1fd-\U0001f1ff]|\U0001f1f9[\U0001f1e6\U0001f1e8\U0001f1e9\U0001f1eb-\U0001f1ed\U0001f1ef-\U0001f1f4\U0001f1f7\U0001f1f9\U0001f1fb\U0001f1fc\U0001f1ff]|\U0001f1fa[\U0001f1e6\U0001f1ec\U0001f1f2\U0001f1f8\U0001f1fe\U0001f1ff]|\U0001f1fb[\U0001f1e6\U0001f1e8\U0001f1ea\U0001f1ec\U0001f1ee\U0001f1f3\U0001f1fa]|\U0001f1fc[\U0001f1eb\U0001f1f8]|\U0001f1fd\U0001f1f0|\U0001f1fe[\U0001f1ea\U0001f1f9]|\U0001f1ff[\U0001f1e6\U0001f1f2\U0001f1fc]")); 15 | QRegularExpression UnicodeUtils::emojiRe = QRegularExpression(pattern); 16 | 17 | QString UnicodeUtils::replaceEmojis(const QString &str, AbstractEmojiReplacer *replacer) 18 | { 19 | QString result = str; 20 | QRegularExpressionMatchIterator it = emojiRe.globalMatch(str); 21 | while (it.hasNext()) { 22 | QRegularExpressionMatch match = it.next(); 23 | Emoji emoji = emojiData[match.captured()]; 24 | QString replaceWith = replacer->replace(emoji); 25 | result.replace(match.captured(), replaceWith); 26 | } 27 | 28 | return result; 29 | } 30 | 31 | } } } 32 | 33 | -------------------------------------------------------------------------------- /unicodeutils.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | #ifndef UNICODEUTILS_H 4 | #define UNICODEUTILS_H 5 | 6 | #include 7 | #include 8 | 9 | #include "emoji-data.h" 10 | 11 | namespace com { namespace cutehacks { namespace emooj { 12 | 13 | class AbstractEmojiReplacer 14 | { 15 | public: 16 | virtual ~AbstractEmojiReplacer() {} 17 | virtual QString replace(const Emoji&) const = 0; 18 | }; 19 | 20 | class UnicodeUtils 21 | { 22 | public: 23 | static QString replaceEmojis(const QString&, AbstractEmojiReplacer*); 24 | 25 | private: 26 | static QRegularExpression emojiRe; 27 | }; 28 | 29 | } } } 30 | 31 | #endif // UNICODEUTILS_H 32 | -------------------------------------------------------------------------------- /utilswrapper.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | #include 4 | #include 5 | 6 | #include "utilswrapper.h" 7 | #include "unicodeutils.h" 8 | 9 | namespace com { namespace cutehacks { namespace emooj { 10 | 11 | class JSEmojiReplacer : public AbstractEmojiReplacer 12 | { 13 | 14 | public: 15 | JSEmojiReplacer(QQmlEngine * engine, const QJSValue& callback) : 16 | m_engine(engine), 17 | m_callback(callback) 18 | { } 19 | 20 | QString replace(const Emoji &emoji) const 21 | { 22 | if (m_callback.isCallable()) { 23 | QJSValue args = m_engine->newObject(); 24 | args.setProperty("unicode", emoji.unicode); 25 | args.setProperty("name", emoji.name); 26 | args.setProperty("shortName", emoji.shortName); 27 | args.setProperty("sheetX", emoji.sheetX); 28 | args.setProperty("sheetY", emoji.sheetY); 29 | return m_callback.call( 30 | QJSValueList() << args 31 | ).toString(); 32 | } 33 | qWarning("Second argument to UnicodeUtils.replaceEmojis should be a function"); 34 | return m_callback.toString(); 35 | } 36 | 37 | private: 38 | QQmlEngine *m_engine; 39 | mutable QJSValue m_callback; 40 | }; 41 | 42 | UtilsWrapper::UtilsWrapper(QQmlEngine *engine) : 43 | QObject(engine), 44 | m_engine(engine) 45 | { } 46 | 47 | /*! 48 | Calculate the number of actual symbols in the QString and not 49 | just the length of the string. 50 | */ 51 | int UtilsWrapper::length(const QString &str) const 52 | { 53 | return str.toUcs4().length(); 54 | } 55 | 56 | /*! 57 | Find all emoijs in 'src' and replace them with the result returned by 58 | callback. 59 | */ 60 | QString UtilsWrapper::replaceEmojis(const QString &src, QJSValue callback) 61 | { 62 | JSEmojiReplacer replacer(m_engine, callback); 63 | return UnicodeUtils::replaceEmojis(src, &replacer); 64 | } 65 | 66 | } } } 67 | 68 | -------------------------------------------------------------------------------- /utilswrapper.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | #ifndef UTILSWRAPPER_H 4 | #define UTILSWRAPPER_H 5 | 6 | #include 7 | #include 8 | 9 | class QQmlEngine; 10 | 11 | namespace com { namespace cutehacks { namespace emooj { 12 | 13 | class UtilsWrapper : public QObject 14 | { 15 | Q_OBJECT 16 | 17 | public: 18 | explicit UtilsWrapper(QQmlEngine *engine = 0); 19 | 20 | Q_INVOKABLE int length(const QString&) const; 21 | Q_INVOKABLE QString replaceEmojis(const QString&, QJSValue); 22 | 23 | private: 24 | QQmlEngine *m_engine; 25 | }; 26 | 27 | } } } 28 | 29 | #endif // UTILSWRAPPER_H 30 | --------------------------------------------------------------------------------