[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:
committed by
Minh Nguyễn
parent
f542d7b2a7
commit
004d04075a
@@ -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
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user