From c3b17357f91f768ba7b0fa17254bbd7cccdb8300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Tue, 27 Dec 2016 11:58:20 -0800 Subject: [macos] Layer filter editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a Layer Filter panel to the bottom of the macosapp document window that edits the selected layer’s predicate. Subclassed NSPredicateEditorRowTemplate to allow for arbitrary key paths in a single template and also to represent an aggregate value as a token field. --- platform/macos/app/Base.lproj/MainMenu.xib | 6 + platform/macos/app/Base.lproj/MapDocument.xib | 230 +++++++++++++++++++-- .../FeatureAttributePredicateEditorRowTemplate.h | 5 + .../FeatureAttributePredicateEditorRowTemplate.m | 147 +++++++++++++ platform/macos/app/MapDocument.m | 34 ++- platform/macos/macos.xcodeproj/project.pbxproj | 6 + 6 files changed, 407 insertions(+), 21 deletions(-) create mode 100644 platform/macos/app/FeatureAttributePredicateEditorRowTemplate.h create mode 100644 platform/macos/app/FeatureAttributePredicateEditorRowTemplate.m diff --git a/platform/macos/app/Base.lproj/MainMenu.xib b/platform/macos/app/Base.lproj/MainMenu.xib index 3243838848..1c7ff6871c 100644 --- a/platform/macos/app/Base.lproj/MainMenu.xib +++ b/platform/macos/app/Base.lproj/MainMenu.xib @@ -451,6 +451,12 @@ + + + + + + diff --git a/platform/macos/app/Base.lproj/MapDocument.xib b/platform/macos/app/Base.lproj/MapDocument.xib index 0394f38533..3a593e6686 100644 --- a/platform/macos/app/Base.lproj/MapDocument.xib +++ b/platform/macos/app/Base.lproj/MapDocument.xib @@ -9,8 +9,9 @@ - + + @@ -38,6 +39,7 @@ identifier visible + predicate @@ -57,14 +59,14 @@ - + - + - + @@ -87,7 +89,7 @@ - + @@ -128,21 +130,223 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + string + + + + + + + + + string + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + integer + + + + + + + + + integer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + float + + + + + + + + + float + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + - - + - + diff --git a/platform/macos/app/FeatureAttributePredicateEditorRowTemplate.h b/platform/macos/app/FeatureAttributePredicateEditorRowTemplate.h new file mode 100644 index 0000000000..6fe6b1b911 --- /dev/null +++ b/platform/macos/app/FeatureAttributePredicateEditorRowTemplate.h @@ -0,0 +1,5 @@ +#import + +@interface FeatureAttributePredicateEditorRowTemplate : NSPredicateEditorRowTemplate + +@end diff --git a/platform/macos/app/FeatureAttributePredicateEditorRowTemplate.m b/platform/macos/app/FeatureAttributePredicateEditorRowTemplate.m new file mode 100644 index 0000000000..0d19bd1635 --- /dev/null +++ b/platform/macos/app/FeatureAttributePredicateEditorRowTemplate.m @@ -0,0 +1,147 @@ +#import "FeatureAttributePredicateEditorRowTemplate.h" + +#import + +@implementation MGLStyleLayer (MBXAdditions) + +- (NSPredicate *)predicate { + return nil; +} + +- (void)setPredicate:(NSPredicate *)predicate { +} + +@end + +@implementation FeatureAttributePredicateEditorRowTemplate { + NSTextField *_keyPathTextField; + NSPopUpButton *_operatorPopUpButton; + NSTextField *_valueTextField; + NSTokenField *_valueTokenField; +} + +- (double)matchForPredicate:(NSComparisonPredicate *)predicate { + if (![predicate isKindOfClass:[NSComparisonPredicate class]]) { + return 0; + } + + id constantValue = predicate.rightExpression.constantValue; + if ([constantValue isKindOfClass:[NSArray class]]) { + constantValue = [[constantValue firstObject] constantValue]; + } + switch (self.rightExpressionAttributeType) { + case NSBooleanAttributeType: + return ([constantValue isKindOfClass:[NSNumber class]] + && (strcmp([constantValue objCType], @encode(char)) == 0 + || strcmp([constantValue objCType], @encode(BOOL)) == 0)) ? 1 : 0; + + case NSDoubleAttributeType: + case NSFloatAttributeType: + return ([constantValue isKindOfClass:[NSNumber class]] + && (strcmp([constantValue objCType], @encode(double)) == 0 + || strcmp([constantValue objCType], @encode(float)) == 0)) ? 1 : 0; + + case NSInteger16AttributeType: + case NSInteger32AttributeType: + case NSInteger64AttributeType: + return ([constantValue isKindOfClass:[NSNumber class]] + && strcmp([constantValue objCType], @encode(double)) != 0 + && strcmp([constantValue objCType], @encode(float)) != 0 + && strcmp([constantValue objCType], @encode(char)) != 0 + && strcmp([constantValue objCType], @encode(BOOL)) != 0) ? 1 : 0; + + case NSStringAttributeType: + return [constantValue isKindOfClass:[NSString class]] ? 0.8 : 0; + + default: + return [super matchForPredicate:predicate]; + } +} + +- (NS_ARRAY_OF(NSView *) *)templateViews { + NSMutableArray *views = super.templateViews.mutableCopy; + views[0] = self.keyPathTextField; + _operatorPopUpButton = views[1]; + _valueTextField = views[2]; + + BOOL isInPredicate = [_operatorPopUpButton.selectedItem.representedObject unsignedIntegerValue] == NSInPredicateOperatorType; + if (isInPredicate) { + [views insertObject:self.valueTokenField atIndex:2]; + } else { + [views addObject:self.valueTokenField]; + } + [views[2] setHidden:NO]; + [views[3] setHidden:YES]; + + return views; +} + +- (NSTextField *)keyPathTextField { + if (!_keyPathTextField) { + _keyPathTextField = [[NSTextField alloc] initWithFrame:NSZeroRect]; + _keyPathTextField.editable = NO; + _keyPathTextField.bordered = NO; + _keyPathTextField.backgroundColor = nil; + _keyPathTextField.font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]; + } + [_keyPathTextField sizeToFit]; + return _keyPathTextField; +} + +- (NSTokenField *)valueTokenField { + if (!_valueTokenField) { + _valueTokenField = [[NSTokenField alloc] initWithFrame:NSZeroRect]; + _valueTokenField.font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]; + } + [_valueTokenField sizeToFit]; + NSRect frame = _valueTokenField.frame; + frame.size.width = _valueTextField.frame.size.width; + _valueTokenField.frame = frame; + return _valueTokenField; +} + +- (NSPredicate *)predicateWithSubpredicates:(NS_ARRAY_OF(NSPredicate *) *)subpredicates { + NSComparisonPredicate *predicate = (NSComparisonPredicate *)[super predicateWithSubpredicates:subpredicates]; + NSAssert([predicate isKindOfClass:[NSComparisonPredicate class]], @"FeatureAttributePredicateEditorRowTemplate only works with comparison predicates."); + + NSExpression *leftExpression = [NSExpression expressionForKeyPath:self.keyPathTextField.stringValue]; + NSExpression *rightExpression = predicate.rightExpression; + if (predicate.predicateOperatorType == NSInPredicateOperatorType) { + rightExpression = [NSExpression expressionForAggregate: + [_valueTokenField.stringValue componentsSeparatedByString:@","]]; + } + return [NSComparisonPredicate predicateWithLeftExpression:leftExpression + rightExpression:rightExpression + modifier:predicate.comparisonPredicateModifier + type:predicate.predicateOperatorType + options:predicate.options]; +} + +- (void)setPredicate:(NSComparisonPredicate *)predicate { + NSAssert([predicate isKindOfClass:[NSComparisonPredicate class]], @"FeatureAttributePredicateEditorRowTemplate only works with comparison predicates."); + + self.keyPathTextField.stringValue = predicate.leftExpression.keyPath; + [self.keyPathTextField sizeToFit]; + + BOOL isInPredicate = predicate.predicateOperatorType == NSInPredicateOperatorType; + _operatorPopUpButton.enabled = !isInPredicate; + _valueTextField.hidden = isInPredicate; + self.valueTokenField.hidden = !isInPredicate; + NSExpression *rightExpression = predicate.rightExpression; + if (isInPredicate) { + NSArray *values = [rightExpression.constantValue valueForKeyPath:@"constantValue"]; + self.valueTokenField.stringValue = [values componentsJoinedByString:@","]; + rightExpression = [NSExpression expressionForConstantValue:nil]; + } else { + self.valueTokenField.stringValue = @""; + } + + predicate = [NSComparisonPredicate predicateWithLeftExpression:self.leftExpressions.firstObject + rightExpression:rightExpression + modifier:predicate.comparisonPredicateModifier + type:predicate.predicateOperatorType + options:predicate.options]; + [super setPredicate:predicate]; +} + +@end diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index feef53062b..5c69dbe0c0 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -55,7 +55,8 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id = 0 || self.styleLayersTableView.selectedRow >= 0; } + if (menuItem.action == @selector(toggleLayerFilter:)) { + BOOL isShown = ![self.predicateSplitView isSubviewCollapsed:self.predicateSplitView.arrangedSubviews.lastObject]; + menuItem.title = isShown ? @"Hide Layer Filter" : @"Show Layer Filter"; + return YES; + } if (menuItem.action == @selector(setLabelLanguage:)) { menuItem.state = menuItem.tag == _isLocalizingLabels ? NSOnState: NSOffState; if (menuItem.tag) { @@ -1079,7 +1097,7 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id