diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 9c88277fdd..f63bb4db37 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -385,7 +385,7 @@ btnDefIconed: iconedButton { font: font(fsize); opacity: 0.78; - overOpacity: 1; + overOpacity: 1.; textPos: point(0px, 0px); downTextPos: point(0px, 0px); @@ -433,7 +433,7 @@ titleBackButton: iconedButton(btnDefIconed) { width: -30px; height: 39px; - opacity: 1; + opacity: 1.; cursor: cursor(default); textPos: point(23px, 10px); @@ -2189,7 +2189,7 @@ mvDropdown: dropdown(dropdownDef) { shadow: sprite(0px, 0px, 0px, 0px); padding: margins(11px, 12px, 11px, 12px); - border: 0; + border: 0px; width: 182px; } mvButton: iconedButton(btnDefIconed) { @@ -2197,8 +2197,8 @@ mvButton: iconedButton(btnDefIconed) { overBgColor: #505050; font: font(fsize); - opacity: 1; - overOpacity: 1; + opacity: 1.; + overOpacity: 1.; width: -32px; height: 36px; @@ -2298,21 +2298,22 @@ overviewFileExtFont: font(18px semibold); // Mac specific -macAccessory: size(450, 90); +macAccessoryWidth: 450.; +macAccessoryHeight: 90.; macEnableFilterAdd: 2; macEnableFilterTop: 5; macSelectorTop: 6; macAlwaysThisAppTop: 4; macAppHintTop: 8; -macCautionIconSize: size(16, 16); +macCautionIconSize: 16; btnContext: iconedButton(btnDefIconed) { bgColor: white; overBgColor: btnWhiteHover; font: font(14px); - opacity: 1; - overOpacity: 1; + opacity: 1.; + overOpacity: 1.; width: -32px; height: 36px; @@ -2399,8 +2400,8 @@ passcodeSkip: 31px; mentionHeight: 40px; mentionScroll: flatScroll(scrollDef) { - topsh: 0; - bottomsh: 0; + topsh: 0px; + bottomsh: 0px; } mentionPadding: margins(8px, 5px, 8px, 5px); mentionTop: 11px; @@ -2420,7 +2421,7 @@ sessionsHeight: 440px; sessionHeight: 70px; sessionCurrentPadding: margins(0px, 7px, 0px, 4px); sessionCurrentHeight: 118px; -sessionPadding: margins(21px, 10px, 21px, 0); +sessionPadding: margins(21px, 10px, 21px, 0px); sessionNameFont: msgNameFont; sessionActiveFont: msgDateFont; sessionActiveColor: #aaa; diff --git a/Telegram/Resources/style_classes.txt b/Telegram/Resources/style_classes.txt index a3405a0485..27079bf5b3 100644 --- a/Telegram/Resources/style_classes.txt +++ b/Telegram/Resources/style_classes.txt @@ -26,7 +26,7 @@ textStyle { monoFg: color; selectBg: color; selectOverlay: color; - lineHeight: number; + lineHeight: pixels; } linkButton { @@ -42,7 +42,7 @@ sysButton { img: sprite; color: color; overColor: color; - duration: number; + duration: int; } flatButton { @@ -54,16 +54,16 @@ flatButton { overBgColor: color; downBgColor: color; - width: number; - height: number; + width: pixels; + height: pixels; - textTop: number; - overTextTop: number; - downTextTop: number; + textTop: pixels; + overTextTop: pixels; + downTextTop: pixels; font: font; overFont: font; - duration: number; + duration: int; cursor: cursor; } @@ -76,17 +76,17 @@ iconedButton { color: color; bgColor: color; overBgColor: color; - width: number; - height: number; + width: pixels; + height: pixels; font: font; - opacity: number; - overOpacity: number; + opacity: double; + overOpacity: double; textPos: point; downTextPos: point; - duration: number; + duration: int; cursor: cursor; } @@ -95,12 +95,12 @@ flatCheckbox { bgColor: color; disColor: color; - width: number; - height: number; - textTop: number; - textLeft: number; + width: pixels; + height: pixels; + textTop: pixels; + textLeft: pixels; font: font; - duration: number; + duration: int; bgFunc: transition; cursor: cursor; @@ -120,8 +120,8 @@ flatInput { textColor: color; bgColor: color; bgActive: color; - width: number; - height: number; + width: pixels; + height: pixels; textMrg: margins; align: align; font: font; @@ -130,7 +130,7 @@ flatInput { imgRect: sprite; imgPos: point; - borderWidth: number; + borderWidth: pixels; borderColor: color; borderActive: color; borderError: color; @@ -139,8 +139,8 @@ flatInput { phFocusColor: color; phPos: point; phAlign: align; - phShift: number; - phDuration: number; + phShift: pixels; + phDuration: int; phLeftFunc: transition; phAlphaFunc: transition; phColorFunc: transition; @@ -149,7 +149,7 @@ flatInput { flatTextarea { textColor: color; bgColor: color; - width: number; + width: pixels; textMrg: margins; align: align; font: font; @@ -159,8 +159,8 @@ flatTextarea { phFocusColor: color; phPos: point; phAlign: align; - phShift: number; - phDuration: number; + phShift: pixels; + phDuration: int; phLeftFunc: transition; phAlphaFunc: transition; phColorFunc: transition; @@ -172,26 +172,26 @@ flatScroll { barOverColor: color; bgOverColor: color; - round: number; + round: pixels; - width: number; - minHeight: number; - deltax: number; - deltat: number; - deltab: number; + width: pixels; + minHeight: pixels; + deltax: pixels; + deltat: pixels; + deltab: pixels; - topsh: number; - bottomsh: number; + topsh: pixels; + bottomsh: pixels; shColor: color; - duration: number; - hiding: number; + duration: int; + hiding: int; } countryInput { - width: number; - height: number; - top: number; + width: pixels; + height: pixels; + top: pixels; bgColor: color; ptrSize: size; textMrg: margins; @@ -201,53 +201,53 @@ countryInput { slider { color: color; - thikness: number; + thikness: pixels; - width: number; + width: pixels; bar: sprite; } flatLabel { font: font; - minWidth: number; - width: number; + minWidth: pixels; + width: pixels; align: align; } switcher { - border: number; + border: pixels; borderColor: color; bgColor: color; bgHovered: color; bgActive: color; - height: number; + height: pixels; font: font; textColor: color; activeColor: color; - duration: number; + duration: int; } dropdown { - border: number; + border: pixels; borderColor: color; padding: margins; shadow: sprite; - shadowShift: number; + shadowShift: pixels; - duration: number; - width: number; + duration: int; + width: pixels; } PopupMenu { - skip: number; + skip: pixels; shadow: sprite; - shadowShift: number; + shadowShift: pixels; itemBg: color; itemBgOver: color; @@ -261,15 +261,15 @@ PopupMenu { itemFont: font; separatorPadding: margins; - separatorWidth: number; + separatorWidth: pixels; separatorFg: color; arrow: sprite; - duration: number; + duration: int; - widthMin: number; - widthMax: number; + widthMin: pixels; + widthMax: pixels; } Tooltip { @@ -280,18 +280,18 @@ Tooltip { textPadding: margins; shift: point; - skip: number; + skip: pixels; - widthMax: number; - linesMax: number; + widthMax: pixels; + linesMax: int; } botKeyboardButton { - margin: number; - padding: number; - height: number; - textTop: number; - downTextTop: number; + margin: pixels; + padding: pixels; + height: pixels; + textTop: pixels; + downTextTop: pixels; } BoxButton { @@ -300,13 +300,13 @@ BoxButton { textBg: color; // rect of textBg with rounded rect of textBgOver upon it textBgOver: color; - width: number; - height: number; + width: pixels; + height: pixels; - textTop: number; + textTop: pixels; font: font; - duration: number; + duration: int; } Checkbox { @@ -317,16 +317,16 @@ Checkbox { checkFgOver: color; checkFgActive: color; - width: number; - height: number; + width: pixels; + height: pixels; textPosition: point; - diameter: number; - thickness: number; + diameter: pixels; + thickness: pixels; checkIcon: sprite; font: font; - duration: number; + duration: int; } Radiobutton { @@ -337,16 +337,16 @@ Radiobutton { checkFgOver: color; checkFgActive: color; - width: number; - height: number; + width: pixels; + height: pixels; textPosition: point; - diameter: number; - thickness: number; - checkSkip: number; + diameter: pixels; + thickness: pixels; + checkSkip: pixels; font: font; - duration: number; + duration: int; } InputArea { @@ -357,23 +357,23 @@ InputArea { placeholderFgActive: color; placeholderMargins: margins; placeholderAlign: align; - placeholderShift: number; + placeholderShift: pixels; - duration: number; + duration: int; borderFg: color; borderFgActive: color; borderFgError: color; - border: number; - borderActive: number; - borderError: number; + border: pixels; + borderActive: pixels; + borderError: pixels; font: font; - width: number; - heightMin: number; - heightMax: number; + width: pixels; + heightMin: pixels; + heightMax: pixels; } InputField { @@ -385,28 +385,28 @@ InputField { placeholderFgActive: color; placeholderMargins: margins; placeholderAlign: align; - placeholderShift: number; + placeholderShift: pixels; - duration: number; + duration: int; borderFg: color; borderFgActive: color; borderFgError: color; - border: number; - borderActive: number; - borderError: number; + border: pixels; + borderActive: pixels; + borderError: pixels; font: font; - width: number; - height: number; + width: pixels; + height: pixels; iconSprite: sprite; iconPosition: point; } PeerAvatarButton { - size: number; - photoSize: number; + size: pixels; + photoSize: pixels; } diff --git a/Telegram/SourceFiles/codegen/common/basic_tokenized_file.cpp b/Telegram/SourceFiles/codegen/common/basic_tokenized_file.cpp index fb6611e498..2a80bea35a 100644 --- a/Telegram/SourceFiles/codegen/common/basic_tokenized_file.cpp +++ b/Telegram/SourceFiles/codegen/common/basic_tokenized_file.cpp @@ -249,6 +249,32 @@ void BasicTokenizedFile::skipWhitespaces() { tokenStart_ = reader_.currentPtr(); } +LogStream operator<<(LogStream &&stream, BasicTokenizedFile::Token::Type type) { + const char *value = "'invalid'"; + switch (type) { + case Type::Invalid: break; + case Type::Int: value = "'int'"; break; + case Type::Double: value = "'double'"; break; + case Type::String: value = "'string'"; break; + case Type::LeftParenthesis: value = "'('"; break; + case Type::RightParenthesis: value = "')'"; break; + case Type::LeftBrace: value = "'{'"; break; + case Type::RightBrace: value = "'}'"; break; + case Type::LeftBracket: value = "'['"; break; + case Type::RightBracket: value = "']'"; break; + case Type::Colon: value = "':'"; break; + case Type::Semicolon: value = "';'"; break; + case Type::Comma: value = "','"; break; + case Type::Dot: value = "'.'"; break; + case Type::Number: value = "'#'"; break; + case Type::Plus: value = "'+'"; break; + case Type::Minus: value = "'-'"; break; + case Type::Equals: value = "'='"; break; + case Type::Name: value = "'identifier'"; break; + } + return std::forward(stream) << value; +} + LogStream BasicTokenizedFile::logError(int code) const { return reader_.logError(code, lineNumber_); } diff --git a/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h b/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h index 97052783e6..400fc90516 100644 --- a/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h +++ b/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h @@ -147,5 +147,11 @@ private: }; +LogStream operator<<(LogStream &&stream, BasicTokenizedFile::Token::Type type); +template <> +LogStream operator<< (LogStream &&stream, BasicTokenizedFile::Token::Type &&value) = delete; +template <> +LogStream operator<< (LogStream &&stream, const BasicTokenizedFile::Token::Type &value) = delete; + } // namespace common } // namespace codegen diff --git a/Telegram/SourceFiles/codegen/common/logging.h b/Telegram/SourceFiles/codegen/common/logging.h index 019cddd26f..55567ac84d 100644 --- a/Telegram/SourceFiles/codegen/common/logging.h +++ b/Telegram/SourceFiles/codegen/common/logging.h @@ -36,7 +36,7 @@ public: } explicit LogStream(std::ostream &stream) : stream_(&stream) { } - LogStream(LogStream &&other) : stream_(other.stream_) { + LogStream(LogStream &&other) : stream_(other.stream_), final_(other.final_) { other.final_ = false; } std::ostream *stream() const { diff --git a/Telegram/SourceFiles/codegen/style/generator.h b/Telegram/SourceFiles/codegen/style/generator.h index ab6f2a0d3c..526a709120 100644 --- a/Telegram/SourceFiles/codegen/style/generator.h +++ b/Telegram/SourceFiles/codegen/style/generator.h @@ -22,7 +22,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include #include -//#include "codegen/common/qstringlist_safe.h" #include "codegen/style/options.h" namespace codegen { diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.cpp b/Telegram/SourceFiles/codegen/style/parsed_file.cpp index b091445696..f5f7e2d667 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.cpp +++ b/Telegram/SourceFiles/codegen/style/parsed_file.cpp @@ -38,6 +38,8 @@ constexpr int kErrorInIncluded = 801; constexpr int kErrorTypeMismatch = 802; constexpr int kErrorUnknownField = 803; constexpr int kErrorIdentifierNotFound = 804; +constexpr int kErrorAlreadyDefined = 805; +constexpr int kErrorBadString = 806; QString tokenValue(const BasicToken &token) { if (token.type == BasicType::String) { @@ -54,13 +56,45 @@ bool isValidColor(const QString &str) { for (auto ch : str) { auto code = ch.unicode(); - if ((code < '0' || code > '9') && (code < 'a' || code > 'f') && (code < 'A' || code > 'F')) { + if ((code < '0' || code > '9') && (code < 'a' || code > 'f')) { return false; } } return true; } +uchar readHexUchar(QChar ch) { + auto code = ch.unicode(); + return (code >= '0' && code <= '9') ? ((code - '0') & 0xFF) : ((code - 'a') & 0xFF); +} + +uchar readHexUchar(QChar char1, QChar char2) { + return ((readHexUchar(char1) & 0x0F) << 4) | (readHexUchar(char2) & 0x0F); +} + +structure::data::color convertWebColor(const QString &str) { + uchar r = 0, g = 0, b = 0, a = 255; + if (isValidColor(str)) { + auto len = str.size(); + if (len == 3 || len == 4) { + r = readHexUchar(str.at(0), str.at(0)); + g = readHexUchar(str.at(1), str.at(1)); + b = readHexUchar(str.at(2), str.at(2)); + if (len == 4) a = readHexUchar(str.at(3), str.at(3)); + } else { + r = readHexUchar(str.at(0), str.at(1)); + g = readHexUchar(str.at(2), str.at(3)); + b = readHexUchar(str.at(4), str.at(5)); + if (len == 8) a = readHexUchar(str.at(6), str.at(7)); + } + } + return { r, g, b, a }; +} + +structure::data::color convertIntColor(int r, int g, int b, int a) { + return { uchar(r & 0xFF), uchar(g & 0xFF), uchar(b & 0xFF), uchar(a & 0xFF) }; +} + std::string logFullName(const structure::FullName &name) { return name.join('.').toStdString(); } @@ -87,6 +121,31 @@ std::string logType(const structure::Type &type) { return builtInTypes->value(type.tag, "invalid"); } +QString fullNameKey(const structure::FullName &name) { + return name.join('.'); +} + +bool validateAnsiString(const QString &value) { + for (auto ch : value) { + if (ch.unicode() > 127) { + return false; + } + } + return true; +} + +bool validateTransitionString(const QString &value) { + return QRegularExpression("^[a-zA-Z_]+$").match(value).hasMatch(); +} + +bool validateCursorString(const QString &value) { + return QRegularExpression("^[a-z_]+$").match(value).hasMatch(); +} + +bool validateAlignString(const QString &value) { + return QRegularExpression("^[a-z_]+$").match(value).hasMatch(); +} + } // namespace ParsedFile::ParsedFile(const Options &options) @@ -109,11 +168,21 @@ bool ParsedFile::read() { } } else if (auto braceOpen = file_.getToken(BasicType::LeftBrace)) { if (auto structResult = readStruct(tokenValue(startToken))) { + if (findStruct(structResult.name)) { + logError(kErrorAlreadyDefined) << "struct '" << logFullName(structResult.name) << "' already defined"; + break; + } + result_.structsByName.insert(fullNameKey(structResult.name), result_.structs.size()); result_.structs.push_back(structResult); continue; } } else if (auto colonToken = file_.getToken(BasicType::Colon)) { if (auto variableResult = readVariable(tokenValue(startToken))) { + if (findVariable(variableResult.name)) { + logError(kErrorAlreadyDefined) << "variable '" << logFullName(variableResult.name) << "' already defined"; + break; + } + result_.variablesByName.insert(fullNameKey(variableResult.name), result_.variables.size()); result_.variables.push_back(variableResult); continue; } @@ -139,19 +208,15 @@ common::LogStream ParsedFile::logErrorTypeMismatch() { structure::Module ParsedFile::readIncluded() { structure::Module result; - if (auto usingFile = file_.getToken(BasicType::String)) { - if (file_.getToken(BasicType::Semicolon)) { + if (auto usingFile = assertNextToken(BasicType::String)) { + if (assertNextToken(BasicType::Semicolon)) { ParsedFile included(includedOptions(tokenValue(usingFile))); if (included.read()) { result = included.data(); } else { logError(kErrorInIncluded) << "error while parsing '" << tokenValue(usingFile).toStdString() << "'"; } - } else { - logErrorUnexpectedToken() << "';'"; } - } else { - logErrorUnexpectedToken() << "file path"; } return result; } @@ -163,13 +228,11 @@ structure::Struct ParsedFile::readStruct(const QString &name) { if (auto field = readStructField(tokenValue(fieldName))) { result.fields.push_back(field); } - } else if (auto braceClose = file_.getToken(BasicType::RightBrace)) { + } else if (assertNextToken(BasicType::RightBrace)) { if (result.fields.isEmpty()) { logErrorUnexpectedToken() << "at least one field in struct"; } break; - } else { - logErrorUnexpectedToken() << "struct field name or '}'"; } } while (!failed()); return result; @@ -179,8 +242,8 @@ structure::Variable ParsedFile::readVariable(const QString &name) { structure::Variable result = { composeFullName(name) }; if (auto value = readValue()) { result.value = value; - if (!file_.getToken(BasicType::Semicolon)) { - logErrorUnexpectedToken() << "';'"; + if (value.type().tag != structure::TypeTag::Struct) { + assertNextToken(BasicType::Semicolon); } } return result; @@ -188,31 +251,30 @@ structure::Variable ParsedFile::readVariable(const QString &name) { structure::StructField ParsedFile::readStructField(const QString &name) { structure::StructField result = { composeFullName(name) }; - if (auto colonToken = file_.getToken(BasicType::Colon)) { + if (auto colonToken = assertNextToken(BasicType::Colon)) { if (auto type = readType()) { result.type = type; - if (!file_.getToken(BasicType::Semicolon)) { - logErrorUnexpectedToken() << "';'"; - } + assertNextToken(BasicType::Semicolon); } - } else { - logErrorUnexpectedToken() << "':'"; } return result; } structure::Type ParsedFile::readType() { structure::Type result; - if (auto nameToken = file_.getToken(BasicType::Name)) { + if (auto nameToken = assertNextToken(BasicType::Name)) { auto name = tokenValue(nameToken); if (auto builtInType = typeNames_.value(name.toStdString())) { result = builtInType; } else { - result.tag = structure::TypeTag::Struct; - result.name = composeFullName(name); + auto fullName = composeFullName(name); + if (findStruct(fullName)) { + result.tag = structure::TypeTag::Struct; + result.name = fullName; + } else { + logError(kErrorIdentifierNotFound) << "type name '" << logFullName(fullName) << "' not found"; + } } - } else { - logErrorUnexpectedToken() << "type name"; } return result; } @@ -254,17 +316,12 @@ structure::Value ParsedFile::readStructValue() { if (auto structName = file_.getToken(BasicType::Name)) { if (auto result = defaultConstructedStruct(composeFullName(tokenValue(structName)))) { if (file_.getToken(BasicType::LeftParenthesis)) { - if (readStructParents(result)) { - if (file_.getToken(BasicType::LeftBrace)) { - readStructValueInner(result); - } else { - logErrorUnexpectedToken() << "'{'"; - } + if (!readStructParents(result)) { + return {}; } - } else if (file_.getToken(BasicType::LeftBrace)) { + } + if (assertNextToken(BasicType::LeftBrace)) { readStructValueInner(result); - } else { - logErrorUnexpectedToken() << "'(' or '{'"; } return result; } @@ -274,71 +331,91 @@ structure::Value ParsedFile::readStructValue() { } structure::Value ParsedFile::defaultConstructedStruct(const structure::FullName &structName) { - structure::Value result; - for (const auto &structType : result_.structs) { - if (structType.name == structName) { - result.fields.reserve(structType.fields.size()); - for (const auto &fieldType : structType.fields) { - result.fields.push_back({ fieldType.name, fieldType.type }); - } + if (auto pattern = findStruct(structName)) { + QList fields; + fields.reserve(pattern->fields.size()); + for (const auto &fieldType : pattern->fields) { + fields.push_back({ fieldType.name, { fieldType.type, Qt::Uninitialized } }); } + return { structName, fields }; } - return result; + return {}; } void ParsedFile::applyStructParent(structure::Value &result, const structure::FullName &parentName) { - for (const auto &structValue : result_.variables) { - if (structValue.name == parentName) { - if (structValue.value.type == result.type) { - const auto &srcFields(structValue.value.fields); - auto &dstFields(result.fields); - logAssert(srcFields.size() == dstFields.size()) << "struct size check failed"; - - for (int i = 0, s = srcFields.size(); i != s; ++i) { - logAssert(srcFields.at(i).value.type == dstFields.at(i).value.type) << "struct field type check failed"; - dstFields[i].value = srcFields.at(i).value; - } - } else { - logErrorTypeMismatch() << "parent '" << logFullName(parentName) << "' has type '" << logType(structValue.value.type) << "' while child value has type " << logType(result.type); - } + if (auto parent = findVariable(parentName)) { + if (parent->value.type() != result.type()) { + logErrorTypeMismatch() << "parent '" << logFullName(parentName) << "' has type '" << logType(parent->value.type()) << "' while child value has type " << logType(result.type()); + return; } + + const auto *srcFields(parent->value.Complex()); + auto *dstFields(result.Complex()); + if (!srcFields || !dstFields) { + logAssert(false) << "struct data check failed"; + return; + } + + logAssert(srcFields->size() == dstFields->size()) << "struct size check failed"; + for (int i = 0, s = srcFields->size(); i != s; ++i) { + const auto &srcValue(srcFields->at(i).value); + auto &dstValue((*dstFields)[i].value); + logAssert(srcValue.type() == dstValue.type()) << "struct field type check failed"; + dstValue = srcValue; + } + } else { + logError(kErrorIdentifierNotFound) << "parent '" << logFullName(parentName) << "' not found"; } } bool ParsedFile::readStructValueInner(structure::Value &result) { do { if (auto fieldName = file_.getToken(BasicType::Name)) { - if (auto field = readVariable(tokenValue(fieldName))) { - for (auto &already : result.fields) { - if (already.name == field.name) { - if (already.value.type == field.value.type) { - already.value = field.value; - return true; - } else { - logErrorTypeMismatch() << "field '" << logFullName(already.name) << "' has type '" << logType(already.value.type) << "' while value has type " << logType(field.value.type); - return false; - } - } - } - logError(kErrorUnknownField) << "field '" << logFullName(field.name) << "' was not found in struct of type '" << logType(result.type) << "'"; + if (!assertNextToken(BasicType::Colon)) { + return false; } - } else if (file_.getToken(BasicType::RightBrace)) { + + if (auto field = readVariable(tokenValue(fieldName))) { + if (!assignStructField(result, field)) { + return false; + } + } + } else if (assertNextToken(BasicType::RightBrace)) { return true; - } else { - logErrorUnexpectedToken() << "variable field name or '}'"; } } while (!failed()); return false; } +bool ParsedFile::assignStructField(structure::Value &result, const structure::Variable &field) { + auto *fields = result.Complex(); + if (!fields) { + logAssert(false) << "struct data check failed"; + return false; + } + for (auto &already : *fields) { + if (already.name == field.name) { + if (already.value.type() == field.value.type()) { + already.value = field.value; + return true; + } else { + logErrorTypeMismatch() << "field '" << logFullName(already.name) << "' has type '" << logType(already.value.type()) << "' while value has type '" << logType(field.value.type()) << "'"; + return false; + } + } + } + logError(kErrorUnknownField) << "field '" << logFullName(field.name) << "' was not found in struct of type '" << logType(result.type()) << "'"; + return false; +} + bool ParsedFile::readStructParents(structure::Value &result) { do { - if (auto parentName = file_.getToken(BasicType::Name)) { + if (auto parentName = assertNextToken(BasicType::Name)) { applyStructParent(result, composeFullName(tokenValue(parentName))); if (file_.getToken(BasicType::RightParenthesis)) { return true; - } else if (!file_.getToken(BasicType::Comma)) { - logErrorUnexpectedToken() << "',' or ')'"; + } else { + assertNextToken(BasicType::Comma); } } else { logErrorUnexpectedToken() << "struct variable parent"; @@ -347,30 +424,17 @@ bool ParsedFile::readStructParents(structure::Value &result) { return false; } -//ParsedFile::Token ParsedFile::readInVariableChild() { -// if (auto value = readValue()) { -// if (file_.getToken(BasicType::Semicolon)) { -// state_ = State::Default; -// return value; -// } -// logErrorUnexpectedToken(";"); -// } else { -// logErrorUnexpectedToken("variable field value"); -// } -// return invalidToken(); -//} -// structure::Value ParsedFile::readPositiveValue() { auto numericToken = file_.getAnyToken(); if (numericToken.type == BasicType::Int) { - return { { structure::TypeTag::Int }, tokenValue(numericToken) }; + return { structure::TypeTag::Int, tokenValue(numericToken).toInt() }; } else if (numericToken.type == BasicType::Double) { - return { { structure::TypeTag::Double }, tokenValue(numericToken) }; + return { tokenValue(numericToken).toDouble() }; } else if (numericToken.type == BasicType::Name) { auto value = tokenValue(numericToken); auto match = QRegularExpression("^\\d+px$").match(value); if (match.hasMatch()) { - return { { structure::TypeTag::Pixels }, value.mid(0, value.size() - 2) }; + return { structure::TypeTag::Pixels, value.mid(0, value.size() - 2).toInt() }; } } file_.putBack(); @@ -382,7 +446,7 @@ structure::Value ParsedFile::readNumericValue() { return value; } else if (auto minusToken = file_.getToken(BasicType::Minus)) { if (auto positiveValue = readNumericValue()) { - return { positiveValue.type, '-' + positiveValue.data }; + return { positiveValue.type().tag, -positiveValue.Int() }; } logErrorUnexpectedToken() << "numeric value"; } @@ -391,7 +455,11 @@ structure::Value ParsedFile::readNumericValue() { structure::Value ParsedFile::readStringValue() { if (auto stringToken = file_.getToken(BasicType::String)) { - return { { structure::TypeTag::String }, stringToken.value }; + auto value = tokenValue(stringToken); + if (validateAnsiString(value)) { + return { structure::TypeTag::String, stringToken.value.toStdString() }; + } + logError(kErrorBadString) << "unicode symbols are not supported"; } return {}; } @@ -400,47 +468,92 @@ structure::Value ParsedFile::readColorValue() { if (auto numberSign = file_.getToken(BasicType::Number)) { auto color = file_.getAnyToken(); if (color.type == BasicType::Int || color.type == BasicType::Name) { - auto chars = tokenValue(color); + auto chars = tokenValue(color).toLower(); if (isValidColor(chars)) { - return { { structure::TypeTag::Color }, chars.toLower() }; + return { convertWebColor(chars) }; } } else { logErrorUnexpectedToken() << "color value in #ccc, #ccca, #cccccc or #ccccccaa format"; } + } else if (auto rgbaToken = file_.getToken(BasicType::Name)) { + if (tokenValue(rgbaToken) == "rgba") { + assertNextToken(BasicType::LeftParenthesis); + + auto r = readNumericValue(); assertNextToken(BasicType::Comma); + auto g = readNumericValue(); assertNextToken(BasicType::Comma); + auto b = readNumericValue(); assertNextToken(BasicType::Comma); + auto a = readNumericValue(); + if (r.type().tag != structure::TypeTag::Int || r.Int() < 0 || r.Int() > 255 || + g.type().tag != structure::TypeTag::Int || g.Int() < 0 || g.Int() > 255 || + b.type().tag != structure::TypeTag::Int || b.Int() < 0 || b.Int() > 255 || + a.type().tag != structure::TypeTag::Int || a.Int() < 0 || a.Int() > 255) { + logErrorTypeMismatch() << "expected four 0-255 values for the rgba color"; + } + + assertNextToken(BasicType::RightParenthesis); + + return { convertIntColor(r.Int(), g.Int(), b.Int(), a.Int()) }; + } else if (tokenValue(rgbaToken) == "rgb") { + assertNextToken(BasicType::LeftParenthesis); + + auto r = readNumericValue(); assertNextToken(BasicType::Comma); + auto g = readNumericValue(); assertNextToken(BasicType::Comma); + auto b = readNumericValue(); + if (r.type().tag != structure::TypeTag::Int || r.Int() < 0 || r.Int() > 255 || + g.type().tag != structure::TypeTag::Int || g.Int() < 0 || g.Int() > 255 || + b.type().tag != structure::TypeTag::Int || b.Int() < 0 || b.Int() > 255) { + logErrorTypeMismatch() << "expected three int values for the rgb color"; + } + + assertNextToken(BasicType::RightParenthesis); + + return { convertIntColor(r.Int(), g.Int(), b.Int(), 255) }; + } + file_.putBack(); } return {}; } structure::Value ParsedFile::readPointValue() { + if (auto font = file_.getToken(BasicType::Name)) { + if (tokenValue(font) == "point") { + assertNextToken(BasicType::LeftParenthesis); + + auto x = readNumericValue(); assertNextToken(BasicType::Comma); + auto y = readNumericValue(); + if (x.type().tag != structure::TypeTag::Pixels || + y.type().tag != structure::TypeTag::Pixels) { + logErrorTypeMismatch() << "expected two px values for the point"; + } + + assertNextToken(BasicType::RightParenthesis); + + return { structure::data::point { x.Int(), y.Int() } }; + } + file_.putBack(); + } return {}; } structure::Value ParsedFile::readSpriteValue() { if (auto font = file_.getToken(BasicType::Name)) { if (tokenValue(font) == "sprite") { - if (!file_.getToken(BasicType::LeftParenthesis)) { - logErrorUnexpectedToken() << "'(' and sprite definition"; - return {}; - } + assertNextToken(BasicType::LeftParenthesis); - auto x = readNumericValue(); file_.getToken(BasicType::Comma); - auto y = readNumericValue(); file_.getToken(BasicType::Comma); - auto w = readNumericValue(); file_.getToken(BasicType::Comma); + auto x = readNumericValue(); assertNextToken(BasicType::Comma); + auto y = readNumericValue(); assertNextToken(BasicType::Comma); + auto w = readNumericValue(); assertNextToken(BasicType::Comma); auto h = readNumericValue(); - if (x.type.tag != structure::TypeTag::Pixels || - y.type.tag != structure::TypeTag::Pixels || - w.type.tag != structure::TypeTag::Pixels || - h.type.tag != structure::TypeTag::Pixels) { - logErrorTypeMismatch() << "px rect for the sprite expected"; - return {}; + if (x.type().tag != structure::TypeTag::Pixels || + y.type().tag != structure::TypeTag::Pixels || + w.type().tag != structure::TypeTag::Pixels || + h.type().tag != structure::TypeTag::Pixels) { + logErrorTypeMismatch() << "expected four px values for the sprite"; } - if (!file_.getToken(BasicType::RightParenthesis)) { - logErrorUnexpectedToken() << "')'"; - return {}; - } + assertNextToken(BasicType::RightParenthesis); - return { { structure::TypeTag::Sprite }, x.data + ',' + y.data + ',' + w.data + ',' + h.data }; + return { structure::data::sprite { x.Int(), y.Int(), w.Int(), h.Int() } }; } file_.putBack(); } @@ -448,43 +561,138 @@ structure::Value ParsedFile::readSpriteValue() { } structure::Value ParsedFile::readSizeValue() { + if (auto font = file_.getToken(BasicType::Name)) { + if (tokenValue(font) == "size") { + assertNextToken(BasicType::LeftParenthesis); + + auto w = readNumericValue(); assertNextToken(BasicType::Comma); + auto h = readNumericValue(); + if (w.type().tag != structure::TypeTag::Pixels || + h.type().tag != structure::TypeTag::Pixels) { + logErrorTypeMismatch() << "expected two px values for the size"; + } + + assertNextToken(BasicType::RightParenthesis); + + return { structure::data::size { w.Int(), h.Int() } }; + } + file_.putBack(); + } return {}; } structure::Value ParsedFile::readTransitionValue() { + if (auto font = file_.getToken(BasicType::Name)) { + if (tokenValue(font) == "transition") { + assertNextToken(BasicType::LeftParenthesis); + + auto transition = tokenValue(assertNextToken(BasicType::Name)); + + assertNextToken(BasicType::RightParenthesis); + + if (validateTransitionString(transition)) { + return { structure::TypeTag::Transition, transition.toStdString() }; + } else { + logError(kErrorBadString) << "bad transition value"; + } + } + file_.putBack(); + } return {}; } structure::Value ParsedFile::readCursorValue() { + if (auto font = file_.getToken(BasicType::Name)) { + if (tokenValue(font) == "cursor") { + assertNextToken(BasicType::LeftParenthesis); + + auto cursor = tokenValue(assertNextToken(BasicType::Name)); + + assertNextToken(BasicType::RightParenthesis); + + if (validateCursorString(cursor)) { + return { structure::TypeTag::Cursor, cursor.toStdString() }; + } else { + logError(kErrorBadString) << "bad cursor string"; + } + } + file_.putBack(); + } return {}; } structure::Value ParsedFile::readAlignValue() { + if (auto font = file_.getToken(BasicType::Name)) { + if (tokenValue(font) == "align") { + assertNextToken(BasicType::LeftParenthesis); + + auto align = tokenValue(assertNextToken(BasicType::Name)); + + assertNextToken(BasicType::RightParenthesis); + + if (validateAlignString(align)) { + return { structure::TypeTag::Align, align.toStdString() }; + } else { + logError(kErrorBadString) << "bad align string"; + } + } + file_.putBack(); + } return {}; } structure::Value ParsedFile::readMarginsValue() { + if (auto font = file_.getToken(BasicType::Name)) { + if (tokenValue(font) == "margins") { + assertNextToken(BasicType::LeftParenthesis); + + auto l = readNumericValue(); assertNextToken(BasicType::Comma); + auto t = readNumericValue(); assertNextToken(BasicType::Comma); + auto r = readNumericValue(); assertNextToken(BasicType::Comma); + auto b = readNumericValue(); + if (l.type().tag != structure::TypeTag::Pixels || + t.type().tag != structure::TypeTag::Pixels || + r.type().tag != structure::TypeTag::Pixels || + b.type().tag != structure::TypeTag::Pixels) { + logErrorTypeMismatch() << "expected four px values for the margins"; + } + + assertNextToken(BasicType::RightParenthesis); + + return { structure::data::margins { l.Int(), t.Int(), r.Int(), b.Int() } }; + } + file_.putBack(); + } return {}; } structure::Value ParsedFile::readFontValue() { if (auto font = file_.getToken(BasicType::Name)) { if (tokenValue(font) == "font") { - if (!file_.getToken(BasicType::LeftParenthesis)) { - logErrorUnexpectedToken() << "'(' and font definition"; - return {}; - } + assertNextToken(BasicType::LeftParenthesis); + int flags = 0; structure::Value family, size; do { + if (auto formatToken = file_.getToken(BasicType::Name)) { + if (tokenValue(formatToken) == "bold") { + flags |= structure::data::font::Bold; + } else if (tokenValue(formatToken) == "italic") { + flags |= structure::data::font::Italic; + } else if (tokenValue(formatToken) == "underline") { + flags |= structure::data::font::Underline; + } else { + file_.putBack(); + } + } if (auto familyValue = readStringValue()) { family = familyValue; } else if (auto sizeValue = readNumericValue()) { size = sizeValue; } else if (auto copyValue = readCopyValue()) { - if (copyValue.type.tag == structure::TypeTag::String) { + if (copyValue.type().tag == structure::TypeTag::String) { family = copyValue; - } else if (copyValue.type.tag == structure::TypeTag::Pixels) { + } else if (copyValue.type().tag == structure::TypeTag::Pixels) { size = copyValue; } else { logErrorUnexpectedToken() << "font family, font size or ')'"; @@ -496,10 +704,10 @@ structure::Value ParsedFile::readFontValue() { } } while (!failed()); - if (size.type.tag != structure::TypeTag::Pixels) { + if (size.type().tag != structure::TypeTag::Pixels) { logErrorTypeMismatch() << "px value for the font size expected"; } - return { { structure::TypeTag::Font }, size.data + ',' + family.data }; + return { structure::data::font { family.String(), size.Int(), flags } }; } file_.putBack(); } @@ -509,18 +717,64 @@ structure::Value ParsedFile::readFontValue() { structure::Value ParsedFile::readCopyValue() { if (auto copyName = file_.getToken(BasicType::Name)) { structure::FullName name = { tokenValue(copyName) }; - for (const auto &variable : result_.variables) { - if (variable.name == name) { - auto result = variable.value; - result.copy = variable.name; - return result; - } + if (auto variable = findVariable(name)) { + return variable->value.makeCopy(variable->name); } logError(kErrorIdentifierNotFound) << "identifier '" << logFullName(name) << "' not found"; } return {}; } +// Returns nullptr if there is no such struct in result_ or any of included modules. +const structure::Struct *ParsedFile::findStruct(const structure::FullName &name) { + if (auto result = findStructInModule(name, result_)) { + return result; + } + for (const auto &included : result_.includes) { + if (auto result = findStructInModule(name, included)) { + return result; + } + } + return nullptr; +} + +const structure::Struct *ParsedFile::findStructInModule(const structure::FullName &name, const structure::Module &module) { + auto index = module.structsByName.value(fullNameKey(name), -1); + if (index < 0) { + return nullptr; + } + return &module.structs.at(index); +} + +// Returns nullptr if there is no such variable in result_ or any of included modules. +const structure::Variable *ParsedFile::findVariable(const structure::FullName &name) { + if (auto result = findVariableInModule(name, result_)) { + return result; + } + for (const auto &included : result_.includes) { + if (auto result = findVariableInModule(name, included)) { + return result; + } + } + return nullptr; +} + +const structure::Variable *ParsedFile::findVariableInModule(const structure::FullName &name, const structure::Module &module) { + auto index = module.variablesByName.value(fullNameKey(name), -1); + if (index < 0) { + return nullptr; + } + return &module.variables.at(index); +} + +BasicToken ParsedFile::assertNextToken(BasicToken::Type type) { + auto result = file_.getToken(type); + if (!result) { + logErrorUnexpectedToken() << type; + } + return result; +} + Options ParsedFile::includedOptions(const QString &filepath) { auto result = options_; result.inputPath = filepath; diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.h b/Telegram/SourceFiles/codegen/style/parsed_file.h index a41efe946c..6106d728a5 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.h +++ b/Telegram/SourceFiles/codegen/style/parsed_file.h @@ -78,6 +78,7 @@ private: structure::Value defaultConstructedStruct(const structure::FullName &name); void applyStructParent(structure::Value &result, const structure::FullName &parentName); bool readStructValueInner(structure::Value &result); + bool assignStructField(structure::Value &result, const structure::Variable &field); bool readStructParents(structure::Value &result); // Simple methods for reading value types. @@ -95,6 +96,18 @@ private: structure::Value readFontValue(); structure::Value readCopyValue(); + // Returns nullptr if there is no such struct in result_ or any of included modules. + const structure::Struct *findStruct(const structure::FullName &name); + const structure::Struct *findStructInModule(const structure::FullName &name, const structure::Module &module); + + // Returns nullptr if there is no such variable in result_ or any of included modules. + const structure::Variable *findVariable(const structure::FullName &name); + const structure::Variable *findVariableInModule(const structure::FullName &name, const structure::Module &module); + + // Read next token and fire unexpected token error if it is not of "type". + using BasicToken = common::BasicTokenizedFile::Token; + BasicToken assertNextToken(BasicToken::Type type); + // Look through include directories in options_ and find absolute include path. Options includedOptions(const QString &filepath); diff --git a/Telegram/SourceFiles/codegen/style/structure.h b/Telegram/SourceFiles/codegen/style/structure.h index c4f7b68c27..8f29350b48 100644 --- a/Telegram/SourceFiles/codegen/style/structure.h +++ b/Telegram/SourceFiles/codegen/style/structure.h @@ -21,60 +21,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include -#include +#include +#include +#include "codegen/style/structure_types.h" namespace codegen { namespace style { namespace structure { -// List of names, like overview.document.bg -using FullName = QStringList; - -enum class TypeTag { - Invalid, - Int, - Double, - Pixels, - String, - Color, - Point, - Sprite, - Size, - Transition, - Cursor, - Align, - Margins, - Font, - Struct, -}; - -struct Type { - TypeTag tag; - FullName name; // only for type == ClassType::Struct - - explicit operator bool() const { - return (tag != TypeTag::Invalid); - } -}; -inline bool operator==(const Type &a, const Type &b) { - return (a.tag == b.tag) && (a.name == b.name); -} -inline bool operator!=(const Type &a, const Type &b) { - return !(a == b); -} - -struct Variable; -struct Value { - Type type; - QString data; // for plain types - QList fields; // for struct types - FullName copy; // for copies of existing named values - - explicit operator bool() const { - return !data.isEmpty() || !fields.isEmpty() || !copy.isEmpty(); - } -}; - struct Variable { FullName name; Value value; @@ -107,6 +61,8 @@ struct Module { QList includes; QList structs; QList variables; + QMap structsByName; + QMap variablesByName; explicit operator bool() const { return !fullpath.isEmpty(); diff --git a/Telegram/SourceFiles/codegen/style/structure_types.cpp b/Telegram/SourceFiles/codegen/style/structure_types.cpp new file mode 100644 index 0000000000..15a973efdc --- /dev/null +++ b/Telegram/SourceFiles/codegen/style/structure_types.cpp @@ -0,0 +1,204 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "codegen/style/structure_types.h" + +#include "codegen/style/structure.h" + +namespace codegen { +namespace style { +namespace structure { + +struct Value::DataTypes { + class TInt : public DataBase { + public: + TInt(int value) : value_(value) { + } + int Int() const override { return value_; } + + private: + int value_; + + }; + class TDouble : public DataBase { + public: + TDouble(double value) : value_(value) { + } + double Double() const override { return value_; } + + private: + double value_; + + }; + class TString : public DataBase { + public: + TString(std::string value) : value_(value) { + } + std::string String() const override { return value_; } + + private: + std::string value_; + + }; + class TPoint : public DataBase { + public: + TPoint(data::point value) : value_(value) { + } + data::point Point() const override { return value_; } + + private: + data::point value_; + + }; + class TSprite : public DataBase { + public: + TSprite(data::sprite value) : value_(value) { + } + data::sprite Sprite() const override { return value_; } + + private: + data::sprite value_; + + }; + class TSize : public DataBase { + public: + TSize(data::size value) : value_(value) { + } + data::size Size() const override { return value_; } + + private: + data::size value_; + + }; + class TColor : public DataBase { + public: + TColor(data::color value) : value_(value) { + } + data::color Color() const override { return value_; } + + private: + data::color value_; + + }; + class TMargins : public DataBase { + public: + TMargins(data::margins value) : value_(value) { + } + data::margins Margins() const override { return value_; } + + private: + data::margins value_; + + }; + class TFont : public DataBase { + public: + TFont(data::font value) : value_(value) { + } + data::font Font() const override { return value_; } + + private: + data::font value_; + + }; + class TComplex : public DataBase { + public: + TComplex(data::complex value) : value_(value) { + } + const data::complex *Complex() const override { return &value_; } + data::complex *Complex() override { return &value_; } + + private: + data::complex value_; + + }; +}; + +Value::Value() : Value(TypeTag::Invalid, std::make_shared()) { +} + +Value::Value(double value) : Value(TypeTag::Double, std::make_shared(value)) { +} + +Value::Value(data::point value) : Value(TypeTag::Point, std::make_shared(value)) { +} + +Value::Value(data::sprite value) : Value(TypeTag::Sprite, std::make_shared(value)) { +} + +Value::Value(data::size value) : Value(TypeTag::Size, std::make_shared(value)) { +} + +Value::Value(data::color value) : Value(TypeTag::Color, std::make_shared(value)) { +} + +Value::Value(data::margins value) : Value(TypeTag::Margins, std::make_shared(value)) { +} + +Value::Value(data::font value) : Value(TypeTag::Font, std::make_shared(value)) { +} + +Value::Value(const FullName &type, data::complex value) +: type_ { TypeTag::Struct, type } +, data_(std::make_shared(value)) { +} + +Value::Value(TypeTag type, int value) : Value(type, std::make_shared(value)) { + if (type_.tag != TypeTag::Int && type_.tag != TypeTag::Pixels) { + type_.tag = TypeTag::Invalid; + data_ = std::make_shared(); + } +} + +Value::Value(TypeTag type, std::string value) : Value(type, std::make_shared(value)) { + if (type_.tag != TypeTag::String && + type_.tag != TypeTag::Transition && + type_.tag != TypeTag::Cursor && + type_.tag != TypeTag::Align) { + type_.tag = TypeTag::Invalid; + data_ = std::make_shared(); + } +} + +Value::Value(Type type, Qt::Initialization) : type_(type) { + switch (type_.tag) { + case TypeTag::Invalid: data_ = std::make_shared(); break; + case TypeTag::Int: data_ = std::make_shared(0); break; + case TypeTag::Double: data_ = std::make_shared(0.); break; + case TypeTag::Pixels: data_ = std::make_shared(0); break; + case TypeTag::String: data_ = std::make_shared(""); break; + case TypeTag::Color: data_ = std::make_shared(data::color { 0, 0, 0, 255 }); break; + case TypeTag::Point: data_ = std::make_shared(data::point { 0, 0 }); break; + case TypeTag::Sprite: data_ = std::make_shared(data::sprite { 0, 0, 0, 0 }); break; + case TypeTag::Size: data_ = std::make_shared(data::size { 0, 0 }); break; + case TypeTag::Transition: data_ = std::make_shared("linear"); break; + case TypeTag::Cursor: data_ = std::make_shared("default"); break; + case TypeTag::Align: data_ = std::make_shared("left"); break; + case TypeTag::Margins: data_ = std::make_shared(data::margins { 0, 0, 0, 0 }); break; + case TypeTag::Font: data_ = std::make_shared(data::font { "", 13, 0 }); break; + case TypeTag::Struct: data_ = std::make_shared(data::complex {}); break; + } +} + +Value::Value(TypeTag type, std::shared_ptr &&data) : type_ { type }, data_(std::move(data)) { +} + +} // namespace structure +} // namespace style +} // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/structure_types.h b/Telegram/SourceFiles/codegen/style/structure_types.h new file mode 100644 index 0000000000..2e2703b3c9 --- /dev/null +++ b/Telegram/SourceFiles/codegen/style/structure_types.h @@ -0,0 +1,174 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include +#include +#include + +namespace codegen { +namespace style { +namespace structure { + +// List of names, like overview.document.bg +using FullName = QStringList; + +struct Variable; + +enum class TypeTag { + Invalid, + Int, + Double, + Pixels, + String, + Color, + Point, + Sprite, + Size, + Transition, + Cursor, + Align, + Margins, + Font, + Struct, +}; + +struct Type { + TypeTag tag; + FullName name; // only for type == ClassType::Struct + + explicit operator bool() const { + return (tag != TypeTag::Invalid); + } +}; +inline bool operator==(const Type &a, const Type &b) { + return (a.tag == b.tag) && (a.name == b.name); +} +inline bool operator!=(const Type &a, const Type &b) { + return !(a == b); +} + +namespace data { + +struct point { + int x, y; +}; +struct sprite { + int left, top, width, height; +}; +struct size { + int width, height; +}; +struct color { + uchar red, green, blue, alpha; +}; +struct margins { + int left, top, right, bottom; +}; +struct font { + enum Flag { + Bold = 0x01, + Italic = 0x02, + Underline = 0x04, + }; + std::string family; + int size; + int flags; +}; +using complex = QList; + +} // namespace data + +class Value { +public: + Value(); + Value(double value); + Value(data::point value); + Value(data::sprite value); + Value(data::size value); + Value(data::color value); + Value(data::margins value); + Value(data::font value); + Value(const FullName &type, data::complex value); + + // Can be int / pixels. + Value(TypeTag type, int value); + + // Can be string / transition / cursor / align. + Value(TypeTag type, std::string value); + + // Default constructed value (uninitialized). + Value(Type type, Qt::Initialization); + + Type type() const { return type_; } + int Int() const { return data_->Int(); } + double Double() const { return data_->Double(); } + std::string String() const { return data_->String(); } + data::point Point() const { return data_->Point(); } + data::sprite Sprite() const { return data_->Sprite(); }; + data::size Size() const { return data_->Size(); }; + data::color Color() const { return data_->Color(); }; + data::margins Margins() const { return data_->Margins(); }; + data::font Font() const { return data_->Font(); }; + const data::complex *Complex() const { return data_->Complex(); }; + data::complex *Complex() { return data_->Complex(); }; + + explicit operator bool() const { + return type_.tag != TypeTag::Invalid; + } + + Value makeCopy(const FullName ©Of) const { + Value result(*this); + result.copyOf_ = copyOf; + return result; + } + +private: + class DataBase { + public: + virtual int Int() const { return 0; } + virtual double Double() const { return 0.; } + virtual std::string String() const { return std::string(); } + virtual data::point Point() const { return {}; }; + virtual data::sprite Sprite() const { return {}; }; + virtual data::size Size() const { return {}; }; + virtual data::color Color() const { return {}; }; + virtual data::margins Margins() const { return {}; }; + virtual data::font Font() const { return {}; }; + virtual const data::complex *Complex() const { return nullptr; }; + virtual data::complex *Complex() { return nullptr; }; + virtual ~DataBase() { + } + }; + struct DataTypes; + + Value(TypeTag type, std::shared_ptr &&data); + + Type type_; + std::shared_ptr data_; + + FullName copyOf_; // for copies of existing named values + +}; + +} // namespace structure +} // namespace style +} // namespace codegen diff --git a/Telegram/SourceFiles/pspecific_mac_p.mm b/Telegram/SourceFiles/pspecific_mac_p.mm index 392acfd00e..574e017794 100644 --- a/Telegram/SourceFiles/pspecific_mac_p.mm +++ b/Telegram/SourceFiles/pspecific_mac_p.mm @@ -794,7 +794,7 @@ void objc_openFile(const QString &f, bool openwith) { NSOpenPanel *openPanel = [NSOpenPanel openPanel]; - NSRect fullRect = { { 0., 0. }, { st::macAccessory.width() * 1., st::macAccessory.height() * 1. } }; + NSRect fullRect = { { 0., 0. }, { st::macAccessoryWidth, st::macAccessoryHeight } }; NSView *accessory = [[NSView alloc] initWithFrame:fullRect]; [accessory setAutoresizesSubviews:YES]; @@ -861,7 +861,7 @@ void objc_openFile(const QString &f, bool openwith) { NSImageView *badIcon = [[NSImageView alloc] init]; NSImage *badImage = [NSImage imageNamed:NSImageNameCaution]; [badIcon setImage:badImage]; - [badIcon setFrame:NSMakeRect(0, 0, st::macCautionIconSize.width(), st::macCautionIconSize.height())]; + [badIcon setFrame:NSMakeRect(0, 0, st::macCautionIconSize, st::macCautionIconSize)]; NSRect badFrame = [badLabel frame], badIconFrame = [badIcon frame]; badFrame.origin.x = (fullRect.size.width - badFrame.size.width + badIconFrame.size.width) / 2.; diff --git a/Telegram/build/vc/codegen_style/codegen_style.vcxproj b/Telegram/build/vc/codegen_style/codegen_style.vcxproj index 848016fb33..29b8674b41 100644 --- a/Telegram/build/vc/codegen_style/codegen_style.vcxproj +++ b/Telegram/build/vc/codegen_style/codegen_style.vcxproj @@ -19,6 +19,7 @@ + @@ -31,6 +32,7 @@ + {E4DF8176-4DEF-4859-962F-B497E3E7A323} diff --git a/Telegram/build/vc/codegen_style/codegen_style.vcxproj.filters b/Telegram/build/vc/codegen_style/codegen_style.vcxproj.filters index 9c64d87374..20abd6169f 100644 --- a/Telegram/build/vc/codegen_style/codegen_style.vcxproj.filters +++ b/Telegram/build/vc/codegen_style/codegen_style.vcxproj.filters @@ -37,6 +37,9 @@ src\style + + src\style + @@ -69,5 +72,8 @@ src\style + + src\style + \ No newline at end of file