NSWindow - 点击外部隐藏窗口

3

我正在进行Mac编程的初步学习。我只有一点iOS开发经验。

我需要构建一个非常简单的应用程序,它坐落在菜单栏中。

我想让它稍微自定义一些,所以决定使用NSWindow并将其附加到NSStatusItem。

我的AppDelegate如下:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Insert code here to initialize your application

    float width = 30.0;
    float height = [[NSStatusBar systemStatusBar] thickness];
    NSRect viewFrame = NSMakeRect(0, 0, width, height);
    statusItem = [[NSStatusItem alloc] init];
    statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:30];
    [statusItem setView:[[TSStatusBarItem alloc] initWithFrame:viewFrame]];


}

- (void)buttonClicked:(int)posx posy:(int)posy {
    [_window setLevel:kCGMaximumWindowLevelKey];
    opened = !opened;
    NSLog(@"Window is shown: %i", opened);

    [_window setFrameTopLeftPoint:NSMakePoint(posx, posy)];

    if(opened == YES) {
        _window.isVisible = YES;
    } else {
        _window.isVisible = NO;
    }

}

这是 TSStatusBarItem 的代码。
- (void)drawRect:(NSRect)rect
{
    // Drawing code here.

    if (clicked) {
        [[NSColor selectedMenuItemColor] set];
        NSRectFill(rect);
    }

    NSImageView *subview = [[NSImageView alloc] initWithFrame:CGRectMake(3, 0, 20, 20)];
    [subview setImage:[NSImage imageNamed:@"icon.png"]];
    [self addSubview:subview];


}

- (void)mouseDown:(NSEvent *)event
{

    NSRect frame = [[self window]frame];
    NSPoint pt = NSMakePoint(NSMinX(frame), NSMinY(frame));
    NSLog(@"X: %f and Y: %f", pt.x, pt.y);

    [self setNeedsDisplay:YES];
    clicked = !clicked;

    [appDelegate buttonClicked:pt.x posy:pt.y];


}

窗口的显示和隐藏还不错,但只有当我点击StatusItem时才有效。我想添加当用户在菜单栏中选择其他项目或在窗口外部单击时隐藏窗口的行为(与典型的NSMenu应用程序类似)。

怎么做呢?如果您有任何简化我的代码的想法(抱歉,我是Mac编程的新手),请告诉我。


1
为什么每次调用draw rect时都要添加一个新的图像视图?我建议将代码的那部分添加到init方法中,或者以编程方式绘制图像。 ;) - Tanner Silva
另外,当状态项被点击时,您是否有NSMenu出现? - Tanner Silva
@TannerSilva 不是的。我只是“打开”NSWindow并将其移动到NSStatusItem的位置。 - Konrad Kolasa
好的。我在想你可以将你的AppDelegate设置为NSMenuDelegate,这样当状态菜单关闭时就会通知你。 - Tanner Silva
3个回答

4

注册NSWindowDidResignKeyNotificationNSWindowDidResignMainNotification以在窗口失去焦点时获得通知:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    SEL theSelector = @selector(closeWindow); 
    NSNotificationCenter* theCenter = [NSNotificationCenter defaultCenter]; 
    NSWindow* theWindow = [self window]; 
    [theCenter addObserver:self selector:theSelector name:NSWindowDidResignKeyNotification object:theWindow]; 
    [theCenter addObserver:self selector:theSelector name:NSWindowDidResignMainNotification object:theWindow]; 
}

如果窗口失去焦点,将执行以下操作:

-(void)closeWindow
{
    [[self window] close];
}

或者使用 NSPanel,如果失去焦点,它会自动隐藏。


谢谢回答!嗯...看起来它对于在外面点击没有反应。我甚至尝试了调用NSLog,但是徒劳无功。还尝试了NSWindowDidBecomeKeyNotification和NSWindowDidBecomeMainNotification,也没用。:( - Konrad Kolasa
只有在启用标题栏时才能正常工作。同样,当启用标题栏时,WebKit可以正常工作,但如果没有启用,则会出现奇怪的问题。;/ - Konrad Kolasa
非常感谢。这是一个快速的帮助。 - Sid
似乎当标题栏被禁用时,窗口永远不会成为关键窗口。为了解决这个问题,需要子类化NSWindow并重写canBecomeKey属性以返回true。然后在NSWindowDelegate中重写windowDidResignKey方法。 - drootang

1

@Anne 的答案的 Swift 5 版本:

NotificationCenter.default.addObserver(forName: NSWindow.didResignKeyNotification, object: nil, queue: OperationQueue.main) { _ in
    self.window?.close()
}

NotificationCenter.default.addObserver(forName: NSWindow.didResignMainNotification, object: nil, queue: OperationQueue.main) { _ in
    self.window?.close()
}

1
-(void)applicationDidResignActive:(NSNotification *)notification
{
    [self window] close];
}

试一试 这只有在您的应用程序处于焦点状态时才有效。 为了使其聚焦……也试试这个:

[NSApp activateIgnoringOtherApps:YES];


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接