J'' J'' - 5 months ago 25
Swift Question

NSWindow not closing when created with custom NSApplication

I'm creating my own subclass of NSApplication, and am running into a roadblock. Here is my implementation of the

run()
method.

override func run() {
finishLaunching()
repeat {

let event = nextEventMatchingMask(0xfffffffffffffff, untilDate: NSDate.distantPast(), inMode: NSDefaultRunLoopMode, dequeue: true)
if event != nil { sendEvent(event!) }
updateWindows()

} while true

}


In my
main.swift
I have this:

let myApp: MyApplication = MyApplication.sharedApplication() as! MyApplication

let window = NSWindow(contentRect: NSMakeRect(0, 0, 100, 100), styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask , backing: .Buffered, defer: false)
window.makeKeyAndOrderFront(nil)

myApp.run()


The reason why I have
0xfffffffffffffff
instead of
Int(NSEventMask.AnyEventMask.rawValue)
is because the latter overflows when converted from
UInt64
to
Int
.

The problem is, when I click on the red close button, the window does not close, and when I select "Quit" from the dock icon menu, it does not quit. Why does this happen?

EDIT: The same problem is discussed in this answer.

EDIT 2: I've created an objective-c version of this code, and everything works fine. I suspect the problem is that I can't use NSAnyEventMask in Swift.

Answer

I seem to have solved the problem, In Objective-C I can just use this, and my app will respond to the quit item in the dock menu.

NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask 
                                    untilDate:nil 
                                       inMode:NSDefaultRunLoopMode 
                                      dequeue:YES];

In Swift if you try to get the next event like that you would do this:

let event = nextEventMatchingMask(Int(NSEventMask.AnyEventMask.rawValue),
                                  untilDate: NSDate.distantPast(), 
                                  inMode: NSDefaultRunLoopMode, 
                                  dequeue: true)

However, you get an overflow error when converting from UInt64 to Int. This seems to be unintended. At first, I tried to resolve this problem by replacing it with 0xfffffffffffffff. This worked fine, and the app would respond to events. But this was not enough, actually. The app needs to also respond to events matching a mask 0x1. I have no idea why, but this allows me to quit and hide my app from the dock menu. (0x0 only allows me to quit.)

So then, the entire run() implementation of a Swift NSApplication subclass is this:

override func run() {
    finishLaunching()
    setValue(true, forKey: "running")

    while true {
        let event = nextEventMatchingMask(0xfffffffffffffff, untilDate: NSDate.distantPast(), inMode: NSDefaultRunLoopMode, dequeue: true)
        let dockEvent = nextEventMatchingMask(0x1, untilDate: NSDate.distantPast(), inMode: NSDefaultRunLoopMode, dequeue: true)

        if dockEvent != nil { sendEvent(dockEvent!) }
        if event != nil { sendEvent(event!) }

        if !running { break }

        updateWindows()
    }
}