appkitnsdocumentnswindowcontroller

Cascading in custom NSWindowController


I have a document-based application. I override NSDocument's makeWindowControllers to instantiate a custom window controller. Its initializer calls init(window: NSWindow?) of its superclass, i.e. it does not use any of the initializers that involve a nib file.

How do I make cascading work (see shouldCascadeWindows)? At the moment each window is opening in the same position on the 1st screen.

Can I somehow reuse the existing cascading logic, maybe by calling something on NSWindowController?

If I have to implement it myself manually, how should I best get the position of the top-most document window? And which of the potentially many windows of a document should be the window from which to compute the offset?


Solution

  • func cascadeTopLeft(from topLeftPoint: NSPoint) -> NSPoint

    Positions the window's top left to a given point.

    Parameters

    topLeftPoint The new top-left point, in screen coordinates, for the window. When NSZeroPoint, the window is not moved, except as needed to constrain to the visible screen

    NSZeroPoint is the starting point for the first window.

    Return Value The point shifted from top left of the window in screen coordinates.

    Discussion

    The returned point can be passed to a subsequent invocation of cascadeTopLeft(from:) to position the next window so the title bars of both windows are fully visible.

    The returned point is the starting point for the next window.

    Example (like TextEdit):

    static var cascadingPoint = NSZeroPoint
    
    override func makeWindowControllers() {
        let window = NSWindow(contentRect: NSMakeRect(100, 100, 500, 500), styleMask: .titled, backing: .buffered, defer: true)
        Document.cascadingPoint = window.cascadeTopLeft(from: Document.cascadingPoint)
        let windowController = NSWindowController(window: window)
        addWindowController(windowController)
    }
    

    Another example (like Safari)

    override func makeWindowControllers() {
        let window = NSWindow(contentRect: NSMakeRect(100, 100, 500, 500), styleMask: .titled, backing: .buffered, defer: true)
        let cascadingPoint = NSApp.mainWindow?.cascadeTopLeft(from: NSZeroPoint) ?? NSZeroPoint
        window.cascadeTopLeft(from: cascadingPoint)
        let windowController = NSWindowController(window: window)
        addWindowController(windowController)
    }