// // Created by EmsiaetKadosh on 25-1-16. // #pragma once #include "Chars.h" #include "def.h" class LiteralText; class Translator; /** * 注意一些默认值。 * FontID=0,表示继承使用默认字体; * color和background=0xffffffff,表示继承使用默认颜色; */ class RenderableString { friend class LiteralText; struct StringConfig { String text; /** * 注意。-1也即0xffffffff被用于标记使用传入的默认颜色。 * 其余,以0xAARRGGBB的形式标记颜色。 */ unsigned int color = -1; /** * 注意。-1也即0xffffffff被用于标记使用传入的默认颜色。 * 其余,以0xAARRGGBB的形式标记颜色。 */ unsigned int background = -1; wchar idFont = 0; bool bold = false; bool italic = false; bool underline = false; bool strikeThrough = false; explicit StringConfig() = default; void reset() noexcept { idFont = 0; color = -1; background = -1; bold = false; italic = false; underline = false; strikeThrough = false; } [[nodiscard]] StringConfig copyConfig() const noexcept { StringConfig ret; ret.idFont = idFont; ret.color = color; ret.background = background; ret.bold = bold; ret.italic = italic; ret.underline = underline; ret.strikeThrough = strikeThrough; return ret; } [[nodiscard]] StringConfig copy() const noexcept { StringConfig ret; ret.idFont = idFont; ret.text = text; ret.idFont = idFont; ret.color = color; ret.background = background; ret.bold = bold; ret.italic = italic; ret.underline = underline; ret.strikeThrough = strikeThrough; return ret; } [[nodiscard]] String toString() const noexcept { String ret; ret.append(L"#"); ret.append(uitowb16(color)); ret.append(L"."); ret.append(uitowb16(background)); ret.append(L",F"); ret.append(std::to_wstring(idFont)); ret.append(L","); if (bold) ret.append(L"="); if (italic) ret.append(L"/"); if (underline) ret.append(L"_"); if (strikeThrough) ret.append(L"-"); ret.append(L":"); ret.append(text); return ret; } }; List configs; using Iterator = List::iterator; using ConstIterator = List::const_iterator; public: explicit RenderableString(const String& string): RenderableString(string.c_str(), string.length()) {} explicit RenderableString(String&& string) : RenderableString(string.c_str(), string.length()) {} explicit RenderableString(const wchar* const string, const QWORD length = static_cast(-1)) { if (length == -1) parseAppend(string); else parseAppend(string, length); } [[nodiscard]] String toString() const noexcept { String ret; for (const auto& config : configs) { ret.append(config.toString()); ret.append(L";\n"); } return ret; } RenderableString& append(const RenderableString& other) { auto iterator = other.configs.cbegin(); int flags = 0; bool clr = true, bg = true, font = true; unsigned int color = configs.back().color; unsigned int background = configs.back().background; wchar idFont = configs.back().idFont; while (iterator != other.configs.cend()) { if (flags >= 3) break; StringConfig config = iterator->copy(); if (font) { if (config.idFont == idFont || config.idFont != 0) { font = false; ++flags; } else config.idFont = idFont; } if (clr) { if (config.color == color || config.color != 0xffffffff) { clr = false; ++flags; } else config.color = color; } if (bg) { if (config.background == background || config.background != 0xffffffff) { bg = false; ++flags; } else config.background = background; } configs.push_back(std::move(config)); ++iterator; } while (iterator != other.configs.cend()) configs.push_back(iterator->copy()); return *this; } RenderableString& append(const String& other) { parseAppend(other.c_str(), other.length()); return *this; } private: void parseAppend(const wchar* string) noexcept { StringConfig config; if (!configs.empty()) { config = configs.back(); configs.pop_back(); } while (*string != L'\0') { const wchar* start = string; if (!config.text.empty()) { configs.push_back(std::move(config)); config = config.copyConfig(); } while (*string != L'\0' && *string != '\\') ++string; if (*string == L'\0') { if (*start != L'\0') config.text.append(start, string); configs.push_back(std::move(config)); return; } if (start != string) config.text.append(start, string); if (*++string == L'\0') { config.text.append(start, string); configs.push_back(std::move(config)); return; } if (!config.text.empty()) { configs.push_back(std::move(config)); config = config.copyConfig(); } switch (*string) { case L'\\': config.text.append(1, L'\\'); ++string; continue; case L'#': { unsigned long long i = 0; while (i < 9) if (string[i++] == L'\0') goto end; config.color = wtouib16(++string, 8); string += 8; continue; } case L'.': { unsigned long long i = 0; while (i < 9) if (string[i++] == L'\0') goto end; config.background = wtouib16(++string, 8); string += 8; continue; } case L'F': case L'f': if (*++string == L'\0') goto end; config.idFont = *string; break; case L'-': case L's': case L'S': config.strikeThrough = true; break; case L'_': case L'u': case L'U': config.underline = true; break; case L'/': case L'i': case L'I': config.italic = true; break; case L'=': case L'b': case L'B': config.bold = true; break; case L'r': case L'R': if (!config.text.empty()) configs.push_back(std::move(config)); config = StringConfig(); break; default: config.text.append(string - 1, 2); break; } ++string; } end: configs.push_back(std::move(config)); } void parseAppend(const wchar* string, const QWORD length) noexcept { StringConfig config = StringConfig(); if (!configs.empty()) { config = configs.back(); configs.pop_back(); } const wchar* const end = string + length; while (string < end) { const wchar* start = string; if (!config.text.empty()) { configs.push_back(std::move(config)); config = config.copyConfig(); } while (string < end && *string != '\\') ++string; if (string >= end) { if (start != end) config.text.append(start, end); configs.push_back(std::move(config)); return; } if (start != string) config.text.append(start, string); if (++string == end) { config.text.append(start, end); configs.push_back(std::move(config)); return; } if (!config.text.empty()) { configs.push_back(std::move(config)); config = config.copyConfig(); } switch (*string) { case L'\\': config.text.append(1, L'\\'); ++string; continue; case L'#': { if (end - string < 9) break; config.color = wtouib16(++string, 8); string += 8; continue; } case L'.': { if (end - string < 9) break; config.background = wtouib16(++string, 8); string += 8; continue; } case L'F': case L'f': if (++string == end) break; config.idFont = *string; break; case L'-': case L's': case L'S': config.strikeThrough = true; break; case L'_': case L'u': case L'U': config.underline = true; break; case L'/': case L'i': case L'I': config.italic = true; break; case L'=': case L'b': case L'B': config.bold = true; break; case L'r': case L'R': if (!config.text.empty()) configs.push_back(std::move(config)); config = StringConfig(); break; default: config.text.append(string - 1, 2); break; } ++string; } configs.push_back(std::move(config)); } }; interface IText { virtual ~IText() = default; virtual const String& getText() const noexcept = 0; virtual const RenderableString& getRenderableString() const noexcept = 0; }; typedef class LiteralText : public IText { const String string; mutable RenderableString renderableString; public: LiteralText(const String& string): string(string), renderableString(string) {} LiteralText(String&& string): string(std::move(string)), renderableString(string) {} const String& getText() const noexcept override { return string; } const RenderableString& getRenderableString() const noexcept override { return renderableString; } } TranslatedText; class TranslatableText final : public IText { const String idSrc; mutable const LiteralText* target = nullptr; mutable QWORD langConfig = 0; public: explicit TranslatableText(const String& id) : idSrc(id) { std::wcout << L"Lvalue init\n"; } explicit TranslatableText(String&& id) : idSrc(std::move(id)) { std::wcout << L"Rvalue init\n"; } const String& getText() const noexcept override; const RenderableString& getRenderableString() const noexcept override; void refreshText() const noexcept; }; struct Language { Map translateTable; int id = 1; }; class Translator { Map langMap{}; List langList{}; TranslatedText nullText{ L"\\#FF""EE0000" }; String lang = L"zh-cn"; int langConfig = 1; int idLangMax = 1; using IterID = Map::const_iterator; using IterLang = List::const_iterator; public: explicit Translator() { langMap.insert(std::make_pair(String(L"zh-cn"), 1)); } void addLang(const String& lang) noexcept { langMap.insert(std::make_pair(lang, ++idLangMax)); } void addLang(String&& lang) noexcept { langMap.insert(std::make_pair(lang, ++idLangMax)); } void loadLang(); [[nodiscard]] const TranslatedText* getText(const String& id) const noexcept { for (const auto& [translateTable, _] : langList) { IterID iterator = translateTable.find(id); if (iterator != translateTable.cend()) return &iterator->second; } return &nullText; } [[nodiscard]] int getConfigVersion() const noexcept { return langConfig; } }; inline static Translator translator = Translator();