Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[macos] Added Layers sidebar
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
1ec5 committed Nov 28, 2016
1 parent 03a2dbe commit 992726a
Show file tree
Hide file tree
Showing 19 changed files with 730 additions and 16 deletions.
6 changes: 6 additions & 0 deletions platform/macos/app/Assets.xcassets/Layers/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "background.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "circle.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "fill.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "symbol.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.
5 changes: 5 additions & 0 deletions platform/macos/app/Base.lproj/MainMenu.xib
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,11 @@
<action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
</connections>
</menuItem>
<menuItem title="Show Layers" keyEquivalent="L" id="qtg-l9-BH3">
<connections>
<action selector="toggleLayers:" target="-1" id="YdA-Mr-MHi"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
Expand Down
185 changes: 172 additions & 13 deletions platform/macos/app/Base.lproj/MapDocument.xib

Large diffs are not rendered by default.

150 changes: 147 additions & 3 deletions platform/macos/app/MapDocument.m
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,12 @@
return flattenedShapes;
}

@interface MapDocument () <NSWindowDelegate, NSSharingServicePickerDelegate, NSMenuDelegate, MGLMapViewDelegate>
@interface MapDocument () <NSWindowDelegate, NSSharingServicePickerDelegate, NSMenuDelegate, NSSplitViewDelegate, MGLMapViewDelegate>

@property (weak) IBOutlet NSArrayController *styleLayersArrayController;
@property (weak) IBOutlet NSTableView *styleLayersTableView;
@property (weak) IBOutlet NSMenu *mapViewContextMenu;
@property (weak) IBOutlet NSSplitView *splitView;

@end

Expand Down Expand Up @@ -98,6 +101,8 @@ - (void)windowControllerDidLoadNib:(NSWindowController *)controller {
NSPressGestureRecognizer *pressGestureRecognizer = [[NSPressGestureRecognizer alloc] initWithTarget:self action:@selector(handlePressGesture:)];
[self.mapView addGestureRecognizer:pressGestureRecognizer];

[self.splitView setPosition:0 ofDividerAtIndex:0];

[self applyPendingState];
}

Expand Down Expand Up @@ -174,6 +179,7 @@ - (IBAction)showStyle:(id)sender {
NSAssert(NO, @"Cannot set style from control with tag %li", (long)tag);
break;
}
[self.undoManager removeAllActionsWithTarget:self];
self.mapView.styleURL = styleURL;
[self.window.toolbar validateVisibleItems];
}
Expand All @@ -195,6 +201,7 @@ - (IBAction)chooseCustomStyle:(id)sender {
[alert addButtonWithTitle:@"Apply"];
[alert addButtonWithTitle:@"Cancel"];
if ([alert runModal] == NSAlertFirstButtonReturn) {
[self.undoManager removeAllActionsWithTarget:self];
self.mapView.styleURL = [NSURL URLWithString:textField.stringValue];
[[NSUserDefaults standardUserDefaults] setURL:self.mapView.styleURL forKey:@"MBXCustomStyleURL"];
[self.window.toolbar validateVisibleItems];
Expand All @@ -214,9 +221,110 @@ - (IBAction)snapToNorth:(id)sender {
}

- (IBAction)reload:(id)sender {
[self.undoManager removeAllActionsWithTarget:self];
[self.mapView reloadStyle:sender];
}

/**
Show or hide the Layers sidebar.
*/
- (IBAction)toggleLayers:(id)sender {
BOOL isShown = ![self.splitView isSubviewCollapsed:self.splitView.arrangedSubviews.firstObject];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext * _Nonnull context) {
context.allowsImplicitAnimation = YES;
[self.splitView setPosition:isShown ? 0 : 100 ofDividerAtIndex:0];
[self.window.toolbar validateVisibleItems];
} completionHandler:nil];
}

/**
Show or hide the selected layers.
*/
- (IBAction)toggleStyleLayers:(id)sender {
NSInteger clickedRow = self.styleLayersTableView.clickedRow;
NSIndexSet *indices = self.styleLayersTableView.selectedRowIndexes;
if (clickedRow >= 0 && ![indices containsIndex:clickedRow]) {
indices = [NSIndexSet indexSetWithIndex:clickedRow];
}
[self toggleStyleLayersAtArrangedObjectIndexes:indices];
}

