【问题标题】:NSButton subclass that responds to right clicks响应右键单击的 NSButton 子类
【发布时间】:2011-04-28 00:46:03
【问题描述】:

我有一个 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: 之前,但按钮不会在发布时升起。这是因为它自己获取新事件,我无法拦截并从鼠标右键事件转换为鼠标左键事件。

【问题讨论】:

  • 你能发布你用来改变事件的代码吗?我做过类似的事情,我会尝试挖掘我的。
  • @Josh Caswell 我添加了代码。谢谢!

标签: cocoa nsbutton


【解决方案1】:

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

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

【讨论】:

  • 是的!突然间一切都变得有意义了。我不得不更改点转换,但我最终转换了点,使用[self convertPoint:[newEvent locationInWindow] fromView:nil],然后将其与[self bound] 进行比较。我还在while循环之后添加了[self highlight:NO]。谢谢!
【解决方案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: 以其他方式触发。因此,如果您单击了鼠标右键,则不能(用鼠标)发送leftMouseUp,因此该按钮仍在跟踪,并且不会接受rightMouseUp 事件。我仍然不知道解决方案是什么,但我认为这将是有用的信息。

【讨论】:

  • 不幸的是,这对我不起作用。不过我正在进一步调查。
  • 顺便说一句,我发现让按钮看起来被按下是由-highlight: 完成的。现在我应该可以自己动手了,只要我弄清楚如何为 NSButton 拦截 -mouseUp:。感谢您的所有帮助!
  • @Gustav:是的,这很奇怪,rightMouseUp: 永远不会被调用。
  • @Gustav:添加了一些我刚刚发现的关于rightMouseUp: 的信息希望它有用!祝你好运!
  • 感谢您的出色调查工作!你肯定在它上面,但唯一的事情是它不是在等待-mouseUp:,而是在等待一个鼠标向上事件。所以我一直试图拦截-mouseUp:,没有运气。
【解决方案3】:

上面的答案没什么可补充的,但是对于那些使用 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
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-20
    • 1970-01-01
    • 2012-11-06
    • 1970-01-01
    • 2021-03-26
    • 2011-01-11
    • 2020-11-21
    相关资源
    最近更新 更多