diff options
-rw-r--r-- | include/mbgl/osx/MGLAnnotationImage.h | 3 | ||||
-rw-r--r-- | platform/osx/app/AppDelegate.m | 44 | ||||
-rw-r--r-- | platform/osx/app/MainMenu.xib | 6 | ||||
-rw-r--r-- | platform/osx/sdk/MGLMapView.mm | 89 |
4 files changed, 118 insertions, 24 deletions
diff --git a/include/mbgl/osx/MGLAnnotationImage.h b/include/mbgl/osx/MGLAnnotationImage.h index e42e062022..66b623a5c1 100644 --- a/include/mbgl/osx/MGLAnnotationImage.h +++ b/include/mbgl/osx/MGLAnnotationImage.h @@ -12,6 +12,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) NSString *reuseIdentifier; @property (nonatomic, getter=isSelectable) BOOL selectable; +/** The cursor that appears above any annotation using this annotation image. By default this property is set to `nil`, representing the current cursor. */ +@property (nonatomic, nullable) NSCursor *cursor; + @end NS_ASSUME_NONNULL_END diff --git a/platform/osx/app/AppDelegate.m b/platform/osx/app/AppDelegate.m index 1b14aa4454..d788a0a271 100644 --- a/platform/osx/app/AppDelegate.m +++ b/platform/osx/app/AppDelegate.m @@ -7,6 +7,7 @@ #import <mbgl/osx/Mapbox.h> static NSString * const MGLMapboxAccessTokenDefaultsKey = @"MGLMapboxAccessToken"; +static NSString * const MGLDroppedPinAnnotationImageIdentifier = @"dropped"; @interface AppDelegate () <NSApplicationDelegate, NSSharingServicePickerDelegate, NSMenuDelegate, MGLMapViewDelegate> @@ -24,6 +25,7 @@ static NSString * const MGLMapboxAccessTokenDefaultsKey = @"MGLMapboxAccessToken NSNumberFormatter *_spellOutNumberFormatter; BOOL _showsToolTipsOnDroppedPins; + BOOL _randomizesCursorsOnDroppedPins; } #pragma mark Lifecycle @@ -232,6 +234,10 @@ static NSString * const MGLMapboxAccessTokenDefaultsKey = @"MGLMapboxAccessToken _showsToolTipsOnDroppedPins = !_showsToolTipsOnDroppedPins; } +- (IBAction)toggleRandomizesCursorsOnDroppedPins:(id)sender { + _randomizesCursorsOnDroppedPins = !_randomizesCursorsOnDroppedPins; +} + #pragma mark Help methods - (IBAction)showShortcuts:(id)sender { @@ -378,6 +384,11 @@ static NSString * const MGLMapboxAccessTokenDefaultsKey = @"MGLMapboxAccessToken menuItem.title = isShown ? @"Hide Tooltips on Dropped Pins" : @"Show Tooltips on Dropped Pins"; return YES; } + if (menuItem.action == @selector(toggleRandomizesCursorsOnDroppedPins:)) { + BOOL isRandom = _randomizesCursorsOnDroppedPins; + menuItem.title = isRandom ? @"Use Default Cursor for Dropped Pins" : @"Use Random Cursors for Dropped Pins"; + return _showsToolTipsOnDroppedPins; + } if (menuItem.action == @selector(showShortcuts:)) { return YES; } @@ -471,6 +482,37 @@ static NSString * const MGLMapboxAccessTokenDefaultsKey = @"MGLMapboxAccessToken return YES; } +- (MGLAnnotationImage *)mapView:(MGLMapView *)mapView imageForAnnotation:(id <MGLAnnotation>)annotation { + MGLAnnotationImage *annotationImage = [self.mapView dequeueReusableAnnotationImageWithIdentifier:MGLDroppedPinAnnotationImageIdentifier]; + if (!annotationImage) { + NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"default_marker" ofType:@"pdf"]; + NSImage *image = [[NSImage alloc] initWithContentsOfFile:imagePath]; + NSRect alignmentRect = image.alignmentRect; + alignmentRect.origin.y = NSMidY(alignmentRect); + alignmentRect.size.height /= 2; + image.alignmentRect = alignmentRect; + annotationImage = [MGLAnnotationImage annotationImageWithImage:image + reuseIdentifier:MGLDroppedPinAnnotationImageIdentifier]; + } + if (_randomizesCursorsOnDroppedPins) { + NSArray *cursors = @[ + [NSCursor IBeamCursor], + [NSCursor crosshairCursor], + [NSCursor pointingHandCursor], + [NSCursor disappearingItemCursor], + [NSCursor IBeamCursorForVerticalLayout], + [NSCursor operationNotAllowedCursor], + [NSCursor dragLinkCursor], + [NSCursor dragCopyCursor], + [NSCursor contextualMenuCursor], + ]; + annotationImage.cursor = cursors[arc4random_uniform(cursors.count)]; + } else { + annotationImage.cursor = nil; + } + return annotationImage; +} + - (void)mapView:(MGLMapView *)mapView didSelectAnnotation:(id <MGLAnnotation>)annotation { if ([annotation isKindOfClass:[DroppedPinAnnotation class]]) { DroppedPinAnnotation *droppedPin = annotation; @@ -478,7 +520,7 @@ static NSString * const MGLMapboxAccessTokenDefaultsKey = @"MGLMapboxAccessToken } } -- (void)mapView:(MGLMapView *)mapView didDeselectAnnotation:(id<MGLAnnotation>)annotation { +- (void)mapView:(MGLMapView *)mapView didDeselectAnnotation:(id <MGLAnnotation>)annotation { if ([annotation isKindOfClass:[DroppedPinAnnotation class]]) { DroppedPinAnnotation *droppedPin = annotation; [droppedPin pause]; diff --git a/platform/osx/app/MainMenu.xib b/platform/osx/app/MainMenu.xib index b7dfb3fa7b..bc476d2766 100644 --- a/platform/osx/app/MainMenu.xib +++ b/platform/osx/app/MainMenu.xib @@ -463,6 +463,12 @@ <action selector="toggleShowsToolTipsOnDroppedPins:" target="-1" id="1YC-Co-QQ6"/> </connections> </menuItem> + <menuItem title="Use Random Cursors for Dropped Pins" id="ZTk-lc-Jgu"> + <modifierMask key="keyEquivalentModifierMask"/> + <connections> + <action selector="toggleRandomizesCursorsOnDroppedPins:" target="-1" id="Mpw-b8-oub"/> + </connections> + </menuItem> </items> </menu> </menuItem> diff --git a/platform/osx/sdk/MGLMapView.mm b/platform/osx/sdk/MGLMapView.mm index 81d5edfdd0..5cee56cd7d 100644 --- a/platform/osx/sdk/MGLMapView.mm +++ b/platform/osx/sdk/MGLMapView.mm @@ -111,6 +111,7 @@ public: std::shared_ptr<mbgl::SQLiteCache> _mbglFileCache; mbgl::DefaultFileSource *_mbglFileSource; + NSPanGestureRecognizer *_panGestureRecognizer; NSMagnificationGestureRecognizer *_magnificationGestureRecognizer; NSRotationGestureRecognizer *_rotationGestureRecognizer; double _scaleAtBeginningOfGesture; @@ -122,6 +123,8 @@ public: MGLAnnotationID _lastSelectedAnnotationID; NSSize _unionedAnnotationImageSize; std::vector<MGLAnnotationID> _annotationsNearbyLastClick; + BOOL _wantsToolTipRects; + BOOL _wantsCursorRects; BOOL _delegateHasAlphasForShapeAnnotations; BOOL _delegateHasStrokeColorsForShapeAnnotations; BOOL _delegateHasFillColorsForShapeAnnotations; @@ -294,9 +297,9 @@ public: _rotateEnabled = YES; _pitchEnabled = YES; - NSPanGestureRecognizer *panGestureRecognizer = [[NSPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)]; - panGestureRecognizer.delaysKeyEvents = YES; - [self addGestureRecognizer:panGestureRecognizer]; + _panGestureRecognizer = [[NSPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)]; + _panGestureRecognizer.delaysKeyEvents = YES; + [self addGestureRecognizer:_panGestureRecognizer]; NSClickGestureRecognizer *clickGestureRecognizer = [[NSClickGestureRecognizer alloc] initWithTarget:self action:@selector(handleClickGesture:)]; clickGestureRecognizer.delaysPrimaryMouseButtonEvents = NO; @@ -591,7 +594,7 @@ public: [self updateZoomControls]; [self updateCompass]; [self updateAnnotationCallouts]; - [self updateToolTips]; + [self updateAnnotationTrackingAreas]; if ([self.delegate respondsToSelector:@selector(mapView:regionDidChangeAnimated:)]) { BOOL animated = change == mbgl::MapChangeRegionDidChangeAnimated; @@ -830,7 +833,7 @@ public: _mbglMap->cancelTransitions(); if (gestureRecognizer.state == NSGestureRecognizerStateBegan) { - [[NSCursor closedHandCursor] push]; + [self.window invalidateCursorRectsForView:self]; _mbglMap->setGestureInProgress(true); } else if (gestureRecognizer.state == NSGestureRecognizerStateChanged) { delta.y *= -1; @@ -839,7 +842,7 @@ public: } else if (gestureRecognizer.state == NSGestureRecognizerStateEnded || gestureRecognizer.state == NSGestureRecognizerStateCancelled) { _mbglMap->setGestureInProgress(false); - [[NSCursor arrowCursor] pop]; + [self.window invalidateCursorRectsForView:self]; } } } @@ -1128,6 +1131,10 @@ public: NSString *symbolName = [MGLAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier]; points.emplace_back(MGLLatLngFromLocationCoordinate2D(annotation.coordinate), symbolName ? [symbolName UTF8String] : ""); + + if (annotation.toolTip.length) { + _wantsToolTipRects = YES; + } } } @@ -1152,7 +1159,7 @@ public: } } - [self updateToolTips]; + [self updateAnnotationTrackingAreas]; } - (void)installAnnotationImage:(MGLAnnotationImage *)annotationImage { @@ -1180,6 +1187,10 @@ public: // Union this slop area with any existing slop areas. _unionedAnnotationImageSize = NSMakeSize(MAX(_unionedAnnotationImageSize.width, size.width), MAX(_unionedAnnotationImageSize.height, size.height)); + + if (annotationImage.cursor) { + _wantsCursorRects = YES; + } } - (void)removeAnnotation:(id <MGLAnnotation>)annotation { @@ -1216,7 +1227,7 @@ public: _mbglMap->removeAnnotations(annotationIDsToRemove); - [self updateToolTips]; + [self updateAnnotationTrackingAreas]; } - (id <MGLAnnotation>)selectedAnnotation { @@ -1518,25 +1529,33 @@ public: [self removeAnnotations:overlays]; } -#pragma mark Tooltips +#pragma mark Tooltips and cursors -- (void)updateToolTips { - [self removeAllToolTips]; - - std::vector<MGLAnnotationID> annotationIDs = [self annotationIDsInRect:self.bounds]; - for (MGLAnnotationID annotationID : annotationIDs) { - MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithID:annotationID]; - id <MGLAnnotation> annotation = [self annotationWithID:annotationID]; - if (annotation.toolTip.length) { - NSImage *image = annotationImage.image; - NSRect annotationRect = [self frameOfImage:image - centeredAtCoordinate:annotation.coordinate]; - annotationRect = NSOffsetRect(image.alignmentRect, annotationRect.origin.x, annotationRect.origin.y); - if (!NSIsEmptyRect(annotationRect)) { - [self addToolTipRect:annotationRect owner:self userData:(void *)(NSUInteger)annotationID]; +- (void)updateAnnotationTrackingAreas { + if (_wantsToolTipRects) { + [self removeAllToolTips]; + std::vector<MGLAnnotationID> annotationIDs = [self annotationIDsInRect:self.bounds]; + for (MGLAnnotationID annotationID : annotationIDs) { + MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithID:annotationID]; + id <MGLAnnotation> annotation = [self annotationWithID:annotationID]; + if (annotation.toolTip.length) { + NSImage *image = annotationImage.image; + NSRect annotationRect = [self frameOfImage:image + centeredAtCoordinate:annotation.coordinate]; + annotationRect = NSOffsetRect(image.alignmentRect, annotationRect.origin.x, annotationRect.origin.y); + if (!NSIsEmptyRect(annotationRect)) { + [self addToolTipRect:annotationRect owner:self userData:(void *)(NSUInteger)annotationID]; + } + } + if (annotationImage.cursor) { + _wantsCursorRects = YES; } } } + + if (_wantsCursorRects) { + [self.window invalidateCursorRectsForView:self]; + } } - (NSString *)view:(__unused NSView *)view stringForToolTip:(__unused NSToolTipTag)tag point:(__unused NSPoint)point userData:(void *)data { @@ -1548,6 +1567,30 @@ public: return annotation.toolTip; } +- (void)resetCursorRects { + if (_panGestureRecognizer.state == NSGestureRecognizerStateBegan + || _panGestureRecognizer.state == NSGestureRecognizerStateChanged) { + [self addCursorRect:self.bounds cursor:[NSCursor closedHandCursor]]; + return; + } + if (!_wantsCursorRects) { + return; + } + + std::vector<MGLAnnotationID> annotationIDs = [self annotationIDsInRect:self.bounds]; + for (MGLAnnotationID annotationID : annotationIDs) { + id <MGLAnnotation> annotation = [self annotationWithID:annotationID]; + MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithID:annotationID]; + if (annotationImage.cursor) { + NSImage *image = annotationImage.image; + NSRect annotationRect = [self frameOfImage:image + centeredAtCoordinate:annotation.coordinate]; + annotationRect = NSOffsetRect(image.alignmentRect, annotationRect.origin.x, annotationRect.origin.y); + [self addCursorRect:annotationRect cursor:annotationImage.cursor]; + } + } +} + #pragma mark Interface Builder methods - (void)prepareForInterfaceBuilder { |