Создайте подкласс NSPopUpButton
и переопределите события mouseDown
/ mouseUp
.
Имейте задержку события mouseDown
на мгновение перед вызовом реализации super
и только еслимышь все еще удерживается нажатой.
Для события mouseUp
установите selectedMenuItem
в nil
(и, следовательно, selectedMenuItemIndex
будет -1
), прежде чем нажать кнопку target
/ * 1016.*.
Единственная другая проблема заключается в обработке быстрых щелчков, когда таймер для одного щелчка может срабатывать в тот момент, когда мышь не работает в течение какого-то будущего щелчка.Вместо использования NSTimer
и аннулирования его, я решил использовать простой счетчик для mouseDown
событий и выручить, если счетчик изменился.
Вот код, который я использую в своем подклассе:
// MyClickAndHoldPopUpButton.h
@interface MyClickAndHoldPopUpButton : NSPopUpButton
@end
// MyClickAndHoldPopUpButton.m
@interface MyClickAndHoldPopUpButton ()
@property BOOL mouseIsDown;
@property BOOL menuWasShownForLastMouseDown;
@property int mouseDownUniquenessCounter;
@end
@implementation MyClickAndHoldPopUpButton
// highlight the button immediately but wait a moment before calling the super method (which will show our popup menu) if the mouse comes up
// in that moment, don't tell the super method about the mousedown at all.
- (void)mouseDown:(NSEvent *)theEvent
{
self.mouseIsDown = YES;
self.menuWasShownForLastMouseDown = NO;
self.mouseDownUniquenessCounter++;
int mouseDownUniquenessCounterCopy = self.mouseDownUniquenessCounter;
[self highlight:YES];
float delayInSeconds = [NSEvent doubleClickInterval];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if (self.mouseIsDown && mouseDownUniquenessCounterCopy == self.mouseDownUniquenessCounter) {
self.menuWasShownForLastMouseDown = YES;
[super mouseDown:theEvent];
}
});
}
// if the mouse was down for a short enough period to avoid showing a popup menu, fire our target/action with no selected menu item, then
// remove the button highlight.
- (void)mouseUp:(NSEvent *)theEvent
{
self.mouseIsDown = NO;
if (!self.menuWasShownForLastMouseDown) {
[self selectItem:nil];
[self sendAction:self.action to:self.target];
}
[self highlight:NO];
}
@end