5

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

到目前为止,我尝试的是重载-rightMouse{Down,Up,Dragged},更改事件以指示鼠标左键单击,然后将其发送到-mouse{Down,Up,Dragged}. 现在这显然充其量只是一个 hack,而且事实证明 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:,但按钮不会在释放时上升。这是因为它自己获取新事件,我无法拦截并从鼠标右键事件转换为鼠标左键事件。

4

3 回答 3

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

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

于 2011-04-28T13:50:56.077 回答
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:永远不会被调用。根据-[NSControl mouseDown:]文档,按钮在收到事件时开始跟踪鼠标,并且在收到mouseDown事件之前不会停止跟踪mouseUp。当它在跟踪时,它不能做任何其他事情。例如,我刚刚尝试在自定义结束时mouseDown:

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

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

于 2011-04-28T02:19:48.687 回答
1

上面的答案没什么可补充的,但是对于那些使用 Swift 的人来说,您可能很难找到事件掩码的常量,这些常量深埋在文档中,而且更难找到一种方法以某种方式组合 (OR) 它们编译器接受,因此这可能会为您节省一些时间。有没有更简洁的方法?这在您的子类中-

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
  }
}
于 2014-11-10T23:03:33.200 回答