1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4 
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #pragma once
9 
10 #include "data/data_wall_paper.h"
11 #include "data/data_cloud_themes.h"
12 #include "ui/style/style_core_palette.h"
13 
14 namespace style {
15 struct colorizer;
16 } // namespace style
17 
18 namespace Main {
19 class Session;
20 } // namespace Main
21 
22 namespace Window {
23 class Controller;
24 } // namespace Window
25 
26 namespace Ui {
27 struct ChatThemeBackground;
28 } // namespace Ui
29 
30 namespace Window {
31 namespace Theme {
32 
33 inline constexpr auto kThemeSchemeSizeLimit = 1024 * 1024;
34 inline constexpr auto kThemeBackgroundSizeLimit = 4 * 1024 * 1024;
35 
36 struct ParsedTheme;
37 
38 [[nodiscard]] bool IsEmbeddedTheme(const QString &path);
39 
40 struct Object {
41 	QString pathRelative;
42 	QString pathAbsolute;
43 	QByteArray content;
44 	Data::CloudTheme cloud;
45 };
46 struct Cached {
47 	QByteArray colors;
48 	QByteArray background;
49 	bool tiled = false;
50 	int32 paletteChecksum = 0;
51 	int32 contentChecksum = 0;
52 };
53 struct Saved {
54 	Object object;
55 	Cached cache;
56 };
57 bool Initialize(Saved &&saved);
58 void Uninitialize();
59 
60 struct Instance {
61 	style::palette palette;
62 	QImage background;
63 	Cached cached;
64 	bool tiled = false;
65 };
66 
67 struct Preview {
68 	Object object;
69 	Instance instance;
70 	QImage preview;
71 };
72 
73 bool Apply(
74 	const QString &filepath,
75 	const Data::CloudTheme &cloud = Data::CloudTheme());
76 bool Apply(std::unique_ptr<Preview> preview);
77 void ApplyDefaultWithPath(const QString &themePath);
78 bool ApplyEditedPalette(const QByteArray &content);
79 void KeepApplied();
80 void KeepFromEditor(
81 	const QByteArray &originalContent,
82 	const ParsedTheme &originalParsed,
83 	const Data::CloudTheme &cloud,
84 	const QByteArray &themeContent,
85 	const ParsedTheme &themeParsed,
86 	const QImage &background);
87 QString NightThemePath();
88 [[nodiscard]] bool IsNightMode();
89 void SetNightModeValue(bool nightMode);
90 [[nodiscard]] rpl::producer<bool> IsNightModeValue();
91 void ToggleNightMode();
92 void ToggleNightMode(const QString &themePath);
93 void ToggleNightModeWithConfirmation(
94 	not_null<Controller*> window,
95 	Fn<void()> toggle);
96 void ResetToSomeDefault();
97 [[nodiscard]] bool IsNonDefaultBackground();
98 void Revert();
99 
100 [[nodiscard]] rpl::producer<bool> IsThemeDarkValue();
101 
102 [[nodiscard]] QString EditingPalettePath();
103 
104 // NB! This method looks to Core::App().settings() to get colorizer by 'file'.
105 bool LoadFromFile(
106 	const QString &path,
107 	not_null<Instance*> out,
108 	Cached *outCache,
109 	QByteArray *outContent);
110 bool LoadFromFile(
111 	const QString &path,
112 	not_null<Instance*> out,
113 	Cached *outCache,
114 	QByteArray *outContent,
115 	const style::colorizer &colorizer);
116 bool LoadFromContent(
117 	const QByteArray &content,
118 	not_null<Instance*> out,
119 	Cached *outCache);
120 
121 struct BackgroundUpdate {
122 	enum class Type {
123 		New,
124 		Changed,
125 		Start,
126 		TestingTheme,
127 		RevertingTheme,
128 		ApplyingTheme,
129 		ApplyingEdit,
130 	};
131 
BackgroundUpdateBackgroundUpdate132 	BackgroundUpdate(Type type, bool tiled) : type(type), tiled(tiled) {
133 	}
paletteChangedBackgroundUpdate134 	[[nodiscard]] bool paletteChanged() const {
135 		return (type == Type::TestingTheme)
136 			|| (type == Type::RevertingTheme)
137 			|| (type == Type::ApplyingEdit)
138 			|| (type == Type::New);
139 	}
140 	Type type;
141 	bool tiled;
142 };
143 
144 enum class ClearEditing {
145 	Temporary,
146 	RevertChanges,
147 	KeepChanges,
148 };
149 
150 class ChatBackground final {
151 public:
152 	ChatBackground();
153 
updates()154 	[[nodiscard]] rpl::producer<BackgroundUpdate> updates() const {
155 		return _updates.events();
156 	}
157 
158 	void start();
159 
160 	// This method is allowed to (and should) be called before start().
161 	void setThemeData(QImage &&themeImage, bool themeTile);
162 
163 	// This method is setting the default (themed) image if none was set yet.
164 	void set(const Data::WallPaper &paper, QImage image = QImage());
165 	void setTile(bool tile);
166 	void setTileDayValue(bool tile);
167 	void setTileNightValue(bool tile);
168 	void setThemeObject(const Object &object);
169 	[[nodiscard]] const Object &themeObject() const;
170 	[[nodiscard]] std::optional<Data::CloudTheme> editingTheme() const;
171 	void setEditingTheme(const Data::CloudTheme &editing);
172 	void clearEditingTheme(ClearEditing clear = ClearEditing::Temporary);
173 	void reset();
174 
175 	void setTestingTheme(Instance &&theme);
176 	void saveAdjustableColors();
177 	void setTestingDefaultTheme();
178 	void revert();
179 
180 	void appliedEditedPalette();
181 	void downloadingStarted(bool tile);
182 
paper()183 	[[nodiscard]] const Data::WallPaper &paper() const {
184 		return _paper;
185 	}
id()186 	[[nodiscard]] WallPaperId id() const {
187 		return _paper.id();
188 	}
prepared()189 	[[nodiscard]] const QImage &prepared() const {
190 		return _prepared;
191 	}
preparedForTiled()192 	[[nodiscard]] const QImage &preparedForTiled() const {
193 		return _preparedForTiled;
194 	}
195 	[[nodiscard]] std::optional<QColor> colorForFill() const;
196 	[[nodiscard]] QImage gradientForFill() const;
197 	void recacheGradientForFill(QImage gradient);
198 	[[nodiscard]] QImage createCurrentImage() const;
199 	[[nodiscard]] bool tile() const;
200 	[[nodiscard]] bool tileDay() const;
201 	[[nodiscard]] bool tileNight() const;
202 	[[nodiscard]] std::optional<QColor> imageMonoColor() const;
203 	[[nodiscard]] bool nightModeChangeAllowed() const;
204 
205 private:
206 	struct AdjustableColor {
207 		AdjustableColor(style::color data);
208 
209 		style::color item;
210 		QColor original;
211 	};
212 
213 	[[nodiscard]] bool started() const;
214 	void initialRead();
215 	void saveForRevert();
216 	void setPreparedAfterPaper(QImage image);
217 	void setPrepared(QImage original, QImage prepared, QImage gradient);
218 	void prepareImageForTiled();
219 	void writeNewBackgroundSettings();
220 	void setPaper(const Data::WallPaper &paper);
221 
222 	[[nodiscard]] bool adjustPaletteRequired();
223 	void adjustPaletteUsingBackground(const QImage &image);
224 	void adjustPaletteUsingColors(const std::vector<QColor> &colors);
225 	void adjustPaletteUsingColor(QColor color);
226 	void restoreAdjustableColors();
227 
228 	void setNightModeValue(bool nightMode);
229 	[[nodiscard]] bool nightMode() const;
230 	void toggleNightMode(std::optional<QString> themePath);
231 	void reapplyWithNightMode(
232 		std::optional<QString> themePath,
233 		bool newNightMode);
234 	void keepApplied(const Object &object, bool write);
235 	[[nodiscard]] bool isNonDefaultThemeOrBackground();
236 	[[nodiscard]] bool isNonDefaultBackground();
237 	void checkUploadWallPaper();
238 	[[nodiscard]] QImage postprocessBackgroundImage(QImage image);
239 
240 	friend bool IsNightMode();
241 	friend void SetNightModeValue(bool nightMode);
242 	friend void ToggleNightMode();
243 	friend void ToggleNightMode(const QString &themePath);
244 	friend void ResetToSomeDefault();
245 	friend void KeepApplied();
246 	friend void KeepFromEditor(
247 		const QByteArray &originalContent,
248 		const ParsedTheme &originalParsed,
249 		const Data::CloudTheme &cloud,
250 		const QByteArray &themeContent,
251 		const ParsedTheme &themeParsed,
252 		const QImage &background);
253 	friend bool IsNonDefaultBackground();
254 
255 	Main::Session *_session = nullptr;
256 	rpl::event_stream<BackgroundUpdate> _updates;
257 	Data::WallPaper _paper = Data::details::UninitializedWallPaper();
258 	std::optional<QColor> _paperColor;
259 	QImage _gradient;
260 	QImage _original;
261 	QImage _prepared;
262 	QImage _preparedForTiled;
263 	bool _nightMode = false;
264 	bool _tileDayValue = false;
265 	bool _tileNightValue = true;
266 	std::optional<bool> _localStoredTileDayValue;
267 	std::optional<bool> _localStoredTileNightValue;
268 
269 	std::optional<QColor> _imageMonoColor;
270 
271 	Object _themeObject;
272 	QImage _themeImage;
273 	bool _themeTile = false;
274 	std::optional<Data::CloudTheme> _editingTheme;
275 
276 	Data::WallPaper _paperForRevert
277 		= Data::details::UninitializedWallPaper();
278 	QImage _originalForRevert;
279 	bool _tileForRevert = false;
280 
281 	std::vector<AdjustableColor> _adjustableColors;
282 	FullMsgId _wallPaperUploadId;
283 	mtpRequestId _wallPaperRequestId = 0;
284 	rpl::lifetime _wallPaperUploadLifetime;
285 
286 	rpl::lifetime _lifetime;
287 
288 };
289 
290 [[nodiscard]] ChatBackground *Background();
291 
292 bool ReadPaletteValues(
293 	const QByteArray &content,
294 	Fn<bool(QLatin1String name, QLatin1String value)> callback);
295 
296 } // namespace Theme
297 } // namespace Window
298