- (void)toggleStyleLayersAtArrangedObjectIndexes:(NSIndexSet *)indices {
NS_ARRAY_OF(MGLStyleLayer *) *layers = [self.mapView.style.layers objectsAtIndexes:indices];
BOOL isVisible = layers.firstObject.visible;
[self.undoManager registerUndoWithTarget:self handler:^(MapDocument * _Nonnull target) {
[target toggleStyleLayersAtArrangedObjectIndexes:indices];
}];

if (!self.undoManager.undoing) {
NSString *actionName;
if (indices.count == 1) {
actionName = [NSString stringWithFormat:@"%@ Layer “%@", isVisible ? @"Hide" : @"Show", layers.firstObject.identifier];
} else {
actionName = [NSString stringWithFormat:@"%@ %@ Layers", isVisible ? @"Hide" : @"Show",
[NSNumberFormatter localizedStringFromNumber:@(indices.count)
numberStyle:NSNumberFormatterDecimalStyle]];
}
[self.undoManager setActionIsDiscardable:YES];
[self.undoManager setActionName:actionName];
}

for (MGLStyleLayer *layer in layers) {
layer.visible = !isVisible;
}

NSIndexSet *columnIndices = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)];
[self.styleLayersTableView reloadDataForRowIndexes:indices columnIndexes:columnIndices];
}

- (IBAction)deleteStyleLayers:(id)sender {
NSInteger clickedRow = self.styleLayersTableView.clickedRow;
NSIndexSet *indices = self.styleLayersTableView.selectedRowIndexes;
if (clickedRow >= 0 && ![indices containsIndex:clickedRow]) {
indices = [NSIndexSet indexSetWithIndex:clickedRow];
}
[self deleteStyleLayersAtArrangedObjectIndexes:indices];
}

- (void)insertStyleLayers:(NS_ARRAY_OF(MGLStyleLayer *) *)layers atArrangedObjectIndexes:(NSIndexSet *)indices {
[self.undoManager registerUndoWithTarget:self handler:^(id _Nonnull target) {
[self deleteStyleLayersAtArrangedObjectIndexes:indices];
}];

if (!self.undoManager.undoing) {
NSString *actionName;
if (indices.count == 1) {
actionName = [NSString stringWithFormat:@"Add Layer “%@", layers.firstObject.identifier];
} else {
actionName = [NSString stringWithFormat:@"Add %@ Layers",
[NSNumberFormatter localizedStringFromNumber:@(indices.count) numberStyle:NSNumberFormatterDecimalStyle]];
}
[self.undoManager setActionName:actionName];
}

[self.styleLayersArrayController insertObjects:layers atArrangedObjectIndexes:indices];
}

- (void)deleteStyleLayersAtArrangedObjectIndexes:(NSIndexSet *)indices {
NS_ARRAY_OF(MGLStyleLayer *) *layers = [self.mapView.style.layers objectsAtIndexes:indices];
[self.undoManager registerUndoWithTarget:self handler:^(id _Nonnull target) {
[self insertStyleLayers:layers atArrangedObjectIndexes:indices];
}];

if (!self.undoManager.undoing) {
NSString *actionName;
if (indices.count == 1) {
actionName = [NSString stringWithFormat:@"Delete Layer “%@", layers.firstObject.identifier];
} else {
actionName = [NSString stringWithFormat:@"Delete %@ Layers",
[NSNumberFormatter localizedStringFromNumber:@(indices.count) numberStyle:NSNumberFormatterDecimalStyle]];
}
[self.undoManager setActionName:actionName];
}

[self.styleLayersArrayController removeObjectsAtArrangedObjectIndexes:indices];
}

