[core] Add image sections to format expression

This commit is contained in:
Alexander Shalamov
2019-11-12 22:21:12 +02:00
parent 5e3ef2e9e0
commit a01ecc92a0
8 changed files with 153 additions and 94 deletions

View File

@@ -42,9 +42,8 @@ bool hasTokens(const std::string& source) {
std::unique_ptr<Expression> convertTokenStringToFormatExpression(const std::string& source) {
auto textExpression = convertTokenStringToExpression(source);
std::vector<FormatExpressionSection> sections;
sections.emplace_back(std::move(textExpression), nullopt, nullopt, nullopt);
return std::make_unique<FormatExpression>(sections);
std::vector<FormatExpressionSection> sections{FormatExpressionSection(std::move(textExpression))};
return std::make_unique<FormatExpression>(std::move(sections));
}
std::unique_ptr<Expression> convertTokenStringToImageExpression(const std::string& source) {

View File

@@ -237,9 +237,8 @@ std::unique_ptr<Expression> format(const char* value) {
}
std::unique_ptr<Expression> format(std::unique_ptr<Expression> input) {
std::vector<FormatExpressionSection> sections;
sections.emplace_back(std::move(input), nullopt, nullopt, nullopt);
return std::make_unique<FormatExpression>(sections);
std::vector<FormatExpressionSection> sections{FormatExpressionSection(std::move(input))};
return std::make_unique<FormatExpression>(std::move(sections));
}
std::unique_ptr<Expression> image(const char* value) {

View File

@@ -6,21 +6,23 @@ namespace mbgl {
namespace style {
namespace expression {
FormatExpressionSection::FormatExpressionSection(std::unique_ptr<Expression> text_,
optional<std::unique_ptr<Expression>> fontScale_,
optional<std::unique_ptr<Expression>> textFont_,
optional<std::unique_ptr<Expression>> textColor_)
: text(std::move(text_))
{
FormatExpressionSection::FormatExpressionSection(std::unique_ptr<Expression> content_) : content(std::move(content_)) {}
void FormatExpressionSection::setTextSectionOptions(optional<std::unique_ptr<Expression>> fontScale_,
optional<std::unique_ptr<Expression>> textFont_,
optional<std::unique_ptr<Expression>> textColor_) {
if (fontScale_) {
assert(*fontScale_);
fontScale = std::shared_ptr<Expression>(std::move(*fontScale_));
}
if (textFont_) {
assert(*textFont_);
textFont = std::shared_ptr<Expression>(std::move(*textFont_));
}
if (textColor_) {
assert(*textColor_);
textColor = std::shared_ptr<Expression>(std::move(*textColor_));
}
}
@@ -34,68 +36,69 @@ using namespace mbgl::style::conversion;
ParseResult FormatExpression::parse(const Convertible& value, ParsingContext& ctx) {
std::size_t argsLength = arrayLength(value);
if (argsLength < 3) {
ctx.error("Expected at least two arguments.");
if (argsLength < 2) {
ctx.error("Expected at least one argument.");
return ParseResult();
}
if ((argsLength - 1) % 2 != 0) {
ctx.error("Expected an even number of arguments.");
if (isObject(arrayMember(value, 1))) {
ctx.error("First argument must be an image or text section.");
return ParseResult();
}
std::vector<FormatExpressionSection> sections;
for (std::size_t i = 1; i < argsLength - 1; i += 2) {
auto textArg = arrayMember(value, i);
ParseResult text = ctx.parse(textArg, 1, {type::Value});
if (!text) {
return ParseResult();
}
auto options = arrayMember(value, i + 1);
if (!isObject(options)) {
ctx.error("Format options argument must be an object.");
return ParseResult();
}
const optional<Convertible> fontScaleOption = objectMember(options, kFormattedSectionFontScale);
ParseResult fontScale;
if (fontScaleOption) {
fontScale = ctx.parse(*fontScaleOption, 1, {type::Number});
if (!fontScale) {
return ParseResult();
}
}
const optional<Convertible> textFontOption = objectMember(options, kFormattedSectionTextFont);
ParseResult textFont;
if (textFontOption) {
textFont = ctx.parse(*textFontOption, 1, {type::Array(type::String)});
if (!textFont) {
return ParseResult();
}
}
bool nextTokenMayBeObject = false;
for (std::size_t i = 1; i <= argsLength - 1; ++i) {
auto arg = arrayMember(value, i);
const optional<Convertible> textColorOption = objectMember(options, kFormattedSectionTextColor);
ParseResult textColor;
if (textColorOption) {
textColor = ctx.parse(*textColorOption, 1, {type::Color});
if (!textColor) {
if (nextTokenMayBeObject && isObject(arg)) {
nextTokenMayBeObject = false;
const optional<Convertible> fontScaleOption = objectMember(arg, kFormattedSectionFontScale);
ParseResult fontScale;
if (fontScaleOption) {
fontScale = ctx.parse(*fontScaleOption, 1, {type::Number});
if (!fontScale) {
return ParseResult();
}
}
const optional<Convertible> textFontOption = objectMember(arg, kFormattedSectionTextFont);
ParseResult textFont;
if (textFontOption) {
textFont = ctx.parse(*textFontOption, 1, {type::Array(type::String)});
if (!textFont) {
return ParseResult();
}
}
const optional<Convertible> textColorOption = objectMember(arg, kFormattedSectionTextColor);
ParseResult textColor;
if (textColorOption) {
textColor = ctx.parse(*textColorOption, 1, {type::Color});
if (!textColor) {
return ParseResult();
}
}
sections.back().setTextSectionOptions(std::move(fontScale), std::move(textFont), std::move(textColor));
} else {
ParseResult parsedArg = ctx.parse(arg, 1, {type::Value});
if (!parsedArg) {
ctx.error("Cannot parse formatted section.");
return ParseResult();
}
}
sections.emplace_back(std::move(*text),
std::move(fontScale),
std::move(textFont),
std::move(textColor));
nextTokenMayBeObject = true;
sections.emplace_back(std::move(*parsedArg));
}
}
return ParseResult(std::make_unique<FormatExpression>(std::move(sections)));
}
void FormatExpression::eachChild(const std::function<void(const Expression&)>& fn) const {
for (auto& section : sections) {
fn(*section.text);
fn(*section.content);
if (section.fontScale) {
fn(**section.fontScale);
}
@@ -117,7 +120,7 @@ bool FormatExpression::operator==(const Expression& e) const {
for (std::size_t i = 0; i < sections.size(); i++) {
const auto& lhsSection = sections.at(i);
const auto& rhsSection = rhs->sections.at(i);
if (*lhsSection.text != *rhsSection.text) {
if (*lhsSection.content != *rhsSection.content) {
return false;
}
if ((lhsSection.fontScale && (!rhsSection.fontScale || **lhsSection.fontScale != **rhsSection.fontScale)) ||
@@ -141,7 +144,7 @@ bool FormatExpression::operator==(const Expression& e) const {
mbgl::Value FormatExpression::serialize() const {
std::vector<mbgl::Value> serialized{{ getOperator() }};
for (const auto& section : sections) {
serialized.push_back(section.text->serialize());
serialized.push_back(section.content->serialize());
std::unordered_map<std::string, mbgl::Value> options;
if (section.fontScale) {
options.emplace(kFormattedSectionFontScale, (*section.fontScale)->serialize());
@@ -160,14 +163,25 @@ mbgl::Value FormatExpression::serialize() const {
EvaluationResult FormatExpression::evaluate(const EvaluationContext& params) const {
std::vector<FormattedSection> evaluatedSections;
for (const auto& section : sections) {
auto textResult = section.text->evaluate(params);
if (!textResult) {
return textResult.error();
auto contentResult = section.content->evaluate(params);
if (!contentResult) {
return contentResult.error();
}
optional<std::string> evaluatedText = toString(*textResult);
if (!evaluatedText) {
return EvaluationError({ "Could not coerce format expression text input to string." });
optional<std::string> evaluatedText;
if (typeOf(*contentResult) == type::Image) {
const auto& image = contentResult->get<Image>();
// Omit sections with empty image ids.
if (!image.id().empty()) {
evaluatedSections.emplace_back(image);
}
// Continue evaluation of a next section, as the image section does not have section options.
continue;
} else {
evaluatedText = toString(*contentResult);
if (!evaluatedText) {
return EvaluationError({"Could not coerce format expression text input to string."});
}
}
optional<double> evaluatedFontScale;

View File

@@ -35,6 +35,16 @@ std::string Formatted::toString() const {
return result;
}
bool Formatted::empty() const {
if (sections.empty()) {
return true;
}
return !std::any_of(sections.begin(), sections.end(), [](const FormattedSection& section) {
return !section.text.empty() || (section.image && !section.image->empty());
});
}
mbgl::Value Formatted::toObject() const {
mapbox::base::ValueObject result;
mapbox::base::ValueArray sectionValues;
@@ -58,6 +68,7 @@ mbgl::Value Formatted::toObject() const {
} else {
serializedSection.emplace("textColor", NullValue());
}
serializedSection.emplace("image", section.image ? section.image->toValue() : NullValue());
sectionValues.emplace_back(serializedSection);
}
result.emplace("sections", std::move(sectionValues));
@@ -76,14 +87,37 @@ optional<Formatted> Converter<Formatted>::operator()(const Convertible& value, E
if (isArray(value)) {
std::vector<FormattedSection> sections;
for (std::size_t i = 0; i < arrayLength(value); ++i) {
Convertible section = arrayMember(value, i);
const Convertible& section = arrayMember(value, i);
std::size_t sectionLength = arrayLength(section);
if (sectionLength < 1) {
error.message = "Section has to contain a text and optional parameters.";
error.message = "Section has to contain a text and optional parameters or an image.";
return nullopt;
}
optional<std::string> sectionText = toString(arrayMember(section, 0));
const Convertible& firstElement = arrayMember(section, 0);
if (isArray(firstElement)) {
if (arrayLength(firstElement) < 2) {
error.message = "Image section has to contain image name.";
return nullopt;
}
optional<std::string> imageOp = toString(arrayMember(firstElement, 0));
if (!imageOp || *imageOp != "image") {
error.message = "Serialized image section has to contain 'image' operator.";
return nullopt;
}
optional<std::string> imageArg = toString(arrayMember(firstElement, 1));
if (!imageArg) {
error.message = "Serialized image section agument has to be of a String type.";
return nullopt;
}
sections.emplace_back(Image(*imageArg));
continue;
}
optional<std::string> sectionText = toString(firstElement);
if (!sectionText) {
error.message = "Section has to contain a text.";
return nullopt;
@@ -93,7 +127,7 @@ optional<Formatted> Converter<Formatted>::operator()(const Convertible& value, E
optional<FontStack> textFont;
optional<Color> textColor;
if (sectionLength > 1) {
Convertible sectionParams = arrayMember(section, 1);
const Convertible& sectionParams = arrayMember(section, 1);
if (!isObject(sectionParams)) {
error.message = "Parameters have to be enclosed in an object.";
return nullopt;

View File

@@ -149,13 +149,18 @@ mbgl::Value ValueConverter<mbgl::Value>::fromExpressionValue(const Value& value)
static std::string formatOperator("format");
serialized.emplace_back(formatOperator);
for (const auto& section : formatted.sections) {
if (section.image) {
serialized.emplace_back(std::vector<mbgl::Value>{std::string("image"), section.image->id()});
continue;
}
serialized.emplace_back(section.text);
std::unordered_map<std::string, mbgl::Value> options;
if (section.fontScale) {
options.emplace("font-scale", *section.fontScale);
}
if (section.fontStack) {
std::vector<mbgl::Value> fontStack;
for (const auto& font : *section.fontStack) {