From f94460041353be26eb7516d7006a2c83a4aa19f2 Mon Sep 17 00:00:00 2001 From: Alexander Shalamov Date: Tue, 18 Jun 2019 14:41:46 +0300 Subject: [darwin] Bindings for new 'text-writing-mode' layout property --- platform/darwin/scripts/generate-style-code.js | 37 ++++++++++++- platform/darwin/src/MGLStyleLayer.h.ejs | 4 +- platform/darwin/src/MGLStyleLayer.mm.ejs | 4 +- platform/darwin/src/MGLSymbolStyleLayer.h | 69 ++++++++++++++++++++++++ platform/darwin/src/MGLSymbolStyleLayer.mm | 33 ++++++++++++ platform/darwin/test/MGLSymbolStyleLayerTests.mm | 47 ++++++++++++++++ 6 files changed, 189 insertions(+), 5 deletions(-) diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js index 37454cba54..2a825ac9c7 100755 --- a/platform/darwin/scripts/generate-style-code.js +++ b/platform/darwin/scripts/generate-style-code.js @@ -79,6 +79,28 @@ global.camelizeWithLeadingLowercase = function (str) { }); }; +// Returns true only if property is an enum or if it is an array +// property with uniquely defined enum. +global.definesEnum = function(property, allProperties) { + if (property.type === "enum") { + return true; + } + + if (property.type === 'array' && property.value === 'enum') { + const uniqueArrayEnum = (prop, enums) => { + if (prop.value !== 'enum') return false; + const enumsEqual = (val1, val2) => val1.length === val1.length && val1.every((val, i) => val === val2[i]); + return enums.filter(e => enumsEqual(Object.keys(prop.values).sort(), Object.keys(e.values).sort())).length == 0; + }; + + const allEnumProperties = _(allProperties).filter({'type': 'enum'}).value(); + const uniqueArrayEnumProperties = _(allProperties).filter({'type': 'array'}).filter(prop => uniqueArrayEnum(prop, allEnumProperties)).value(); + return _(uniqueArrayEnumProperties).filter({'name': property.name}).value().length != 0; + } + + return false; +} + global.objCName = function (property) { return camelizeWithLeadingLowercase(property.name); }; @@ -144,6 +166,8 @@ global.objCTestValue = function (property, layerType, arraysAsStructs, indent) { } case 'anchor': return `@"{'top','bottom'}"`; + case 'mode': + return `@"{'horizontal','vertical'}"`; default: throw new Error(`unknown array type for ${property.name}`); } @@ -194,6 +218,8 @@ global.mbglTestValue = function (property, layerType) { return '{ 1, 1 }'; case 'anchor': return '{ mbgl::style::SymbolAnchorType::Top, mbgl::style::SymbolAnchorType::Bottom }'; + case 'mode': + return '{ mbgl::style::TextWritingModeType::Horizontal, mbgl::style::TextWritingModeType::Vertical }'; default: throw new Error(`unknown array type for ${property.name}`); } @@ -213,6 +239,8 @@ global.mbglExpressionTestValue = function (property, layerType) { switch (arrayType(property)) { case 'anchor': return `{"top", "bottom"}`; + case 'mode': + return `{"horizontal", "vertical"}`; default: break; } @@ -439,6 +467,8 @@ global.describeType = function (property) { return '`MGLSphericalPosition`'; case 'anchor': return '`MGLTextAnchor` array'; + case 'mode': + return '`MGLTextWritingMode` array'; default: return 'array'; } @@ -568,6 +598,7 @@ global.propertyType = function (property) { case 'translate': return 'NSValue *'; case 'anchor': + case 'mode': return 'NSArray *'; default: throw new Error(`unknown array type for ${property.name}`); @@ -615,6 +646,8 @@ global.valueTransformerArguments = function (property) { return ['std::array', objCType]; case 'anchor': return ['std::vector', objCType, 'mbgl::style::SymbolAnchorType', 'MGLTextAnchor']; + case 'mode': + return ['std::vector', objCType, 'mbgl::style::TextWritingModeType', 'MGLTextWritingMode']; default: throw new Error(`unknown array type for ${property.name}`); } @@ -666,6 +699,8 @@ global.mbglType = function(property) { return 'mbgl::style::Position'; case 'anchor': return 'std::vector'; + case 'mode': + return 'std::vector'; default: throw new Error(`unknown array type for ${property.name}`); } @@ -773,7 +808,7 @@ var renamedPropertiesByLayerType = {}; for (var layer of layers) { layer.properties = _.concat(layer.layoutProperties, layer.paintProperties); - let enumProperties = _.filter(layer.properties, prop => prop.type === 'enum'); + let enumProperties = _.filter(layer.properties, prop => definesEnum(prop, layer.properties)); if (enumProperties.length) { layer.enumProperties = enumProperties; } diff --git a/platform/darwin/src/MGLStyleLayer.h.ejs b/platform/darwin/src/MGLStyleLayer.h.ejs index 9435e0d2ff..4bbb9e9f0d 100644 --- a/platform/darwin/src/MGLStyleLayer.h.ejs +++ b/platform/darwin/src/MGLStyleLayer.h.ejs @@ -19,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN <% for (const property of layoutProperties) { -%> -<% if (property.type == "enum") { -%> +<% if (definesEnum(property, layoutProperties)) { -%> /** <%- propertyDoc(property.name, property, type, 'enum').wrap(80, 1) %> @@ -38,7 +38,7 @@ typedef NS_ENUM(NSUInteger, MGL<%- camelize(property.name) %>) { <% } -%> <% } -%> <% for (const property of paintProperties) { -%> -<% if (property.type == "enum") { -%> +<% if (definesEnum(property, paintProperties)) { -%> /** <%- propertyDoc(property.name, property, type, 'enum').wrap(80, 1) %> diff --git a/platform/darwin/src/MGLStyleLayer.mm.ejs b/platform/darwin/src/MGLStyleLayer.mm.ejs index 26cb3e26f6..e8c8d8cfd9 100644 --- a/platform/darwin/src/MGLStyleLayer.mm.ejs +++ b/platform/darwin/src/MGLStyleLayer.mm.ejs @@ -25,7 +25,7 @@ namespace mbgl { <% if (layoutProperties.length) { -%> <% for (const property of layoutProperties) { -%> -<% if (property.type == "enum") { -%> +<% if (definesEnum(property, layoutProperties)) { -%> MBGL_DEFINE_ENUM(MGL<%- camelize(property.name) %>, { <% for (const value in property.values) { -%> { MGL<%- camelize(property.name) %><%- camelize(value) %>, "<%-value%>" }, @@ -37,7 +37,7 @@ namespace mbgl { <% } -%> <% if (paintProperties.length) { -%> <% for (const property of paintProperties) { -%> -<% if (property.type == "enum") { -%> +<% if (definesEnum(property, paintProperties)) { -%> MBGL_DEFINE_ENUM(MGL<%- camelize(property.name) %>, { <% for (const value in property.values) { -%> { MGL<%- camelize(property.name) %><%- camelize(value) %>, "<%-value%>" }, diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h index 658a31679e..bbb5dae905 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.h +++ b/platform/darwin/src/MGLSymbolStyleLayer.h @@ -316,6 +316,30 @@ typedef NS_ENUM(NSUInteger, MGLTextTransform) { MGLTextTransformLowercase, }; +/** + The property allows to control an orientation of a symbol. Note, that the + property values act as a hint, so that Symbols whose language doesn't support + provided orientation, will be laid out in their natural orientation. Example: + English point symbol will be rendered horizontally even if `"textWritingMode": + ["vertical"]` is set. The order of elements in an array define priority order + for the placement of an orientation variant. + + Values of this type are used in the `MGLSymbolStyleLayer.textWritingMode` + property. + */ +typedef NS_ENUM(NSUInteger, MGLTextWritingMode) { + /** + If a text's language supports horizontal writing mode, symbols with point + placement would be laid out horizontally. + */ + MGLTextWritingModeHorizontal, + /** + If a text's language supports vertical writing mode, symbols with point + placement would be laid out vertically. + */ + MGLTextWritingModeVertical, +}; + /** Controls the frame of reference for `MGLSymbolStyleLayer.iconTranslation`. @@ -1646,6 +1670,38 @@ MGL_EXPORT */ @property (nonatomic, null_resettable) NSExpression *textVariableAnchor; +/** + The property allows to control an orientation of a symbol. Note, that the + property values act as a hint, so that Symbols whose language doesn't support + provided orientation, will be laid out in their natural orientation. Example: + English point symbol will be rendered horizontally even if `"textWritingMode": + ["vertical"]` is set. The order of elements in an array define priority order + for the placement of an orientation variant. + + This property is only applied to the style if `text` is non-`nil`, and + `symbolPlacement` is set to an expression that evaluates to or + `MGLSymbolPlacementPoint`. Otherwise, it is ignored. + + You can set this property to an expression containing any of the following: + + * Constant `MGLTextWritingMode` array values + * Constant array, whose each element is any of the following constant string + values: + * `horizontal`: If a text's language supports horizontal writing mode, + symbols with point placement would be laid out horizontally. + * `vertical`: If a text's language supports vertical writing mode, symbols + with point placement would be laid out vertically. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. + */ +@property (nonatomic, null_resettable) NSExpression *textWritingMode; + #pragma mark - Accessing the Paint Attributes #if TARGET_OS_IPHONE @@ -2382,6 +2438,19 @@ MGL_EXPORT */ @property (readonly) MGLTextTransform MGLTextTransformValue; +/** + Creates a new value object containing the given `MGLTextWritingMode` enumeration. + + @param textWritingMode The value for the new object. + @return A new value object that contains the enumeration value. + */ ++ (instancetype)valueWithMGLTextWritingMode:(MGLTextWritingMode)textWritingMode; + +/** + The `MGLTextWritingMode` enumeration representation of the value. + */ +@property (readonly) MGLTextWritingMode MGLTextWritingModeValue; + /** Creates a new value object containing the given `MGLIconTranslationAnchor` enumeration. diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm index e54a0b9a15..99cfe5db10 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.mm +++ b/platform/darwin/src/MGLSymbolStyleLayer.mm @@ -96,6 +96,11 @@ namespace mbgl { { MGLTextTransformLowercase, "lowercase" }, }); + MBGL_DEFINE_ENUM(MGLTextWritingMode, { + { MGLTextWritingModeHorizontal, "horizontal" }, + { MGLTextWritingModeVertical, "vertical" }, + }); + MBGL_DEFINE_ENUM(MGLIconTranslationAnchor, { { MGLIconTranslationAnchorMap, "map" }, { MGLIconTranslationAnchorViewport, "viewport" }, @@ -1023,6 +1028,24 @@ namespace mbgl { return MGLStyleValueTransformer, NSArray *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toExpression(propertyValue); } +- (void)setTextWritingMode:(NSExpression *)textWritingMode { + MGLAssertStyleLayerIsValid(); + MGLLogDebug(@"Setting textWritingMode: %@", textWritingMode); + + auto mbglValue = MGLStyleValueTransformer, NSArray *, mbgl::style::TextWritingModeType, MGLTextWritingMode>().toPropertyValue>>(textWritingMode, false); + self.rawLayer->setTextWritingMode(mbglValue); +} + +- (NSExpression *)textWritingMode { + MGLAssertStyleLayerIsValid(); + + auto propertyValue = self.rawLayer->getTextWritingMode(); + if (propertyValue.isUndefined()) { + propertyValue = self.rawLayer->getDefaultTextWritingMode(); + } + return MGLStyleValueTransformer, NSArray *, mbgl::style::TextWritingModeType, MGLTextWritingMode>().toExpression(propertyValue); +} + #pragma mark - Accessing the Paint Attributes - (void)setIconColor:(NSExpression *)iconColor { @@ -1599,6 +1622,16 @@ namespace mbgl { return textTransform; } ++ (NSValue *)valueWithMGLTextWritingMode:(MGLTextWritingMode)textWritingMode { + return [NSValue value:&textWritingMode withObjCType:@encode(MGLTextWritingMode)]; +} + +- (MGLTextWritingMode)MGLTextWritingModeValue { + MGLTextWritingMode textWritingMode; + [self getValue:&textWritingMode]; + return textWritingMode; +} + + (NSValue *)valueWithMGLIconTranslationAnchor:(MGLIconTranslationAnchor)iconTranslationAnchor { return [NSValue value:&iconTranslationAnchor withObjCType:@encode(MGLIconTranslationAnchor)]; } diff --git a/platform/darwin/test/MGLSymbolStyleLayerTests.mm b/platform/darwin/test/MGLSymbolStyleLayerTests.mm index 87091acc1b..0f51fd000c 100644 --- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm +++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm @@ -2074,6 +2074,50 @@ XCTAssertThrowsSpecificNamed(layer.textVariableAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } + // text-writing-mode + { + XCTAssertTrue(rawLayer->getTextWritingMode().isUndefined(), + @"text-writing-mode should be unset initially."); + NSExpression *defaultExpression = layer.textWritingMode; + + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"{'horizontal','vertical'}"]; + layer.textWritingMode = constantExpression; + mbgl::style::PropertyValue> propertyValue = { { mbgl::style::TextWritingModeType::Horizontal, mbgl::style::TextWritingModeType::Vertical } }; + XCTAssertEqual(rawLayer->getTextWritingMode(), propertyValue, + @"Setting textWritingMode to a constant value expression should update text-writing-mode."); + XCTAssertEqualObjects(layer.textWritingMode, constantExpression, + @"textWritingMode should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"{'horizontal','vertical'}"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textWritingMode = functionExpression; + + { + using namespace mbgl::style::expression::dsl; + propertyValue = mbgl::style::PropertyExpression>( + step(zoom(), literal({"horizontal", "vertical"}), 18.0, literal({"horizontal", "vertical"})) + ); + } + + XCTAssertEqual(rawLayer->getTextWritingMode(), propertyValue, + @"Setting textWritingMode to a camera expression should update text-writing-mode."); + XCTAssertEqualObjects(layer.textWritingMode, functionExpression, + @"textWritingMode should round-trip camera expressions."); + + + layer.textWritingMode = nil; + XCTAssertTrue(rawLayer->getTextWritingMode().isUndefined(), + @"Unsetting textWritingMode should return text-writing-mode to the default value."); + XCTAssertEqualObjects(layer.textWritingMode, defaultExpression, + @"textWritingMode should return the default value after being unset."); + + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.textWritingMode = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.textWritingMode = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + } + // icon-color { XCTAssertTrue(rawLayer->getIconColor().isUndefined(), @@ -3084,6 +3128,7 @@ [self testPropertyName:@"text-rotation-alignment" isBoolean:NO]; [self testPropertyName:@"text-transform" isBoolean:NO]; [self testPropertyName:@"text-variable-anchor" isBoolean:NO]; + [self testPropertyName:@"text-writing-mode" isBoolean:NO]; [self testPropertyName:@"icon-color" isBoolean:NO]; [self testPropertyName:@"icon-halo-blur" isBoolean:NO]; [self testPropertyName:@"icon-halo-color" isBoolean:NO]; @@ -3148,6 +3193,8 @@ XCTAssertEqual([NSValue valueWithMGLTextTransform:MGLTextTransformNone].MGLTextTransformValue, MGLTextTransformNone); XCTAssertEqual([NSValue valueWithMGLTextTransform:MGLTextTransformUppercase].MGLTextTransformValue, MGLTextTransformUppercase); XCTAssertEqual([NSValue valueWithMGLTextTransform:MGLTextTransformLowercase].MGLTextTransformValue, MGLTextTransformLowercase); + XCTAssertEqual([NSValue valueWithMGLTextWritingMode:MGLTextWritingModeHorizontal].MGLTextWritingModeValue, MGLTextWritingModeHorizontal); + XCTAssertEqual([NSValue valueWithMGLTextWritingMode:MGLTextWritingModeVertical].MGLTextWritingModeValue, MGLTextWritingModeVertical); XCTAssertEqual([NSValue valueWithMGLIconTranslationAnchor:MGLIconTranslationAnchorMap].MGLIconTranslationAnchorValue, MGLIconTranslationAnchorMap); XCTAssertEqual([NSValue valueWithMGLIconTranslationAnchor:MGLIconTranslationAnchorViewport].MGLIconTranslationAnchorValue, MGLIconTranslationAnchorViewport); XCTAssertEqual([NSValue valueWithMGLTextTranslationAnchor:MGLTextTranslationAnchorMap].MGLTextTranslationAnchorValue, MGLTextTranslationAnchorMap); -- cgit v1.2.1