Generate emoji config by a build action.

Also hopefully optimize the build speed for emoji config.
This commit is contained in:
John Preston 2017-03-31 22:08:25 +03:00
parent a1b53f07d5
commit f1718f1d10
9 changed files with 213 additions and 16838 deletions

View File

@ -46,7 +46,7 @@ constexpr int kErrorCantWritePath = 851;
common::ProjectInfo Project = { common::ProjectInfo Project = {
"codegen_emoji", "codegen_emoji",
"empty", "empty",
true, // forceReGenerate false, // forceReGenerate
}; };
QRect computeSourceRect(const QImage &image) { QRect computeSourceRect(const QImage &image) {
@ -126,14 +126,14 @@ QString computeId(Id id) {
} // namespace } // namespace
Generator::Generator(const Options &options) : project_(Project), data_(PrepareData()) { Generator::Generator(const Options &options) : project_(Project), writeImages_(options.writeImages), data_(PrepareData()) {
QDir dir(options.outputPath); QDir dir(options.outputPath);
if (!dir.mkpath(".")) { if (!dir.mkpath(".")) {
common::logError(kErrorCantWritePath, "Command Line") << "can not open path for writing: " << dir.absolutePath().toStdString(); common::logError(kErrorCantWritePath, "Command Line") << "can not open path for writing: " << dir.absolutePath().toStdString();
data_ = Data(); data_ = Data();
} }
outputPath_ = dir.absolutePath() + "/emoji_config"; outputPath_ = dir.absolutePath() + "/emoji";
spritePath_ = dir.absolutePath() + "/emoji"; spritePath_ = dir.absolutePath() + "/emoji";
} }
@ -142,15 +142,20 @@ int Generator::generate() {
return -1; return -1;
} }
if (writeImages_) {
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
if (!writeImages()) { return writeImages() ? 0 : -1;
return -1; #else // Q_OS_MAC
} common::logError(common::kErrorInternal, "Command Line") << "can not generate images in this OS.";
#endif // Q_OS_MAC #endif // Q_OS_MAC
}
if (!writeSource()) { if (!writeSource()) {
return -1; return -1;
} }
if (!writeHeader()) {
return -1;
}
return 0; return 0;
} }
@ -263,11 +268,20 @@ bool Generator::writeSource() {
source_->pushNamespace("Ui").pushNamespace("Emoji").pushNamespace(); source_->pushNamespace("Ui").pushNamespace("Emoji").pushNamespace();
source_->stream() << "\ source_->stream() << "\
\n\ \n\
constexpr auto kCount = " << data_.list.size() << ";\n\
auto WorkingIndex = -1;\n\
\n\
std::vector<One> Items;\n\ std::vector<One> Items;\n\
\n"; \n";
if (!writeInitCode()) {
return false;
}
if (!writeSections()) {
return false;
}
if (!writeFindReplace()) {
return false;
}
if (!writeFind()) {
return false;
}
source_->popNamespace().newline().pushNamespace("internal"); source_->popNamespace().newline().pushNamespace("internal");
source_->stream() << "\ source_->stream() << "\
\n\ \n\
@ -275,90 +289,100 @@ EmojiPtr ByIndex(int index) {\n\
return (index >= 0 && index < Items.size()) ? &Items[index] : nullptr;\n\ return (index >= 0 && index < Items.size()) ? &Items[index] : nullptr;\n\
}\n\ }\n\
\n\ \n\
template <typename ...Args>\n\ EmojiPtr FindReplace(const QChar *start, const QChar *end, int *outLength) {\n\
inline QString ComputeId(Args... args) {\n\ auto index = FindReplaceIndex(start, end, outLength);\n\
auto utf16 = { args... };\n\ return index ? &Items[index - 1] : nullptr;\n\
}\n\
\n\
EmojiPtr Find(const QChar *start, const QChar *end, int *outLength) {\n\
auto index = FindIndex(start, end, outLength);\n\
return index ? &Items[index - 1] : nullptr;\n\
}\n\
\n\
inline QString ComputeId(gsl::span<ushort> utf16) {\n\
auto result = QString();\n\ auto result = QString();\n\
result.reserve(utf16.size());\n\ result.reserve(utf16.size());\n\
for (auto ch : utf16) {\n\ for (auto ch : utf16) {\n\
result.append(QChar(ch));\n\ result.append(QChar(ch));\n\
}\n\ }\n\
return result;\n\ return result;\n\
}\n";
if (!writeFindReplace()) {
return false;
}
if (!writeFind()) {
return false;
}
source_->popNamespace();
if (!writeInitCode()) {
return false;
}
if (!writeSections()) {
return false;
}
source_->stream() << "\
\n\
int Index() {\n\
return WorkingIndex;\n\
}\n\ }\n\
\n\ \n\
int One::variantsCount() const {\n\ void Init() {\n\
return hasVariants() ? " << colorsCount_ << " : 0;\n\ auto id = IdData;\n\
}\n\ Items.reserve(base::array_size(Data));\n\
\n\ for (auto &data : Data) {\n\
int One::variantIndex(EmojiPtr variant) const {\n\ Items.emplace_back(ComputeId(gsl::make_span(id, data.idSize)), data.column, data.row, data.postfixed, data.variated, data.original ? &Items[data.original - 1] : nullptr, One::CreationTag());\n\
return (variant - original());\n\ id += data.idSize;\n\
}\n\ }\n\
\n\
EmojiPtr One::variant(int index) const {\n\
return (index >= 0 && index <= variantsCount()) ? (original() + index) : this;\n\
}\n\
\n\
int One::index() const {\n\
return (this - &Items[0]);\n\
}\n\ }\n\
\n"; \n";
source_->popNamespace();
if (!writeGetSections()) {
return false;
}
return source_->finalize(); return source_->finalize();
} }
bool Generator::writeInitCode() { bool Generator::writeHeader() {
constexpr const char *variantNames[] = { auto header = std::make_unique<common::CppFile>(outputPath_ + ".h", project_);
"dbisOne", header->pushNamespace("Ui").pushNamespace("Emoji").pushNamespace("internal");
"dbisOneAndQuarter", header->stream() << "\
"dbisOneAndHalf",
"dbisTwo"
};
source_->stream() << "\
\n\ \n\
void Init() {\n\ void Init();\n\
auto tag = One::CreationTag();\n\
auto scaleForEmoji = cRetina() ? dbisTwo : cScale();\n\
\n\ \n\
switch (scaleForEmoji) {\n"; EmojiPtr ByIndex(int index);\n\
auto variantIndex = 0;
for (auto name : variantNames) {
source_->stream() << "\
case " << name << ": WorkingIndex = " << variantIndex++ << "; break;\n";
}
source_->stream() << "\
};\n\
\n\ \n\
Items.reserve(kCount);\n\ EmojiPtr Find(const QChar *ch, const QChar *end, int *outLength = nullptr);\n\
\n\
inline bool IsReplaceEdge(const QChar *ch) {\n\
return true;\n\
\n\
// switch (ch->unicode()) {\n\
// case '.': case ',': case ':': case ';': case '!': case '?': case '#': case '@':\n\
// case '(': case ')': case '[': case ']': case '{': case '}': case '<': case '>':\n\
// case '+': case '=': case '-': case '_': case '*': case '/': case '\\\\': case '^': case '$':\n\
// case '\"': case '\\'':\n\
// case 8212: case 171: case 187: // --, <<, >>\n\
// return true;\n\
// }\n\
// return false;\n\
}\n\
\n\
EmojiPtr FindReplace(const QChar *ch, const QChar *end, int *outLength = nullptr);\n\
\n"; \n";
header->popNamespace().stream() << "\
\n\
enum class Section {\n\
Recent,\n\
People,\n\
Nature,\n\
Food,\n\
Activity,\n\
Travel,\n\
Objects,\n\
Symbols,\n\
};\n\
\n\
int Index();\n\
\n\
int GetSectionCount(Section section);\n\
EmojiPack GetSection(Section section);\n\
\n";
return header->finalize();
}
template <typename Callback>
bool Generator::enumerateWholeList(Callback callback) {
auto column = 0; auto column = 0;
auto row = 0; auto row = 0;
auto index = 0; auto index = 0;
auto variated = -1; auto variated = -1;
auto coloredCount = 0; auto coloredCount = 0;
for (auto &item : data_.list) { for (auto &item : data_.list) {
source_->stream() << "\ callback(item.id, column, row, item.postfixed, item.variated, item.colored, variated);
Items.emplace_back(" << computeId(item.id) << ", " << column << ", " << row << ", " << (item.postfixed ? "true" : "false") << ", " << (item.variated ? "true" : "false") << ", " << (item.colored ? "&Items[" + QString::number(variated) + "]" : "nullptr") << ", tag);\n";
if (coloredCount > 0 && (item.variated || !item.colored)) { if (coloredCount > 0 && (item.variated || !item.colored)) {
if (!colorsCount_) { if (!colorsCount_) {
colorsCount_ = coloredCount; colorsCount_ = coloredCount;
@ -385,13 +409,93 @@ void Init() {\n\
} }
++index; ++index;
} }
return true;
}
bool Generator::writeInitCode() {
source_->stream() << "\
struct DataStruct {\n\
ushort idSize;\n\
ushort column;\n\
ushort row;\n\
ushort original;\n\
bool postfixed;\n\
bool variated;\n\
};\n\
\n\
ushort IdData[] = {";
auto count = 0;
auto fulllength = 0;
if (!enumerateWholeList([this, &count, &fulllength](Id id, int column, int row, bool isPostfixed, bool isVariated, bool isColored, int original) {
for (auto ch : id) {
if (fulllength > 0) source_->stream() << ",";
if (!count++) {
source_->stream() << "\n";
} else {
if (count == 12) {
count = 0;
}
source_->stream() << " ";
}
source_->stream() << "0x" << QString::number(ch.unicode(), 16);
++fulllength;
}
})) {
return false;
}
if (fulllength >= std::numeric_limits<ushort>::max()) {
logDataError() << "Too many IdData elements.";
return false;
}
source_->stream() << " };\n\
\n\
DataStruct Data[] = {\n";
if (!enumerateWholeList([this](Id id, int column, int row, bool isPostfixed, bool isVariated, bool isColored, int original) {
source_->stream() << "\
{ ushort(" << id.size() << "), ushort(" << column << "), ushort(" << row << "), ushort(" << (isColored ? (original + 1) : 0) << "), " << (isPostfixed ? "true" : "false") << ", " << (isVariated ? "true" : "false") << " },\n";
})) {
return false;
}
source_->stream() << "\ source_->stream() << "\
}\n"; };\n";
return true; return true;
} }
bool Generator::writeSections() { bool Generator::writeSections() {
source_->stream() << "\
ushort SectionData[] = {";
auto count = 0, fulllength = 0;
for (auto &category : data_.categories) {
for (auto index : category) {
if (fulllength > 0) source_->stream() << ",";
if (!count++) {
source_->stream() << "\n";
} else {
if (count == 12) {
count = 0;
}
source_->stream() << " ";
}
source_->stream() << index;
++fulllength;
}
}
source_->stream() << " };\n\
\n\
EmojiPack fillSection(int offset, int size) {\n\
auto result = EmojiPack();\n\
result.reserve(size);\n\
for (auto index : gsl::make_span(SectionData + offset, size)) {\n\
result.push_back(&Items[index]);\n\
}\n\
return result;\n\
}\n\n";
return true;
}
bool Generator::writeGetSections() {
constexpr const char *sectionNames[] = { constexpr const char *sectionNames[] = {
"Section::People", "Section::People",
"Section::Nature", "Section::Nature",
@ -431,6 +535,7 @@ EmojiPack GetSection(Section section) {\n\
return result;\n\ return result;\n\
} break;\n"; } break;\n";
auto index = 0; auto index = 0;
auto offset = 0;
for (auto name : sectionNames) { for (auto name : sectionNames) {
if (index >= int(data_.categories.size())) { if (index >= int(data_.categories.size())) {
logDataError() << "category " << index << " not found."; logDataError() << "category " << index << " not found.";
@ -440,29 +545,23 @@ EmojiPack GetSection(Section section) {\n\
source_->stream() << "\ source_->stream() << "\
\n\ \n\
case " << name << ": {\n\ case " << name << ": {\n\
static auto result = EmojiPack();\n\ static auto result = fillSection(" << offset << ", " << category.size() << ");\n\
if (result.isEmpty()) {\n\
result.reserve(" << category.size() << ");\n";
for (auto index : category) {
source_->stream() << "\
result.push_back(&Items[" << index << "]);\n";
}
source_->stream() << "\
}\n\
return result;\n\ return result;\n\
} break;\n"; } break;\n";
offset += category.size();
} }
source_->stream() << "\ source_->stream() << "\
}\n\ }\n\
return EmojiPack();\n\ return EmojiPack();\n\
}\n"; }\n\
\n";
return true; return true;
} }
bool Generator::writeFindReplace() { bool Generator::writeFindReplace() {
source_->stream() << "\ source_->stream() << "\
\n\ \n\
EmojiPtr FindReplace(const QChar *start, const QChar *end, int *outLength) {\n\ int FindReplaceIndex(const QChar *start, const QChar *end, int *outLength) {\n\
auto ch = start;\n\ auto ch = start;\n\
\n"; \n";
@ -479,7 +578,7 @@ EmojiPtr FindReplace(const QChar *start, const QChar *end, int *outLength) {\n\
bool Generator::writeFind() { bool Generator::writeFind() {
source_->stream() << "\ source_->stream() << "\
\n\ \n\
EmojiPtr Find(const QChar *start, const QChar *end, int *outLength) {\n\ int FindIndex(const QChar *start, const QChar *end, int *outLength) {\n\
auto ch = start;\n\ auto ch = start;\n\
\n"; \n";
@ -604,13 +703,13 @@ bool Generator::writeFindFromDictionary(const std::map<QString, int, std::greate
//source_->stream() << tabs(1 + chars.size()) << "if (ch + " << chars.size() << " == end || IsReplaceEdge(*(ch + " << chars.size() << ")) || (ch + " << chars.size() << ")->unicode() == ' ') {\n"; //source_->stream() << tabs(1 + chars.size()) << "if (ch + " << chars.size() << " == end || IsReplaceEdge(*(ch + " << chars.size() << ")) || (ch + " << chars.size() << ")->unicode() == ' ') {\n";
//source_->stream() << tabs(1 + chars.size()) << "\treturn &Items[" << item.second << "];\n"; //source_->stream() << tabs(1 + chars.size()) << "\treturn &Items[" << item.second << "];\n";
//source_->stream() << tabs(1 + chars.size()) << "}\n"; //source_->stream() << tabs(1 + chars.size()) << "}\n";
source_->stream() << tabs(tabsUsed) << "return &Items[" << item.second << "];\n"; source_->stream() << tabs(tabsUsed) << "return " << (item.second + 1) << ";\n";
} }
finishChecksTillKey(QString()); finishChecksTillKey(QString());
source_->stream() << "\ source_->stream() << "\
\n\ \n\
return nullptr;\n"; return 0;\n";
return true; return true;
} }

View File

@ -42,15 +42,21 @@ private:
QImage generateImage(int variantIndex); QImage generateImage(int variantIndex);
bool writeImages(); bool writeImages();
bool writeSource(); bool writeSource();
bool writeHeader();
template <typename Callback>
bool enumerateWholeList(Callback callback);
bool writeInitCode(); bool writeInitCode();
bool writeSections(); bool writeSections();
bool writeGetSections();
bool writeFindReplace(); bool writeFindReplace();
bool writeFind(); bool writeFind();
bool writeFindFromDictionary(const std::map<QString, int, std::greater<QString>> &dictionary, bool skipPostfixes = false); bool writeFindFromDictionary(const std::map<QString, int, std::greater<QString>> &dictionary, bool skipPostfixes = false);
const common::ProjectInfo &project_; const common::ProjectInfo &project_;
int colorsCount_ = 0; int colorsCount_ = 0;
bool writeImages_ = false;
QString outputPath_; QString outputPath_;
QString spritePath_; QString spritePath_;
std::unique_ptr<common::CppFile> source_; std::unique_ptr<common::CppFile> source_;

View File

@ -50,6 +50,8 @@ Options parseOptions() {
} }
} else if (arg.startsWith("-o")) { } else if (arg.startsWith("-o")) {
result.outputPath = arg.mid(2); result.outputPath = arg.mid(2);
} else if (arg == "--images") {
result.writeImages = true;
} }
} }
if (result.outputPath.isEmpty()) { if (result.outputPath.isEmpty()) {

View File

@ -28,6 +28,7 @@ namespace emoji {
struct Options { struct Options {
QString outputPath = "."; QString outputPath = ".";
bool writeImages = false;
}; };
// Parsing failed if inputPath is empty in the result. // Parsing failed if inputPath is empty in the result.

View File

@ -3356,6 +3356,8 @@ HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null<Window::Controller*>
updateControlsVisibility(); updateControlsVisibility();
} }
}); });
orderWidgets();
} }
void HistoryWidget::start() { void HistoryWidget::start() {

View File

@ -152,7 +152,7 @@ emojiPanAnimation: PanelAnimation(defaultPanelAnimation) {
emojiPanPadding: 12px; emojiPanPadding: 12px;
emojiPanSize: size(45px, 41px); emojiPanSize: size(45px, 41px);
emojiPanWidth: 345px; emojiPanWidth: 345px;
emojiPanMaxHeight: 366px; emojiPanMaxHeight: 720px;
emojiPanShowDuration: 200; emojiPanShowDuration: 200;
emojiPanDuration: 200; emojiPanDuration: 200;
emojiPanHover: windowBgOver; emojiPanHover: windowBgOver;

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#pragma once #pragma once
#include "ui/text/text.h" #include "ui/text/text.h"
#include "emoji.h"
namespace Ui { namespace Ui {
namespace Emoji { namespace Emoji {
@ -29,41 +30,6 @@ constexpr auto kPostfix = static_cast<ushort>(0xFE0F);
constexpr auto kPanelPerRow = 7; constexpr auto kPanelPerRow = 7;
constexpr auto kPanelRowsPerPage = 6; constexpr auto kPanelRowsPerPage = 6;
enum class Section {
Recent,
People,
Nature,
Food,
Activity,
Travel,
Objects,
Symbols,
};
namespace internal {
EmojiPtr ByIndex(int index);
EmojiPtr Find(const QChar *ch, const QChar *end, int *outLength = nullptr);
inline bool IsReplaceEdge(const QChar *ch) {
return true;
// switch (ch->unicode()) {
// case '.': case ',': case ':': case ';': case '!': case '?': case '#': case '@':
// case '(': case ')': case '[': case ']': case '{': case '}': case '<': case '>':
// case '+': case '=': case '-': case '_': case '*': case '/': case '\\': case '^': case '$':
// case '"': case '\'':
// case 8212: case 171: case 187: // --, <<, >>
// return true;
// }
// return false;
}
EmojiPtr FindReplace(const QChar *ch, const QChar *end, int *outLength = nullptr);
} // namespace internal
void Init(); void Init();
class One { class One {
@ -130,7 +96,7 @@ private:
const bool _colorizable = false; const bool _colorizable = false;
const EmojiPtr _original = nullptr; const EmojiPtr _original = nullptr;
friend void Init(); friend void internal::Init();
}; };
@ -215,8 +181,6 @@ inline int ColorIndexFromOldKey(uint64 oldKey) {
return ColorIndexFromCode(uint32(oldKey & 0xFFFFFFFFLLU)); return ColorIndexFromCode(uint32(oldKey & 0xFFFFFFFFLLU));
} }
int Index();
inline int Size(int index = Index()) { inline int Size(int index = Index()) {
int sizes[] = { 18, 22, 27, 36, 45 }; int sizes[] = { 18, 22, 27, 36, 45 };
return sizes[index]; return sizes[index];
@ -233,9 +197,6 @@ inline QString Filename(int index = Index()) {
return QString::fromLatin1(EmojiNames[index]); return QString::fromLatin1(EmojiNames[index]);
} }
int GetSectionCount(Section section);
EmojiPack GetSection(Section section);
inline void appendPartToResult(QString &result, const QChar *start, const QChar *from, const QChar *to, EntitiesInText *inOutEntities) { inline void appendPartToResult(QString &result, const QChar *start, const QChar *from, const QChar *to, EntitiesInText *inOutEntities) {
if (to > from) { if (to > from) {
for (auto &entity : *inOutEntities) { for (auto &entity : *inOutEntities) {

View File

@ -126,6 +126,21 @@
], ],
'message': 'codegen_scheme-ing scheme.tl..', 'message': 'codegen_scheme-ing scheme.tl..',
'process_outputs_as_sources': 1, 'process_outputs_as_sources': 1,
}, {
'action_name': 'codegen_emoji',
'inputs': [
'<(PRODUCT_DIR)/codegen_emoji<(exe_ext)',
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/emoji.cpp',
'<(SHARED_INTERMEDIATE_DIR)/emoji.h',
],
'action': [
'<(PRODUCT_DIR)/codegen_emoji<(exe_ext)',
'-o<(SHARED_INTERMEDIATE_DIR)',
],
'message': 'codegen_emoji-ing..',
'process_outputs_as_sources': 1,
}], }],
'rules': [{ 'rules': [{
'rule_name': 'codegen_style', 'rule_name': 'codegen_style',