[osx] Annotation cursors

Also added an option to osxapp for assigning a random cursor to all the annotations each time an annotation is added or removed.
This commit is contained in:
Minh Nguyễn
2015-12-01 15:34:15 -08:00
committed by Minh Nguyễn
parent f542d7b2a7
commit 004d04075a
4 changed files with 118 additions and 24 deletions

View File

@@ -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

View File

@@ -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];

View File

@@ -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>

View File

@@ -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 {