2012-02-08 16 views
7

我想創建一個NSButton發送被點擊時它的動作,但是當它被按下1兩秒它示出了NSMenu。與此問題here完全相同,但由於該答案無法解決我的問題,所以我決定再次提問。NSButton遲發NSMenu - 目標C /可可

舉個例子,去查找,打開一個新的窗口,通過一些瀏覽文件夾,然後單擊後退按鈕:你去到一個文件夾。現在點擊並按住後退按鈕:顯示一個菜單。我不知道如何用NSPopUpButton來做到這一點。

回答

8

使用NSSegmentedControl

通過發送setMenu:forSegment:的控制(任何連接到menu出口在IB不會做的伎倆)添加菜單。有一個行動連接到控制(這很重要)。

應完全按照您所描述的方式工作。

+1

太糟糕了,您無法爲NSSegmentedControl設置自定義高度 - 我需要將該菜單附加到大按鈕上。 – zrxq 2012-03-20 04:47:45

+0

工作完美!謝謝! – Alex 2012-03-28 10:01:04

+0

不錯的訣竅,在IB玩一玩就可以得到像這樣的優雅控制。 – 2012-05-08 15:16:45

5

創建NSPopUpButton的子類並覆蓋mouseDown/mouseUp事件。

在調用super的實現並且只有在鼠標仍在按住之前,纔有mouseDown事件延遲片刻。

mouseUp事件觸發按鈕的target/action前將selectedMenuItemnil(因此selectedMenuItemIndex-1)。

唯一的另一個問題是處理快速點擊,其中對於點擊定時器可能在瞬間火的時候,按下鼠標時對一些未來的點擊。我沒有使用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 
+1

美麗!這正是我所期待的。太糟糕了,在App Kit中沒有這樣的標準控件(這很奇怪,因爲Apple在其自己的應用程序中使用了這種UI慣例)。 – aapierce 2014-03-12 19:24:42

+1

對於'delayInSeconds'考慮使用'NSEvent。doubleClickInterval'而不是常量'0.2'。這將根據用戶的鼠標處理偏好來調整延遲。對於雙擊時間短的用戶,延遲時間更短,延遲時間更短,對於雙擊時間更長的用戶,延遲時間更短,延遲時間更短。 – 2015-07-13 10:20:37

+1

謝謝@GrahamMiln,我已經更新了我的答案。 – 2015-07-13 11:24:15

1

如果有人仍然需要這一點,這裏是基於一個普通NSButton,不分段控制我的解決方案。

子類NSButton和實施開始當前運行環內的計時器自定義mouseDown。在mouseUp中,檢查定時器是否未被觸發。在這種情況下,取消它並執行默認操作。

這是一個非常簡單的方法,它適用於任何NSButton您可以在IB使用。下面

代碼:

- (void)mouseDown:(NSEvent *)theEvent { 
    [self setHighlighted:YES]; 
    [self setNeedsDisplay:YES]; 

    _menuShown = NO; 
    _timer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(showContextMenu:) userInfo:nil repeats:NO]; 

    [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode]; 
} 

- (void)mouseUp:(NSEvent *)theEvent { 
    [self setHighlighted:NO]; 
    [self setNeedsDisplay:YES]; 

    [_timer invalidate]; 
    _timer = nil; 

    if(!_menuShown) { 
     [NSApp sendAction:[self action] to:[self target] from:self]; 
    } 

    _menuShown = NO; 
} 

- (void)showContextMenu:(NSTimer*)timer { 
    if(!_timer) { 
     return; 
    } 

    _timer = nil; 
    _menuShown = YES; 

    NSMenu *theMenu = [[NSMenu alloc] initWithTitle:@"Contextual Menu"]; 

    [[theMenu addItemWithTitle:@"Beep" action:@selector(beep:) keyEquivalent:@""] setTarget:self]; 
    [[theMenu addItemWithTitle:@"Honk" action:@selector(honk:) keyEquivalent:@""] setTarget:self]; 

    [theMenu popUpMenuPositioningItem:nil atLocation:NSMakePoint(self.bounds.size.width-8, self.bounds.size.height-1) inView:self]; 

    NSWindow* window = [self window]; 

    NSEvent* fakeMouseUp = [NSEvent mouseEventWithType:NSLeftMouseUp 
               location:self.bounds.origin 
             modifierFlags:0 
              timestamp:[NSDate timeIntervalSinceReferenceDate] 
              windowNumber:[window windowNumber] 
               context:[NSGraphicsContext currentContext] 
              eventNumber:0 
              clickCount:1 
               pressure:0.0]; 

    [window postEvent:fakeMouseUp atStart:YES]; 

    [self setState:NSOnState]; 
} 

我已經貼在我的GitHub上一個working sample

相關問題