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 = {
"codegen_emoji",
"empty",
true, // forceReGenerate
false, // forceReGenerate
};
QRect computeSourceRect(const QImage &image) {
@ -126,14 +126,14 @@ QString computeId(Id id) {
} // 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);
if (!dir.mkpath(".")) {
common::logError(kErrorCantWritePath, "Command Line") << "can not open path for writing: " << dir.absolutePath().toStdString();
data_ = Data();
}
outputPath_ = dir.absolutePath() + "/emoji_config";
outputPath_ = dir.absolutePath() + "/emoji";
spritePath_ = dir.absolutePath() + "/emoji";
}
@ -142,15 +142,20 @@ int Generator::generate() {
return -1;
}
if (writeImages_) {
#ifdef Q_OS_MAC
if (!writeImages()) {
return -1;
}
return writeImages() ? 0 : -1;
#else // Q_OS_MAC
common::logError(common::kErrorInternal, "Command Line") << "can not generate images in this OS.";
#endif // Q_OS_MAC
}
if (!writeSource()) {
return -1;
}
if (!writeHeader()) {
return -1;
}
return 0;
}
@ -263,11 +268,20 @@ bool Generator::writeSource() {
source_->pushNamespace("Ui").pushNamespace("Emoji").pushNamespace();
source_->stream() << "\
\n\
constexpr auto kCount = " << data_.list.size() << ";\n\
auto WorkingIndex = -1;\n\
\n\
std::vector<One> Items;\n\
\n";
if (!writeInitCode()) {
return false;
}
if (!writeSections()) {
return false;
}
if (!writeFindReplace()) {
return false;
}
if (!writeFind()) {
return false;
}
source_->popNamespace().newline().pushNamespace("internal");
source_->stream() << "\
\n\
@ -275,90 +289,100 @@ EmojiPtr ByIndex(int index) {\n\
return (index >= 0 && index < Items.size()) ? &Items[index] : nullptr;\n\
}\n\
\n\
template <typename ...Args>\n\
inline QString ComputeId(Args... args) {\n\
auto utf16 = { args... };\n\
EmojiPtr FindReplace(const QChar *start, const QChar *end, int *outLength) {\n\
auto index = FindReplaceIndex(start, end, outLength);\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\
result.reserve(utf16.size());\n\
for (auto ch : utf16) {\n\
result.append(QChar(ch));\n\
}\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\
int One::variantsCount() const {\n\
return hasVariants() ? " << colorsCount_ << " : 0;\n\
}\n\
\n\
int One::variantIndex(EmojiPtr variant) const {\n\
return (variant - original());\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\
void Init() {\n\
auto id = IdData;\n\
Items.reserve(base::array_size(Data));\n\
for (auto &data : Data) {\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\
id += data.idSize;\n\
}\n\
}\n\
\n";
source_->popNamespace();
if (!writeGetSections()) {
return false;
}
return source_->finalize();
}
bool Generator::writeInitCode() {
constexpr const char *variantNames[] = {
"dbisOne",
"dbisOneAndQuarter",
"dbisOneAndHalf",
"dbisTwo"
};
source_->stream() << "\
bool Generator::writeHeader() {
auto header = std::make_unique<common::CppFile>(outputPath_ + ".h", project_);
header->pushNamespace("Ui").pushNamespace("Emoji").pushNamespace("internal");
header->stream() << "\
\n\
void Init() {\n\
auto tag = One::CreationTag();\n\
auto scaleForEmoji = cRetina() ? dbisTwo : cScale();\n\
void Init();\n\
\n\
switch (scaleForEmoji) {\n";
auto variantIndex = 0;
for (auto name : variantNames) {
source_->stream() << "\
case " << name << ": WorkingIndex = " << variantIndex++ << "; break;\n";
}
source_->stream() << "\
};\n\
EmojiPtr ByIndex(int index);\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";
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 row = 0;
auto index = 0;
auto variated = -1;
auto coloredCount = 0;
for (auto &item : data_.list) {
source_->stream() << "\
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";
callback(item.id, column, row, item.postfixed, item.variated, item.colored, variated);
if (coloredCount > 0 && (item.variated || !item.colored)) {
if (!colorsCount_) {
colorsCount_ = coloredCount;
@ -385,13 +409,93 @@ void Init() {\n\
}
++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() << "\
}\n";
};\n";
return true;
}
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[] = {
"Section::People",
"Section::Nature",
@ -431,6 +535,7 @@ EmojiPack GetSection(Section section) {\n\
return result;\n\
} break;\n";
auto index = 0;
auto offset = 0;
for (auto name : sectionNames) {
if (index >= int(data_.categories.size())) {
logDataError() << "category " << index << " not found.";
@ -440,29 +545,23 @@ EmojiPack GetSection(Section section) {\n\
source_->stream() << "\
\n\
case " << name << ": {\n\
static auto result = EmojiPack();\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\
static auto result = fillSection(" << offset << ", " << category.size() << ");\n\
return result;\n\
} break;\n";
offset += category.size();
}
source_->stream() << "\
}\n\
return EmojiPack();\n\
}\n";
}\n\
\n";
return true;
}
bool Generator::writeFindReplace() {
source_->stream() << "\
\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\
\n";
@ -479,7 +578,7 @@ EmojiPtr FindReplace(const QChar *start, const QChar *end, int *outLength) {\n\
bool Generator::writeFind() {
source_->stream() << "\
\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\
\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()) << "\treturn &Items[" << item.second << "];\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());
source_->stream() << "\
\n\
return nullptr;\n";
return 0;\n";
return true;
}

View File

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

View File

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

View File

@ -28,6 +28,7 @@ namespace emoji {
struct Options {
QString outputPath = ".";
bool writeImages = false;
};
// 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();
}
});
orderWidgets();
}
void HistoryWidget::start() {

View File

@ -152,7 +152,7 @@ emojiPanAnimation: PanelAnimation(defaultPanelAnimation) {
emojiPanPadding: 12px;
emojiPanSize: size(45px, 41px);
emojiPanWidth: 345px;
emojiPanMaxHeight: 366px;
emojiPanMaxHeight: 720px;
emojiPanShowDuration: 200;
emojiPanDuration: 200;
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
#include "ui/text/text.h"
#include "emoji.h"
namespace Ui {
namespace Emoji {
@ -29,41 +30,6 @@ constexpr auto kPostfix = static_cast<ushort>(0xFE0F);
constexpr auto kPanelPerRow = 7;
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();
class One {
@ -130,7 +96,7 @@ private:
const bool _colorizable = false;
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));
}
int Index();
inline int Size(int index = Index()) {
int sizes[] = { 18, 22, 27, 36, 45 };
return sizes[index];
@ -233,9 +197,6 @@ inline QString Filename(int index = 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) {
if (to > from) {
for (auto &entity : *inOutEntities) {

View File

@ -126,6 +126,21 @@
],
'message': 'codegen_scheme-ing scheme.tl..',
'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': [{
'rule_name': 'codegen_style',