-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[ios, macos] Add format expression convenience methods support. #14094
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggested changelog entry:
- Added the
mgl_attributed:
andMGL_ATTRIBUTED
expression operators, which concatenateMGLAttributedExpression
objects for specifying rich text in theMGLSymbolStyleLayer.text
property.
Also, the “Predicates and Expressions” guide should be updated to reflect the addition of these new operators.
FOUNDATION_EXTERN MGL_EXPORT MGLAttributedExpressionKey const MGLFontSizeAttribute; | ||
|
||
MGL_EXPORT | ||
@interface MGLAttributedExpression : NSObject |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class seems to represent an attribute range (or attribute run) rather than an expression in its own right. Naming it MGLAttributedExpression could cause some confusion, since it looks like a subclass of NSExpression that can be set directly on NSExpression-typed properties. Acknowledging the difference with a name like MGLAttributedRun (akin to Core Text’s CTRun) would head off that confusion.
Ideally, we’d create a system akin to NSAttributedString, where you build up an NSAttributedExpression by applying attributes to ranges of the string and enumerate those ranges. But that’s potentially a large API surface area to implement, and it’s difficult to think in terms of ranges when the underlying content is of undefined length.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm using the same mental model between NSString
and NSAttributedString
where you can't assign the later to the first one and vice versa. I think MGLAttributedExpression
keeps the concept of an object that contains an expression with attributes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a little different though: NSString doesn’t hold an array of NSAttributedStrings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, we have:
textField(
format(
formatEntry("this is", formatFontScale(0.75)),
formatEntry(
concat(literal("\n"), get(TITLE_FEATURE_PROPERTY)),
formatFontScale(1.25),
formatTextFont(new String[] {"DIN Offc Pro Bold", "Arial Unicode MS Regular"})
)
)
),
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the concept that resembles the intent is MGLAttributedExpression
, in regards to MGLTextFormatAttributes
the underlaying object is not text per se (although it may contain a string constant).
@@ -1115,6 +1139,11 @@ - (id)mgl_jsonExpressionObject { | |||
} | |||
[NSException raise:NSInvalidArgumentException | |||
format:@"Casting expression to %@ not yet implemented.", type]; | |||
} else if ([function isEqualToString:@"mgl_attributed:"] || | |||
[function isEqualToString:@"MGL_FORMAT:"] || | |||
[function isEqualToString:@"MGL_FORMAT"]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MGL_FORMAT
looks like something related to printf
-style string formatting or number formatting (#13632) rather than rich text. Let’s stick to “attributed” terminology if possible.
Apart from that, MGL_FORMAT
and MGL_FORMAT:
need to be installed above in +installFunctions
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forgot to remove this lines I just wanted to use mgl_attributed:
do you think it would be useful have MGL_ATTRIBUTED
as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, no need.
MGLAttributedExpression *attribute3 = [MGLAttributedExpression initWithExpression:[NSExpression expressionForConstantValue:@"bar"] | ||
fontNames:nil | ||
fontSize:@(0.8)]; | ||
MGLAttributedExpression *attribute4 = [MGLAttributedExpression initWithExpression:[NSExpression expressionForConstantValue:@"\r"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to support the newline \n
as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does support '\n' I will add a test for this.
MGLAttributedExpression *attribute4 = [MGLAttributedExpression initWithExpression:[NSExpression expressionForConstantValue:@"\r"] | ||
fontNames:@[] | ||
fontSize:nil]; | ||
NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_attributed(%@)", @[attribute1, attribute4, attribute2, attribute3]]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#12739 also envisioned a conversion from NSAttributedString to the format
expression operator as a convenience. (A conversion to MGLAttributedExpression might make more sense, on second thought.) Note that NSAttributedString isn’t only intended for rich text; it’s meant to be a general-purpose container for strings that have attributes associated with portions of the text.
That doesn’t necessarily happen to happen as part of this PR, but I’d hold off on closing #12739 until there is something like that. The use case for an NSAttributedString conversion is a constant expression with sophisticated formatting that would be tedious to express as an NSExpression format string.
NSMutableDictionary *attrs = [NSMutableDictionary dictionary]; | ||
|
||
if (fontNames && fontNames.count > 0) { | ||
[attrs setObject:fontNames forKey:MGLFontNamesAttribute]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: use subscript notation:
[attrs setObject:fontNames forKey:MGLFontNamesAttribute]; | |
attrs[MGLFontNamesAttribute] = fontNames; |
(×2)
FOUNDATION_EXTERN MGL_EXPORT MGLAttributedExpressionKey const MGLFontSizeAttribute; | ||
|
||
MGL_EXPORT | ||
@interface MGLAttributedExpression : NSObject |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this class is public, remember to give it a documentation comment.
formatParameter = self.operand; | ||
} | ||
|
||
if ([formatParameter respondsToSelector:@selector(constantValue)] && [formatParameter.constantValue isKindOfClass:[NSArray class]]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A developer might be tempted to create a constant-value NSExpression from a bare MGLAttributedExpression, which would be an error here. Maybe we could convert a standalone, constant MGLAttributedExpression into format
expression too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we do that, then it starts to seem like we could combine this operator with mgl_join:
so that joining MGLAttributedExpressions together creates a format
expression instead of a concat
expression.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not thrilled about the naming of MGLAttributedExpression (see that thread), but that’s all I’m concerned about here. The implementation looks good.
<dd><code>mgl_attributed({x, y, z})</code></dd> | ||
</dl> | ||
|
||
Returns formatted text containing annotations for use in mixed-format `text-field` entries. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concatenates and returns the array of
MGLAttributedExpression
objects, for use with theMGLSymbolStyleLayer.text
property.
Returns formatted text containing annotations for use in mixed-format `text-field` entries. | ||
|
||
This function corresponds to the | ||
[`mgl_attributed`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-format) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It’s called format
in the style specification.
FOUNDATION_EXTERN MGL_EXPORT MGLAttributedExpressionKey const MGLFontSizeAttribute; | ||
|
||
/** | ||
An `NSExpression` that has associated text formatting attibutes (such as font size or |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn’t an NSExpression. It’s something that can be inserted into a specific operator inside an NSExpression.
@@ -403,6 +403,7 @@ In style specification | Method, function, or predicate type | Format string syn | |||
`zoom` | `NSExpression.zoomLevelVariableExpression` | `$zoomLevel` | |||
`heatmap-density` | `NSExpression.heatmapDensityVariableExpression` | `$heatmapDensity` | |||
`line-progress` | `NSExpression.lineProgressVariableExpression` | `$lineProgress` | |||
`format` | `mgl_attributed:` | `mgl_attributed({x, y, z})` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we extend NSExpression with an initializer corresponding to this operator?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think makes sense.
46adc67
to
e6faefe
Compare
Thank you @fabian-guerra for getting this in 👍 |
Fixes #12739
I'm using
MGLAttributedExpression
as a custom structure that holds an expression and it's respective attributes. This structure is treated as a constant value but the underlaying expression property can be of any kind.