MGLStyle key-value coding compliance#6097
Conversation
f0d8a22 to
6f19b2f
Compare
| // Layers | ||
| std::vector<style::Layer*> getLayers(); | ||
| style::Layer* getLayer(const std::string& layerID); | ||
| void setLayers(std::vector<style::Layer*>&); |
There was a problem hiding this comment.
Map/Style must have ownership of the layers, so this signature needs to be void setLayers(std::vector<std::unique_ptr<style::Layer>>).
25f0c27 to
03d59a0
Compare
b11c4d6 to
40d77ae
Compare
40d77ae to
25f1529
Compare
abe1845 to
dc4aef8
Compare
| } | ||
|
|
||
| void Map::setLayers(std::vector<std::unique_ptr<style::Layer>> layers) { | ||
| impl->view.activate(); |
There was a problem hiding this comment.
Needs an if (!impl->style) guard and impl->styleMutated = true; like the other mutators.
| std::vector<std::unique_ptr<mbgl::style::Layer>> rawLayers; | ||
| rawLayers.reserve(layers.count); | ||
| for (MGLStyleLayer *layer in layers.reverseObjectEnumerator) { | ||
| rawLayers.push_back(std::unique_ptr<mbgl::style::Layer>(layer.layer)); |
There was a problem hiding this comment.
Constructing a std::unique_ptr from anything other than the result of new is usually incorrect (and even then, you should be using std::make_unique). What's the ownership model of layer.layer?
There was a problem hiding this comment.
It’s complicated: #6254. 😕 Maybe we need to tackle that issue first.
There was a problem hiding this comment.
To respect ownership of the layers passed into -setLayers: – some of which may exist in the style and some of which may be new – we need to ability to remove a layer from the style and regain ownership of it in order to re-add it. So we’re blocked on mbgl::Map::removeLayer() returning ownership of the layer: #6959.
297337a to
a789dd5
Compare
a789dd5 to
ec57aea
Compare
|
Great work here @1ec5 |
c45be63 to
3dd8910
Compare
There was a problem hiding this comment.
This goes in the opposite branch -- we're only mutating the style if we have one.
There was a problem hiding this comment.
👍 I ended up not using this method in the SDK due to the memory management conundrum in the PR description. Should I keep this method around to complement getLayers() or remove it?
There was a problem hiding this comment.
Yes, please remove it if it's not used.
There was a problem hiding this comment.
Not part of this PR, but this cast is undefined behavior. You need to write something like:
template <class T, class U>
std::unique_ptr<T> dynamic_ptr_cast(std::unique_ptr<U> p) {
T* t = dynamic_cast<T*>(p.get());
if (!t) return nullptr;
p.release();
return t;
}
And be aware that this will return a null unique_ptr (and deallocate the input) if the dynamic_cast fails, i.e. if the input is not the anticipated derived type. In this particular code, it looks like this could happen if the user was aliasing *StyleLayer objects while at the same time removing a layer and adding another layer with the same identifier but a different layer type.
3dd8910 to
f331b89
Compare
Added new layers and sources properties to MGLStyle that contain all the style’s layers and sources, respectively. These properties are KVC-compliant with all the mutable to-many methods. Layers are ordered from topmost to bottommost, for consistency with Cocoa APIs where front/first means top and back/last means bottom. Also added storage for mbgl::style::Source in MGLSource proper for wrapping AnnotationSource. Until the style finishes loading, its name property is set to nil. Fixes #6003.
Replaced -[MGLMapView style] with a property. Keep the MGLStyle object around for the lifetime of the style. Bracket changes to layers in willChange and didChange calls. The built-in point annotation layer is added after the style is finished loading but before the map is finished loading. Cause a second wave of change notifications to go out, about both sources and layers. Issue change notifications for style layers when shape annotations are added or removed.
Fixed a crash that often occurred when switching styles. MGLMapView was ahead of MapDocument in the responder chain, so calls to -setStyle: were going to MGLMapView’s private -setStyle: method. The sender was being passed in the first parameter instead of an MGLStyle method.
Added a sidebar to the main document window in macosapp that lists the layers in the current style and updates whenever the style changes or a layer is added or removed programmatically. Display an icon beside each layer in the sidebar that indicates the kind of layer. Double-click a layer or layers to toggle their visibility, which is an undoable action. Added a menu item and toolbar button to toggle the Layers sidebar. Added a context menu for toggling visibility of and deleting layers selected in the Layers sidebar. Checked in the original SVGs for layer icons.
Display the custom style’s name in the Style toolbar button.
Added a setting to macosapp to change all symbol layers in the current style to use the preferred language if available. If the setting is off, change all symbol layers to use local languages.
f331b89 to
83e134e
Compare
This PR makes the runtime styling API’s entry points key-value coding compliant.
The
-[MGLMapView style]factory method has been replaced with a KVC-compliantstyleproperty whose value changes only when reloading the style or changing the style URL. This makes the API more convenient in Swift and more predictable overall.New
layersandsourcesproperties have been added to MGLStyle that make it easy to enumerate style layers or sources without having to hard-code style-specific identifiers in application code. See #5999, #7030, and #7031 for examples of how these properties would be used.The
layersarray is ordered from front to back, for consistency with Cocoa, as opposed to mbgl’s back-to-front ordering. There are complementary-insertLayer:aboveLayer:and-insertLayer:belowLayer:methods, for consistency with MapKit’s-[MKMapView insertOverlay:aboveOverlay:]and-[MKMapView insertOverlay:belowOverlay:]. Please double-check my index arithmetic to make sure I didn’t make any off-by-one errors. Thesourcesproperty is a unordered set. A dictionary mapping identifiers to source objects would better conform to the style specification but would encourage some antipatterns we want to avoid; see #7011 (comment) for details.The mutable
layersandsourcescollections comply with KVC conventions for to-many relationships, which entails additional methods thatmbgl::Mapandmbgl::style::Stylewouldn’t ordinarily provide. These KVC methods could be optimized further with corresponding methods in mbgl; however, memory management would become a lot less clear. For example, who owns anmbgl::style::Layerif it’s already part of a style but is included in an array passed into-[MGLStyle setLayers:]? Rather than muddle the pointer ownership model or implement complex array diffing, this PR sticks to the safest, most straightforward implementation despite a possible performance tradeoff.As a real-world stress test of KVC compliance, a sidebar has been added to macosapp that displays the layers in the current style and updates the list automatically using Cocoa bindings. The sidebar can be opened and closed via View ‣ Show/Hide Layers or ⇧⌘L. Layers are shown with their identifiers and icons representing their types. Hidden layers are shown in italics. To toggle one or more layers’ visibility, select them and double-click on them. Or use the context menu, which also has an option to delete the selected layers.
Finally, to illustrate one common use of the new APIs, macosapp now has an option to localize all labels in the user’s preferred language, and the Style toolbar button displays the name of the current style when a custom style is shown.
Hiding layers using the new Layers sidebar in macosapp.
styleproperty KVC-compliant-[MGLStyle insertLayer:aboveLayer:](reprising [macos, ios] Insert layer above #6730)Fixes #6003, fixes #6096, fixes #6931.
/cc @frederoni @jfirebaugh