响应右键单击的NSButton子类

时间:2011-04-28 00:46:04

标签: cocoa nsbutton

我有一个NSButton子类,我想用鼠标右键单击来完成工作。只是重载-rightMouseDown:不会削减它,因为我想要与常规点击相同的行为(例如按下按钮,用户可以通过离开按钮取消,当鼠标发送动作被释放等。)。

我到目前为止尝试的是重载-rightMouse{Down,Up,Dragged},更改事件以指示鼠标左键单击,然后将其发送到-mouse{Down,Up,Dragged}。现在这显然是一个充其量的黑客,因为事实证明Mac OS X并不喜欢这一切。我可以单击按钮,但在释放时,按钮仍然被按下。

我可以模仿自己的行为,这不应该太复杂。但是,我不知道如何按下按钮。


在你说“不要!这是一个非传统的Mac OS X行为并且应该避免”之前:我已经考虑过这一点,右键点击可以极大地改善工作流程。基本上按钮循环通过4个状态,我想要右键单击以使其反向循环。这不是一个必不可少的功能,但它会很好。如果你仍然想说“不要!”,那就让我知道你的想法吧。我很感激!

谢谢!

编辑:这是我尝试更改事件(你不能改变类型,所以我创建了一个新的,复制所有信息。我的意思是,我知道这是框架明确告诉我不要做这个,但我像你一样试了一下):

// I've contracted all three for brevity
- (void)rightMouse{Down,Up,Dragging}:(NSEvent *)theEvent {
    NSEvent *event = [NSEvent mouseEventWithType:NSLeftMouse{Down,Up,Dragging} location:[theEvent locationInWindow] modifierFlags:[theEvent modifierFlags] timestamp:[theEvent timestamp] windowNumber:[theEvent windowNumber] context:[theEvent context] eventNumber:[theEvent eventNumber] clickCount:[theEvent clickCount] pressure:[theEvent pressure]];
    [self mouse{Down,Up,Dragging}:event]; 
}

更新:我注意到-mouseUp:从未发送到NSButton,如果我将其更改为NSControl,那就是。我不知道为什么会这样,直到Francis McGrew指出它包含自己的事件处理循环。现在,这也解释了为什么在我重新路由-rightMouseDown:之前,但按钮不会在发布时上升。这是因为它本身正在获取新事件,我无法拦截并从右向左转换为鼠标按钮事件。

3 个答案:

答案 0 :(得分:6)

NSButton正在进入鼠标跟踪循环。要更改此项,您必须继承NSButton并创建自己的自定义跟踪循环。试试这段代码:

- (void) rightMouseDown:(NSEvent *)theEvent 
{
    NSEvent *newEvent = theEvent;
    BOOL mouseInBounds = NO;

    while (YES) 
        {
            mouseInBounds = NSPointInRect([newEvent locationInWindow], [self convertRect:[self frame] fromView:nil]);
            [self highlight:mouseInBounds];

            newEvent = [[self window] nextEventMatchingMask:NSRightMouseDraggedMask | NSRightMouseUpMask];

            if (NSRightMouseUp == [newEvent type])
            {
                break;
            }
        }
    if (mouseInBounds) [self performClick:nil];
}

我就是这样做的;希望它对你有用。

答案 1 :(得分:2)

我已经在路径控件上将鼠标左键单击并按住了一个假的右键。我不确定这会解决你所有的问题,但我发现当我这样做的关键区别在于改变时间戳:

NSEvent *event = [NSEvent mouseEventWithType:NSLeftMouseDown 
                                    location:[theEvent locationInWindow] 
                               modifierFlags:[theEvent modifierFlags] 
                                   timestamp:CFAbsoluteGetTimeCurrent()
                                windowNumber:[theEvent windowNumber] 
                                     context:[theEvent context]
 // I was surprised to find eventNumber didn't seem to need to be faked 
                                 eventNumber:[theEvent eventNumber]   
                                  clickCount:[theEvent clickCount] 
                                    pressure:[theEvent pressure]];

另一件事是,根据您的按钮类型,其state可能是使其显示为推或不显示的值,因此您可能会尝试戳它。

更新:我想我已经弄清楚为什么rightMouseUp:永远不会被调用。根据{{​​3}}文档,按钮会在获得mouseDown事件时开始跟踪鼠标,并且在获得mouseUp之前不会停止跟踪。在跟踪时,它无法做任何其他事情。我刚试过,例如,在自定义mouseDown:

的末尾
[self performSelector:@selector(mouseUp:) withObject:myFakeMouseUpEvent afterDelay:1.0];

但这会被推迟,直到通过其他方式触发正常mouseUp:。因此,如果您单击鼠标右键,则无法(使用鼠标)发送leftMouseUp,因此该按钮仍在跟踪,并且不会接受rightMouseUp事件。我仍然不知道解决方案是什么,但我认为这将是有用的信息。

答案 2 :(得分:1)

在上面的答案中添加的内容并不多,但对于那些在Swift中工作的人来说,您可能无法找到事件掩码的常量,深埋在文档中,并且更难找到一种方法来组合(或)它们以编译器接受的方式,这可以节省您一些时间。有更简洁的方式吗?这在您的子类中 -

var rightAction: Selector = nil 
  // add a new property, by analogy with action property

override func rightMouseDown(var theEvent: NSEvent!) {
  var newEvent: NSEvent!

  let maskUp = NSEventMask.RightMouseUpMask.rawValue
  let maskDragged = NSEventMask.RightMouseDraggedMask.rawValue
  let mask = Int( maskUp | maskDragged ) // cast from UInt

  do {
    newEvent = window!.nextEventMatchingMask(mask)
  }
  while newEvent.type == .RightMouseDragged

我的循环已成为一个do..while,因为它总是必须执行至少一次,我从不喜欢写while true,我不需要对拖动事件做任何事情。

我无法从convertRect()获得有意义的结果,可能是因为我的控件嵌入在表视图中。感谢上面的Gustav Larsson最后一部分 -

let whereUp = newEvent.locationInWindow
let p = convertPoint(whereUp, fromView: nil)
let mouseInBounds = NSMouseInRect(p, bounds, flipped) // bounds, not frame
if mouseInBounds {
  sendAction(rightAction, to: target)
  // assuming rightAction and target have been allocated
  }
}