点击按钮,而不是提升窗口

时间:2009-09-30 14:48:05

标签: objective-c cocoa nsapplication nsevent

iTunes迷你播放器(仅举一个例子)支持点击,当使用播放/暂停和音量控制时,应用程序不会显示在前面。

这是怎么做到的?

我一直在查看Apple的文档,并在Cocoa Event-Handling Guide, Event Dispatch中指出了一些内容:

  

某些事件,其中许多是由Application Kit(类型NSAppKitDefined)定义的,与窗口或应用程序对象本身控制的操作有关。这些事件的示例是与激活,停用,隐藏和显示应用程序相关的事件。 NSApp在其调度例程的早期过滤掉这些事件并自行处理它们。

所以,从我有限的理解(How an Event Enters a Cocoa, Application)继承NSApplication和覆盖 - (void)sendEvent:(NSEvent *)theEvent应该捕获每个鼠标和键盘事件,但仍然会在单击时引发窗口。因此,在NSApplication看到事件之前窗口会被提升,或者我错过了其他内容。

我看过Matt Gallagher的Demystifying NSApplication by recreating it,不幸的是Matt没有报道事件队列,所以除此之外,我很难过。

任何帮助都将不胜感激,谢谢。

已编辑添加:Lloyd's Lounge找到了一个帖子,其中他谈到同一问题并链接到CocoaBuilder, capture first right mouse down的帖子。我正在尝试在那里提供的代码,在一些摆弄并重新激活[theEvent类型]的NSLog后,鼠标左键活动被捕获。

现在,左键单击窗口将其向前移动会生成一系列事件类型13, 1, 13,这些类型又是NSAppKitDefined,NSLeftMouseDown和NSAppKitDefined。我可以过滤掉它们或找到它们的去向吗?

4 个答案:

答案 0 :(得分:3)

这可以通过NSPanel来完成,该YES被设置为在停用时不隐藏,并且仅在需要时成为关键。这也假定您将使用的控件将-acceptsFirstMouse:返回NSButton; -setBecomesKeyOnlyIfNeeded:YES默认情况下会这样做。

您可以通过IB为面板关闭隐藏在停用状态标记,但是您需要向面板发出{{1}}消息,以便在按钮点击时保持该应用程序不会出现。

答案 1 :(得分:2)

这不是我想要的答案,但是现在它的效果非常好。 通过继承NSView并实现以下方法,该视图可以充当按钮。

- (void)mouseDown:(NSEvent *)theEvent {
    [NSApp preventWindowOrdering];
//  [self setNeedsDisplay:YES];
}

- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent {
    return YES;
}

- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)theEvent {
    return YES;
}

不需要其他任何东西,就是这样。当然这绕过了我想要保留的所有NSButton优点,我必须设计然后处理绘制和更新按钮状态,但我很高兴得到答案。

答案 2 :(得分:2)

当应用程序处于活动状态时,可以使NSButton响应点击事件而不改变其行为:

接口(ClickThroughButton.h):

#import <AppKit/AppKit.h>

@interface ClickThroughButton : NSButton

@end

实施(ClickThroughButton.m):

#import "ClickThroughButton.h"

@implementation ClickThroughButton

- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)theEvent {
    return ![NSApp isActive];    
}

- (void)mouseDown:(NSEvent *)theEvent {    
    if (![NSApp isActive]) {
        [NSApp preventWindowOrdering];        

        [self highlight:YES];

        NSEvent *mouseUpEvent = [[self window] nextEventMatchingMask:NSLeftMouseUpMask
                                                  untilDate:[NSDate distantFuture]
                                                     inMode:NSEventTrackingRunLoopMode
                                                    dequeue:YES];
        NSPoint mouseLocation = [self convertPoint:[mouseUpEvent locationInWindow] fromView:nil];
        BOOL mouseUpInside = [self mouse:mouseLocation inRect:[self bounds]];

        if (mouseUpInside) {
            if ([self target])
                [[self target] performSelector:[self action] withObject:self]; 
        }

        [self highlight:NO];            

    } else {
        [super mouseDown:theEvent];        
    }
}

@end

答案 3 :(得分:1)

你签出了acceptsFirstMouse:吗?任何NSView都可以向此事件返回YES,表示视图应该处理事件而不是将窗口置于前台。据推测,通过拦截事件,它不会被用来将窗口带到前台,但我无法保证这一点。

您显然需要将要使此行为的任何控件子类化以覆盖acceptsFirstMouse:

相关问题