1 // This file is part of Desktop App Toolkit,
2 // a set of libraries for developing nice desktop applications.
3 //
4 // For license and copyright information please follow this link:
5 // https://github.com/desktop-app/legal/blob/master/LEGAL
6 //
7 #pragma once
8 
9 #include "base/basic_types.h"
10 #include "base/binary_guard.h"
11 #include "base/qt_adapters.h"
12 #include "emoji.h"
13 
14 #include <QtGui/QPainter>
15 #include <QtGui/QPixmap>
16 
17 #include <rpl/producer.h>
18 
19 namespace Ui {
20 namespace Emoji {
21 namespace internal {
22 
23 [[nodiscard]] QString CacheFileFolder();
24 [[nodiscard]] QString SetDataPath(int id);
25 
26 } // namespace internal
27 
28 void Init();
29 void Clear();
30 
31 void ClearIrrelevantCache();
32 
33 // Thread safe, callback is called on main thread.
34 void SwitchToSet(int id, Fn<void(bool)> callback);
35 
36 [[nodiscard]] int CurrentSetId();
37 [[nodiscard]] int NeedToSwitchBackToId();
38 void ClearNeedSwitchToId();
39 [[nodiscard]] bool SetIsReady(int id);
40 [[nodiscard]] rpl::producer<> Updated();
41 
42 [[nodiscard]] int GetSizeNormal();
43 [[nodiscard]] int GetSizeLarge();
44 #ifdef Q_OS_MAC
45 [[nodiscard]] int GetSizeTouchbar();
46 #endif
47 
48 class One {
49 	struct CreationTag {
50 	};
51 
52 public:
53 	One(One &&other) = default;
One(const QString & id,EmojiPtr original,uint32 index,bool hasPostfix,bool colorizable,const CreationTag &)54 	One(const QString &id, EmojiPtr original, uint32 index, bool hasPostfix, bool colorizable, const CreationTag &)
55 	: _id(id)
56 	, _original(original)
57 	, _index(index)
58 	, _hasPostfix(hasPostfix)
59 	, _colorizable(colorizable) {
60 		Expects(!_colorizable || !colored());
61 	}
62 
id()63 	QString id() const {
64 		return _id;
65 	}
text()66 	QString text() const {
67 		return hasPostfix() ? (_id + QChar(kPostfix)) : _id;
68 	}
69 
colored()70 	bool colored() const {
71 		return (_original != nullptr);
72 	}
original()73 	EmojiPtr original() const {
74 		return _original ? _original : this;
75 	}
nonColoredId()76 	QString nonColoredId() const {
77 		return original()->id();
78 	}
79 
hasPostfix()80 	bool hasPostfix() const {
81 		return _hasPostfix;
82 	}
83 
hasVariants()84 	bool hasVariants() const {
85 		return _colorizable || colored();
86 	}
87 	int variantsCount() const;
88 	int variantIndex(EmojiPtr variant) const;
89 	EmojiPtr variant(int index) const;
90 
index()91 	int index() const {
92 		return _index;
93 	}
sprite()94 	int sprite() const {
95 		return int(_index >> 9);
96 	}
row()97 	int row() const {
98 		return int((_index >> 5) & 0x0FU);
99 	}
column()100 	int column() const {
101 		return int(_index & 0x1FU);
102 	}
103 
toUrl()104 	QString toUrl() const {
105 		return "emoji://e." + QString::number(index());
106 	}
107 
108 private:
109 	const QString _id;
110 	const EmojiPtr _original = nullptr;
111 	const uint32 _index = 0;
112 	const bool _hasPostfix = false;
113 	const bool _colorizable = false;
114 
115 	friend void internal::Init();
116 
117 };
118 
FromUrl(const QString & url)119 [[nodiscard]] inline EmojiPtr FromUrl(const QString &url) {
120 	auto start = qstr("emoji://e.");
121 	if (url.startsWith(start)) {
122 		return internal::ByIndex(base::StringViewMid(url, start.size()).toInt()); // skip emoji://e.
123 	}
124 	return nullptr;
125 }
126 
127 [[nodiscard]] inline EmojiPtr Find(const QChar *start, const QChar *end, int *outLength = nullptr) {
128 	return internal::Find(start, end, outLength);
129 }
130 
131 [[nodiscard]] inline EmojiPtr Find(const QString &text, int *outLength = nullptr) {
132 	return Find(text.constBegin(), text.constEnd(), outLength);
133 }
134 
135 [[nodiscard]] QString IdFromOldKey(uint64 oldKey);
136 
FromOldKey(uint64 oldKey)137 [[nodiscard]] inline EmojiPtr FromOldKey(uint64 oldKey) {
138 	return Find(IdFromOldKey(oldKey));
139 }
140 
ColorIndexFromCode(uint32 code)141 [[nodiscard]] inline int ColorIndexFromCode(uint32 code) {
142 	switch (code) {
143 	case 0xD83CDFFBU: return 1;
144 	case 0xD83CDFFCU: return 2;
145 	case 0xD83CDFFDU: return 3;
146 	case 0xD83CDFFEU: return 4;
147 	case 0xD83CDFFFU: return 5;
148 	}
149 	return 0;
150 }
151 
ColorIndexFromOldKey(uint64 oldKey)152 [[nodiscard]] inline int ColorIndexFromOldKey(uint64 oldKey) {
153 	return ColorIndexFromCode(uint32(oldKey & 0xFFFFFFFFLLU));
154 }
155 
156 QVector<EmojiPtr> GetDefaultRecent();
157 
158 const QPixmap &SinglePixmap(EmojiPtr emoji, int fontHeight);
159 void Draw(QPainter &p, EmojiPtr emoji, int size, int x, int y);
160 
161 class UniversalImages {
162 public:
163 	explicit UniversalImages(int id);
164 
165 	int id() const;
166 	bool ensureLoaded();
167 	void clear();
168 
169 	void draw(QPainter &p, EmojiPtr emoji, int size, int x, int y) const;
170 
171 	// This method must be thread safe and so it is called after
172 	// the _id value is fixed and all _sprites are loaded.
173 	QImage generate(int size, int index) const;
174 
175 private:
176 	const int _id = 0;
177 	std::vector<QImage> _sprites;
178 
179 };
180 
181 [[nodiscard]] const std::shared_ptr<UniversalImages> &SourceImages();
182 void ClearSourceImages(const std::shared_ptr<UniversalImages> &images);
183 
184 } // namespace Emoji
185 } // namespace Ui
186