orion
orion

Reputation: 408

Differentiate between NSWindow close and Multiple-Tab-NSWindow close?

I'm trying to make my application call hide when the user attempts to close the last window via the red button.

Note: The tabs mentioned below are part of the sierra automatic tabbing feature.

I'm aware that I can hide the application with NSApplication.shared().hide(), however, I only want to do this when the user has tried to close the last open window (meaning, the red button that would close ALL tabs for that window). However, I want to permit the close buttons on the tabs to perform normally, and close the tab.

So far a tab closure and a window closure appear identical in the API and I'm having a hard time achieving the behavior I want. Is there a way to determine if the is being closed via its red close button, or its tab close button?

Upvotes: 0

Views: 901

Answers (1)

mike wyatt
mike wyatt

Reputation: 1420

Disclaimer:

  • I've only been working with Swift/Cocoa for a few weeks.
  • There's probably a better way of doing this.

So I could not find an easy way of doing this. The idea behind my implementation is to call a debounced function when a window closes. Since clicking the "X" in the corner closes all windows, the debouncing will allow you to have a function called once when it's all done.

There's some other stuff too: You've got to cache the number of windows in a tab. You've got to keep a list of the windows being closed.

But in the end, you can differentiate between:

  • Closing multiple tabs in a window ("Close other tabs")
  • Closing a single tab in a window
  • Closing a whole window of tabs
  • Closing a window without tabs

Anyways, here's my implementation. You'll need debounce from here:

// Stands for TabbedWindowCloseDebouncer
class TWCDebouncer {
    private static var Debouncers = [NSWindowTabGroup: () -> Void]()
    private static var TabStartCounts = [NSWindowTabGroup: Int]()
    private static var Windows = [NSWindowTabGroup: [NSWindow]]()
    private static var LastTabGroup: NSWindowTabGroup?

    func handleClose(window: NSWindow) {
        // This handles a window without tabs.
        // Check presence in Debouncers, otherwise it will also catch the last
        // window of a tabbed window closing.
        if window.tabbedWindows == nil && TWCDebouncer.Debouncers[window.tabGroup!] == nil {
            // You can consider this to be the same as closing a whole window.
            return
        }

        // This could probably lead to problems.
        TWCDebouncer.LastTabGroup = window.tabGroup

        // Store the initial tab count.
        if TWCDebouncer.TabStartCounts[TWCDebouncer.LastTabGroup!] == nil {
            TWCDebouncer.TabStartCounts[TWCDebouncer.LastTabGroup!] = window.tabbedWindows?.count
        }

        // Initialize the list of windows closing.
        if TWCDebouncer.Windows[TWCDebouncer.LastTabGroup!] == nil {
            TWCDebouncer.Windows[TWCDebouncer.LastTabGroup!] = []
        }

        // Set up the debounced function.
        if TWCDebouncer.Debouncers[TWCDebouncer.LastTabGroup!] == nil {
            TWCDebouncer.Debouncers[TWCDebouncer.LastTabGroup!] = debounce(delay: .milliseconds(20), action: {
                let countAfter = TWCDebouncer.LastTabGroup?.windows.count ?? 0

                print(TWCDebouncer.Windows[TWCDebouncer.LastTabGroup!])
                if countAfter == 0 {
                    // All windows were closed.
                } else {
                    // One or more windows were closed in the tab group
                }

                // Reset.
                TWCDebouncer.Debouncers[TWCDebouncer.LastTabGroup!] = nil
                TWCDebouncer.TabStartCounts[TWCDebouncer.LastTabGroup!] = nil
                TWCDebouncer.Windows[TWCDebouncer.LastTabGroup!] = nil
                TWCDebouncer.LastTabGroup = nil
            })
        }

        // Store the window.
        TWCDebouncer.Windows[TWCDebouncer.LastTabGroup!]?.append(window)
        // Call the debounced function.
        TWCDebouncer.Debouncers[window.tabGroup!]!()
    }
}

To use it, I've put it on my WindowController. I had to put it in windowShouldClose, by the time windowWillClose is called, window.tabbedWindows is nil.

class WindowController: NSWindowController {
    var closeDebouncer = TWCDebouncer()

    func windowShouldClose(_ sender: NSWindow) -> Bool {
        self.closeDebouncer.handleClose(window: self.window!)
        return true
    }
}

Upvotes: 0

Related Questions