533 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			533 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
This file is part of Telegram Desktop,
 | 
						|
the official desktop application for the Telegram messaging service.
 | 
						|
 | 
						|
For license and copyright information please follow this link:
 | 
						|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
						|
*/
 | 
						|
#include "window/themes/window_themes_embedded.h"
 | 
						|
 | 
						|
#include "window/themes/window_theme.h"
 | 
						|
#include "storage/serialize_common.h"
 | 
						|
#include "core/application.h"
 | 
						|
#include "core/core_settings.h"
 | 
						|
 | 
						|
namespace Window {
 | 
						|
namespace Theme {
 | 
						|
namespace {
 | 
						|
 | 
						|
constexpr auto kMaxAccentColors = 3;
 | 
						|
constexpr auto kEnoughLightnessForContrast = 64;
 | 
						|
 | 
						|
const auto kColorizeIgnoredKeys = base::flat_set<QLatin1String>{ {
 | 
						|
	qstr("boxTextFgGood"),
 | 
						|
	qstr("boxTextFgError"),
 | 
						|
	qstr("historyPeer1NameFg"),
 | 
						|
	qstr("historyPeer1NameFgSelected"),
 | 
						|
	qstr("historyPeer1UserpicBg"),
 | 
						|
	qstr("historyPeer2NameFg"),
 | 
						|
	qstr("historyPeer2NameFgSelected"),
 | 
						|
	qstr("historyPeer2UserpicBg"),
 | 
						|
	qstr("historyPeer3NameFg"),
 | 
						|
	qstr("historyPeer3NameFgSelected"),
 | 
						|
	qstr("historyPeer3UserpicBg"),
 | 
						|
	qstr("historyPeer4NameFg"),
 | 
						|
	qstr("historyPeer4NameFgSelected"),
 | 
						|
	qstr("historyPeer4UserpicBg"),
 | 
						|
	qstr("historyPeer5NameFg"),
 | 
						|
	qstr("historyPeer5NameFgSelected"),
 | 
						|
	qstr("historyPeer5UserpicBg"),
 | 
						|
	qstr("historyPeer6NameFg"),
 | 
						|
	qstr("historyPeer6NameFgSelected"),
 | 
						|
	qstr("historyPeer6UserpicBg"),
 | 
						|
	qstr("historyPeer7NameFg"),
 | 
						|
	qstr("historyPeer7NameFgSelected"),
 | 
						|
	qstr("historyPeer7UserpicBg"),
 | 
						|
	qstr("historyPeer8NameFg"),
 | 
						|
	qstr("historyPeer8NameFgSelected"),
 | 
						|
	qstr("historyPeer8UserpicBg"),
 | 
						|
	qstr("msgFile1Bg"),
 | 
						|
	qstr("msgFile1BgDark"),
 | 
						|
	qstr("msgFile1BgOver"),
 | 
						|
	qstr("msgFile1BgSelected"),
 | 
						|
	qstr("msgFile2Bg"),
 | 
						|
	qstr("msgFile2BgDark"),
 | 
						|
	qstr("msgFile2BgOver"),
 | 
						|
	qstr("msgFile2BgSelected"),
 | 
						|
	qstr("msgFile3Bg"),
 | 
						|
	qstr("msgFile3BgDark"),
 | 
						|
	qstr("msgFile3BgOver"),
 | 
						|
	qstr("msgFile3BgSelected"),
 | 
						|
	qstr("msgFile4Bg"),
 | 
						|
	qstr("msgFile4BgDark"),
 | 
						|
	qstr("msgFile4BgOver"),
 | 
						|
	qstr("msgFile4BgSelected"),
 | 
						|
	qstr("mediaviewFileRedCornerFg"),
 | 
						|
	qstr("mediaviewFileYellowCornerFg"),
 | 
						|
	qstr("mediaviewFileGreenCornerFg"),
 | 
						|
	qstr("mediaviewFileBlueCornerFg"),
 | 
						|
} };
 | 
						|
 | 
						|
QColor qColor(std::string_view hex) {
 | 
						|
	Expects(hex.size() == 6);
 | 
						|
 | 
						|
	const auto component = [](char a, char b) {
 | 
						|
		const auto convert = [](char ch) {
 | 
						|
			Expects((ch >= '0' && ch <= '9')
 | 
						|
				|| (ch >= 'A' && ch <= 'F')
 | 
						|
				|| (ch >= 'a' && ch <= 'f'));
 | 
						|
 | 
						|
			return (ch >= '0' && ch <= '9')
 | 
						|
				? int(ch - '0')
 | 
						|
				: int(ch - ((ch >= 'A' && ch <= 'F') ? 'A' : 'a') + 10);
 | 
						|
		};
 | 
						|
		return convert(a) * 16 + convert(b);
 | 
						|
	};
 | 
						|
 | 
						|
	return QColor(
 | 
						|
		component(hex[0], hex[1]),
 | 
						|
		component(hex[2], hex[3]),
 | 
						|
		component(hex[4], hex[5]));
 | 
						|
};
 | 
						|
 | 
						|
Colorizer::Color cColor(std::string_view hex) {
 | 
						|
	const auto q = qColor(hex);
 | 
						|
	auto hue = int();
 | 
						|
	auto saturation = int();
 | 
						|
	auto value = int();
 | 
						|
	q.getHsv(&hue, &saturation, &value);
 | 
						|
	return Colorizer::Color{ hue, saturation, value };
 | 
						|
}
 | 
						|
 | 
						|
} // namespace
 | 
						|
 | 
						|
Colorizer ColorizerFrom(const EmbeddedScheme &scheme, const QColor &color) {
 | 
						|
	using Color = Colorizer::Color;
 | 
						|
	using Pair = std::pair<Color, Color>;
 | 
						|
 | 
						|
	auto result = Colorizer();
 | 
						|
	result.ignoreKeys = kColorizeIgnoredKeys;
 | 
						|
	result.hueThreshold = 15;
 | 
						|
	scheme.accentColor.getHsv(
 | 
						|
		&result.was.hue,
 | 
						|
		&result.was.saturation,
 | 
						|
		&result.was.value);
 | 
						|
	color.getHsv(
 | 
						|
		&result.now.hue,
 | 
						|
		&result.now.saturation,
 | 
						|
		&result.now.value);
 | 
						|
	switch (scheme.type) {
 | 
						|
	case EmbeddedType::DayBlue:
 | 
						|
		result.lightnessMax = 160;
 | 
						|
		break;
 | 
						|
	case EmbeddedType::Night:
 | 
						|
		result.keepContrast = base::flat_map<QLatin1String, Pair>{ {
 | 
						|
			//{ qstr("windowFgActive"), Pair{ cColor("5288c1"), cColor("17212b") } }, // windowBgActive
 | 
						|
			{ qstr("activeButtonFg"), Pair{ cColor("2f6ea5"), cColor("17212b") } }, // activeButtonBg
 | 
						|
			{ qstr("profileVerifiedCheckFg"), Pair{ cColor("5288c1"), cColor("17212b") } }, // profileVerifiedCheckBg
 | 
						|
			{ qstr("overviewCheckFgActive"), Pair{ cColor("5288c1"), cColor("17212b") } }, // overviewCheckBgActive
 | 
						|
			{ qstr("historyFileInIconFg"), Pair{ cColor("3f96d0"), cColor("182533") } }, // msgFileInBg, msgInBg
 | 
						|
			{ qstr("historyFileInIconFgSelected"), Pair{ cColor("6ab4f4"), cColor("2e70a5") } }, // msgFileInBgSelected, msgInBgSelected
 | 
						|
			{ qstr("historyFileInRadialFg"), Pair{ cColor("3f96d0"), cColor("182533") } }, // msgFileInBg, msgInBg
 | 
						|
			{ qstr("historyFileInRadialFgSelected"), Pair{ cColor("6ab4f4"), cColor("2e70a5") } }, // msgFileInBgSelected, msgInBgSelected
 | 
						|
			{ qstr("historyFileOutIconFg"), Pair{ cColor("4c9ce2"), cColor("2b5278") } }, // msgFileOutBg, msgOutBg
 | 
						|
			{ qstr("historyFileOutIconFgSelected"), Pair{ cColor("58abf3"), cColor("2e70a5") } }, // msgFileOutBgSelected, msgOutBgSelected
 | 
						|
			{ qstr("historyFileOutRadialFg"), Pair{ cColor("4c9ce2"), cColor("2b5278") } }, // msgFileOutBg, msgOutBg
 | 
						|
			{ qstr("historyFileOutRadialFgSelected"), Pair{ cColor("58abf3"), cColor("2e70a5") } }, // msgFileOutBgSelected, msgOutBgSelected
 | 
						|
		} };
 | 
						|
		result.lightnessMin = 64;
 | 
						|
		break;
 | 
						|
	case EmbeddedType::NightGreen:
 | 
						|
		result.keepContrast = base::flat_map<QLatin1String, Pair>{ {
 | 
						|
			//{ qstr("windowFgActive"), Pair{ cColor("3fc1b0"), cColor("282e33") } }, // windowBgActive, windowBg
 | 
						|
			{ qstr("activeButtonFg"), Pair{ cColor("2da192"), cColor("282e33") } }, // activeButtonBg, windowBg
 | 
						|
			{ qstr("profileVerifiedCheckFg"), Pair{ cColor("3fc1b0"), cColor("282e33") } }, // profileVerifiedCheckBg, windowBg
 | 
						|
			{ qstr("overviewCheckFgActive"), Pair{ cColor("3fc1b0"), cColor("282e33") } }, // overviewCheckBgActive
 | 
						|
			{ qstr("callIconFg"), Pair{ cColor("5ad1c1"), cColor("26282c") } }, // callAnswerBg, callBg
 | 
						|
		} };
 | 
						|
		result.lightnessMin = 64;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	const auto nowLightness = color.lightness();
 | 
						|
	const auto limitedLightness = std::clamp(
 | 
						|
		nowLightness,
 | 
						|
		result.lightnessMin,
 | 
						|
		result.lightnessMax);
 | 
						|
	if (limitedLightness != nowLightness) {
 | 
						|
		QColor::fromHsl(
 | 
						|
			color.hslHue(),
 | 
						|
			color.hslSaturation(),
 | 
						|
			limitedLightness).getHsv(
 | 
						|
				&result.now.hue,
 | 
						|
				&result.now.saturation,
 | 
						|
				&result.now.value);
 | 
						|
	}
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
Colorizer ColorizerForTheme(const QString &absolutePath) {
 | 
						|
	if (absolutePath.isEmpty() || !IsEmbeddedTheme(absolutePath)) {
 | 
						|
		return Colorizer();
 | 
						|
	}
 | 
						|
	const auto schemes = EmbeddedThemes();
 | 
						|
	const auto i = ranges::find(
 | 
						|
		schemes,
 | 
						|
		absolutePath,
 | 
						|
		&EmbeddedScheme::path);
 | 
						|
	if (i == end(schemes)) {
 | 
						|
		return Colorizer();
 | 
						|
	}
 | 
						|
	const auto &colors = Core::App().settings().themesAccentColors();
 | 
						|
	if (const auto accent = colors.get(i->type)) {
 | 
						|
		return ColorizerFrom(*i, *accent);
 | 
						|
	}
 | 
						|
	return Colorizer();
 | 
						|
}
 | 
						|
 | 
						|
[[nodiscard]] std::optional<Colorizer::Color> Colorize(
 | 
						|
		const Colorizer::Color &color,
 | 
						|
		const Colorizer &colorizer) {
 | 
						|
	const auto changeColor = std::abs(color.hue - colorizer.was.hue)
 | 
						|
		< colorizer.hueThreshold;
 | 
						|
	if (!changeColor) {
 | 
						|
		return std::nullopt;
 | 
						|
	}
 | 
						|
	const auto nowHue = color.hue + (colorizer.now.hue - colorizer.was.hue);
 | 
						|
	const auto nowSaturation = ((color.saturation > colorizer.was.saturation)
 | 
						|
		&& (colorizer.now.saturation > colorizer.was.saturation))
 | 
						|
		? (((colorizer.now.saturation * (255 - colorizer.was.saturation))
 | 
						|
			+ ((color.saturation - colorizer.was.saturation)
 | 
						|
				* (255 - colorizer.now.saturation)))
 | 
						|
			/ (255 - colorizer.was.saturation))
 | 
						|
		: ((color.saturation != colorizer.was.saturation)
 | 
						|
			&& (colorizer.was.saturation != 0))
 | 
						|
		? ((color.saturation * colorizer.now.saturation)
 | 
						|
			/ colorizer.was.saturation)
 | 
						|
		: colorizer.now.saturation;
 | 
						|
	const auto nowValue = (color.value > colorizer.was.value)
 | 
						|
		? (((colorizer.now.value * (255 - colorizer.was.value))
 | 
						|
			+ ((color.value - colorizer.was.value)
 | 
						|
				* (255 - colorizer.now.value)))
 | 
						|
			/ (255 - colorizer.was.value))
 | 
						|
		: (color.value < colorizer.was.value)
 | 
						|
		? ((color.value * colorizer.now.value)
 | 
						|
			/ colorizer.was.value)
 | 
						|
		: colorizer.now.value;
 | 
						|
	return Colorizer::Color{
 | 
						|
		((nowHue + 360) % 360),
 | 
						|
		nowSaturation,
 | 
						|
		nowValue
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
[[nodiscard]] std::optional<QColor> Colorize(
 | 
						|
		const QColor &color,
 | 
						|
		const Colorizer &colorizer) {
 | 
						|
	auto hue = 0;
 | 
						|
	auto saturation = 0;
 | 
						|
	auto lightness = 0;
 | 
						|
	color.getHsv(&hue, &saturation, &lightness);
 | 
						|
	const auto result = Colorize(
 | 
						|
		Colorizer::Color{ hue, saturation, lightness },
 | 
						|
		colorizer);
 | 
						|
	if (!result) {
 | 
						|
		return std::nullopt;
 | 
						|
	}
 | 
						|
	const auto &fields = *result;
 | 
						|
	return QColor::fromHsv(fields.hue, fields.saturation, fields.value);
 | 
						|
}
 | 
						|
 | 
						|
void FillColorizeResult(uchar &r, uchar &g, uchar &b, const QColor &color) {
 | 
						|
	auto nowR = 0;
 | 
						|
	auto nowG = 0;
 | 
						|
	auto nowB = 0;
 | 
						|
	color.getRgb(&nowR, &nowG, &nowB);
 | 
						|
	r = uchar(nowR);
 | 
						|
	g = uchar(nowG);
 | 
						|
	b = uchar(nowB);
 | 
						|
}
 | 
						|
 | 
						|
void Colorize(uchar &r, uchar &g, uchar &b, const Colorizer &colorizer) {
 | 
						|
	const auto changed = Colorize(QColor(int(r), int(g), int(b)), colorizer);
 | 
						|
	if (changed) {
 | 
						|
		FillColorizeResult(r, g, b, *changed);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void Colorize(
 | 
						|
		QLatin1String name,
 | 
						|
		uchar &r,
 | 
						|
		uchar &g,
 | 
						|
		uchar &b,
 | 
						|
		const Colorizer &colorizer) {
 | 
						|
	if (colorizer.ignoreKeys.contains(name)) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	const auto i = colorizer.keepContrast.find(name);
 | 
						|
	if (i == end(colorizer.keepContrast)) {
 | 
						|
		Colorize(r, g, b, colorizer);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	const auto check = i->second.first;
 | 
						|
	const auto rgb = QColor(int(r), int(g), int(b));
 | 
						|
	const auto changed = Colorize(rgb, colorizer);
 | 
						|
	const auto checked = Colorize(check, colorizer).value_or(check);
 | 
						|
	const auto lightness = [](QColor hsv) {
 | 
						|
		return hsv.value() - (hsv.value() * hsv.saturation()) / 511;
 | 
						|
	};
 | 
						|
	const auto changedLightness = lightness(changed.value_or(rgb).toHsv());
 | 
						|
	const auto checkedLightness = lightness(
 | 
						|
		QColor::fromHsv(checked.hue, checked.saturation, checked.value));
 | 
						|
	const auto delta = std::abs(changedLightness - checkedLightness);
 | 
						|
	if (delta >= kEnoughLightnessForContrast) {
 | 
						|
		if (changed) {
 | 
						|
			FillColorizeResult(r, g, b, *changed);
 | 
						|
		}
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	const auto replace = i->second.second;
 | 
						|
	const auto result = Colorize(replace, colorizer).value_or(replace);
 | 
						|
	FillColorizeResult(
 | 
						|
		r,
 | 
						|
		g,
 | 
						|
		b,
 | 
						|
		QColor::fromHsv(result.hue, result.saturation, result.value));
 | 
						|
}
 | 
						|
 | 
						|
void Colorize(uint32 &pixel, const Colorizer &colorizer) {
 | 
						|
	const auto chars = reinterpret_cast<uchar*>(&pixel);
 | 
						|
	Colorize(
 | 
						|
		chars[2],
 | 
						|
		chars[1],
 | 
						|
		chars[0],
 | 
						|
		colorizer);
 | 
						|
}
 | 
						|
 | 
						|
void Colorize(QImage &image, const Colorizer &colorizer) {
 | 
						|
	image = std::move(image).convertToFormat(QImage::Format_ARGB32);
 | 
						|
	const auto bytes = image.bits();
 | 
						|
	const auto bytesPerLine = image.bytesPerLine();
 | 
						|
	for (auto line = 0; line != image.height(); ++line) {
 | 
						|
		const auto ints = reinterpret_cast<uint32*>(
 | 
						|
			bytes + line * bytesPerLine);
 | 
						|
		const auto end = ints + image.width();
 | 
						|
		for (auto p = ints; p != end; ++p) {
 | 
						|
			Colorize(*p, colorizer);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void Colorize(EmbeddedScheme &scheme, const Colorizer &colorizer) {
 | 
						|
	const auto colors = {
 | 
						|
		&EmbeddedScheme::background,
 | 
						|
		&EmbeddedScheme::sent,
 | 
						|
		&EmbeddedScheme::received,
 | 
						|
		&EmbeddedScheme::radiobuttonActive,
 | 
						|
		&EmbeddedScheme::radiobuttonInactive
 | 
						|
	};
 | 
						|
	for (const auto color : colors) {
 | 
						|
		if (const auto changed = Colorize(scheme.*color, colorizer)) {
 | 
						|
			scheme.*color = changed->toRgb();
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
QByteArray Colorize(
 | 
						|
		QLatin1String hexColor,
 | 
						|
		const Colorizer &colorizer) {
 | 
						|
	Expects(hexColor.size() == 7 || hexColor.size() == 9);
 | 
						|
 | 
						|
	auto color = qColor(std::string_view(hexColor.data() + 1, 6));
 | 
						|
	const auto changed = Colorize(color, colorizer).value_or(color).toRgb();
 | 
						|
 | 
						|
	auto result = QByteArray();
 | 
						|
	result.reserve(hexColor.size());
 | 
						|
	result.append(hexColor.data()[0]);
 | 
						|
	const auto addHex = [&](int code) {
 | 
						|
		if (code >= 0 && code < 10) {
 | 
						|
			result.append('0' + code);
 | 
						|
		} else if (code >= 10 && code < 16) {
 | 
						|
			result.append('a' + (code - 10));
 | 
						|
		}
 | 
						|
	};
 | 
						|
	const auto addValue = [&](int code) {
 | 
						|
		addHex(code / 16);
 | 
						|
		addHex(code % 16);
 | 
						|
	};
 | 
						|
	addValue(changed.red());
 | 
						|
	addValue(changed.green());
 | 
						|
	addValue(changed.blue());
 | 
						|
	if (hexColor.size() == 9) {
 | 
						|
		result.append(hexColor.data()[7]);
 | 
						|
		result.append(hexColor.data()[8]);
 | 
						|
	}
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
std::vector<EmbeddedScheme> EmbeddedThemes() {
 | 
						|
	return {
 | 
						|
		EmbeddedScheme{
 | 
						|
			EmbeddedType::Default,
 | 
						|
			qColor("9bd494"),
 | 
						|
			qColor("eaffdc"),
 | 
						|
			qColor("ffffff"),
 | 
						|
			qColor("eaffdc"),
 | 
						|
			qColor("ffffff"),
 | 
						|
			tr::lng_settings_theme_classic,
 | 
						|
			QString()
 | 
						|
		},
 | 
						|
		EmbeddedScheme{
 | 
						|
			EmbeddedType::DayBlue,
 | 
						|
			qColor("7ec4ea"),
 | 
						|
			qColor("d7f0ff"),
 | 
						|
			qColor("ffffff"),
 | 
						|
			qColor("d7f0ff"),
 | 
						|
			qColor("ffffff"),
 | 
						|
			tr::lng_settings_theme_day,
 | 
						|
			":/gui/day-blue.tdesktop-theme",
 | 
						|
			qColor("40a7e3")
 | 
						|
		},
 | 
						|
		EmbeddedScheme{
 | 
						|
			EmbeddedType::Night,
 | 
						|
			qColor("485761"),
 | 
						|
			qColor("5ca7d4"),
 | 
						|
			qColor("6b808d"),
 | 
						|
			qColor("6b808d"),
 | 
						|
			qColor("5ca7d4"),
 | 
						|
			tr::lng_settings_theme_tinted,
 | 
						|
			":/gui/night.tdesktop-theme",
 | 
						|
			qColor("5288c1")
 | 
						|
		},
 | 
						|
		EmbeddedScheme{
 | 
						|
			EmbeddedType::NightGreen,
 | 
						|
			qColor("485761"),
 | 
						|
			qColor("6b808d"),
 | 
						|
			qColor("6b808d"),
 | 
						|
			qColor("6b808d"),
 | 
						|
			qColor("75bfb5"),
 | 
						|
			tr::lng_settings_theme_night,
 | 
						|
			":/gui/night-green.tdesktop-theme",
 | 
						|
			qColor("3fc1b0")
 | 
						|
		},
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
std::vector<QColor> DefaultAccentColors(EmbeddedType type) {
 | 
						|
	switch (type) {
 | 
						|
	case EmbeddedType::DayBlue:
 | 
						|
		return {
 | 
						|
			qColor("45bce7"),
 | 
						|
			qColor("52b440"),
 | 
						|
			qColor("d46c99"),
 | 
						|
			qColor("df8a49"),
 | 
						|
			qColor("9978c8"),
 | 
						|
			qColor("c55245"),
 | 
						|
			qColor("687b98"),
 | 
						|
			qColor("dea922"),
 | 
						|
		};
 | 
						|
	case EmbeddedType::Default:
 | 
						|
		return {};
 | 
						|
	case EmbeddedType::Night:
 | 
						|
		return {
 | 
						|
			qColor("58bfe8"),
 | 
						|
			qColor("466f42"),
 | 
						|
			qColor("aa6084"),
 | 
						|
			qColor("a46d3c"),
 | 
						|
			qColor("917bbd"),
 | 
						|
			qColor("ab5149"),
 | 
						|
			qColor("697b97"),
 | 
						|
			qColor("9b834b"),
 | 
						|
		};
 | 
						|
	case EmbeddedType::NightGreen:
 | 
						|
		return {
 | 
						|
			qColor("60a8e7"),
 | 
						|
			qColor("4e9c57"),
 | 
						|
			qColor("ca7896"),
 | 
						|
			qColor("cc925c"),
 | 
						|
			qColor("a58ed2"),
 | 
						|
			qColor("d27570"),
 | 
						|
			qColor("7b8799"),
 | 
						|
			qColor("cbac67"),
 | 
						|
		};
 | 
						|
	}
 | 
						|
	Unexpected("Type in Window::Theme::AccentColors.");
 | 
						|
}
 | 
						|
 | 
						|
QByteArray AccentColors::serialize() const {
 | 
						|
	auto result = QByteArray();
 | 
						|
	if (_data.empty()) {
 | 
						|
		return result;
 | 
						|
	}
 | 
						|
 | 
						|
	const auto count = _data.size();
 | 
						|
	auto size = sizeof(qint32) * (count + 1)
 | 
						|
		+ Serialize::colorSize() * count;
 | 
						|
	result.reserve(size);
 | 
						|
 | 
						|
	auto stream = QDataStream(&result, QIODevice::WriteOnly);
 | 
						|
	stream.setVersion(QDataStream::Qt_5_1);
 | 
						|
	stream << qint32(_data.size());
 | 
						|
	for (const auto &[type, color] : _data) {
 | 
						|
		stream << static_cast<qint32>(type);
 | 
						|
		Serialize::writeColor(stream, color);
 | 
						|
	}
 | 
						|
	stream.device()->close();
 | 
						|
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
bool AccentColors::setFromSerialized(const QByteArray &serialized) {
 | 
						|
	if (serialized.isEmpty()) {
 | 
						|
		_data.clear();
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
	auto copy = QByteArray(serialized);
 | 
						|
	auto stream = QDataStream(©, QIODevice::ReadOnly);
 | 
						|
	stream.setVersion(QDataStream::Qt_5_1);
 | 
						|
 | 
						|
	auto count = qint32();
 | 
						|
	stream >> count;
 | 
						|
	if (stream.status() != QDataStream::Ok) {
 | 
						|
		return false;
 | 
						|
	} else if (count <= 0 || count > kMaxAccentColors) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	auto data = base::flat_map<EmbeddedType, QColor>();
 | 
						|
	for (auto i = 0; i != count; ++i) {
 | 
						|
		auto type = qint32();
 | 
						|
		stream >> type;
 | 
						|
		const auto color = Serialize::readColor(stream);
 | 
						|
		const auto uncheckedType = static_cast<EmbeddedType>(type);
 | 
						|
		switch (uncheckedType) {
 | 
						|
		case EmbeddedType::DayBlue:
 | 
						|
		case EmbeddedType::Night:
 | 
						|
		case EmbeddedType::NightGreen:
 | 
						|
			data.emplace(uncheckedType, color);
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (stream.status() != QDataStream::Ok) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	_data = std::move(data);
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
void AccentColors::set(EmbeddedType type, const QColor &value) {
 | 
						|
	_data.emplace_or_assign(type, value);
 | 
						|
}
 | 
						|
 | 
						|
void AccentColors::clear(EmbeddedType type) {
 | 
						|
	_data.remove(type);
 | 
						|
}
 | 
						|
 | 
						|
std::optional<QColor> AccentColors::get(EmbeddedType type) const {
 | 
						|
	const auto i = _data.find(type);
 | 
						|
	return (i != end(_data)) ? std::make_optional(i->second) : std::nullopt;
 | 
						|
}
 | 
						|
 | 
						|
} // namespace Theme
 | 
						|
} // namespace Window
 |