- (void)applyPendingState {
if (_inheritedStyleURL) {
self.mapView.styleURL = _inheritedStyleURL;
Expand Down Expand Up @@ -607,6 +715,27 @@ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
if (menuItem.action == @selector(reload:)) {
return YES;
}
if (menuItem.action == @selector(toggleLayers:)) {
BOOL isShown = ![self.splitView isSubviewCollapsed:self.splitView.arrangedSubviews.firstObject];
menuItem.title = isShown ? @"Hide Layers" : @"Show Layers";
return YES;
}
if (menuItem.action == @selector(toggleStyleLayers:)) {
NSInteger row = self.styleLayersTableView.clickedRow;
if (row == -1) {
row = self.styleLayersTableView.selectedRow;
}
if (row == -1) {
menuItem.title = @"Show";
} else {
BOOL isVisible = self.mapView.style.layers[row].visible;
menuItem.title = isVisible ? @"Hide" : @"Show";
}
return row != -1;
}
if (menuItem.action == @selector(deleteStyleLayers:)) {
return self.styleLayersTableView.clickedRow >= 0 || self.styleLayersTableView.selectedRow >= 0;
}
if (menuItem.action == @selector(manipulateStyle:)) {
return YES;
}
Expand Down Expand Up @@ -722,7 +851,8 @@ - (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem {
return NO;
}

if (toolbarItem.action == @selector(showShareMenu:)) {
SEL action = toolbarItem.action;
if (action == @selector(showShareMenu:)) {
[(NSButton *)toolbarItem.view sendActionOn:NSLeftMouseDownMask];
if (![MGLAccountManager accessToken]) {
return NO;
Expand All @@ -731,7 +861,7 @@ - (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem {
return ([styleURL.scheme isEqualToString:@"mapbox"]
&& [styleURL.pathComponents.firstObject isEqualToString:@"styles"]);
}
if (toolbarItem.action == @selector(showStyle:)) {
if (action == @selector(showStyle:)) {
NSPopUpButton *popUpButton = (NSPopUpButton *)toolbarItem.view;
NSUInteger index = self.indexOfStyleInToolbarItem;
if (index == NSNotFound) {
Expand All @@ -740,6 +870,10 @@ - (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem {
}
[popUpButton selectItemAtIndex:index];
}
if (action == @selector(toggleLayers:)) {
BOOL isShown = ![self.splitView isSubviewCollapsed:self.splitView.arrangedSubviews.firstObject];
[(NSButton *)toolbarItem.view setState:isShown ? NSOnState : NSOffState];
}
return NO;
}

Expand Down Expand Up @@ -773,6 +907,16 @@ - (void)menuWillOpen:(NSMenu *)menu {
}
}

#pragma mark NSSplitViewDelegate methods

- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview {
return subview != self.mapView;
}

- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex {
return YES;
}

#pragma mark MGLMapViewDelegate methods

- (BOOL)mapView:(MGLMapView *)mapView annotationCanShowCallout:(id <MGLAnnotation>)annotation {
Expand Down
5 changes: 5 additions & 0 deletions platform/macos/app/StyleLayerIconTransformer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>

@interface StyleLayerIconTransformer : NSValueTransformer

@end
38 changes: 38 additions & 0 deletions platform/macos/app/StyleLayerIconTransformer.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#import "StyleLayerIconTransformer.h"

#import <Mapbox/Mapbox.h>

@implementation StyleLayerIconTransformer

+ (Class)transformedValueClass {
return [NSString class];
}

+ (BOOL)allowsReverseTransformation {
return NO;
}

- (id)transformedValue:(MGLStyleLayer *)layer {
if ([layer isKindOfClass:[MGLBackgroundStyleLayer class]]) {
return [NSImage imageNamed:@"background"];
}
if ([layer isKindOfClass:[MGLCircleStyleLayer class]]) {
return [NSImage imageNamed:@"circle"];
}
if ([layer isKindOfClass:[MGLFillStyleLayer class]]) {
return [NSImage imageNamed:@"fill"];
}
if ([layer isKindOfClass:[MGLLineStyleLayer class]]) {
return [NSImage imageNamed:@"NSListViewTemplate"];
}
if ([layer isKindOfClass:[MGLRasterStyleLayer class]]) {
return [[NSWorkspace sharedWorkspace] iconForFileType:@"jpg"];
}
if ([layer isKindOfClass:[MGLSymbolStyleLayer class]]) {
return [NSImage imageNamed:@"symbol"];
}

return nil;
}

@end
Loading

0 comments on commit 992726a

Please sign in to comment.