How do I simulate automatic Tab creation in a macOS window with the Option key held down?

86 views Asked by At

Default behavior: The option key forces a Tab

I have a document based macOS app, with a custom NSDocumentController. The app can handle two types of documents, each with their own windows and views.

Now, if the user has turned off the option to always create Tabs in windows, cmd-N will always create a fresh window.

And if the user types cmd-option-N, the framework automatically creates a Tab inside the front window regardless of the global or app's tabbing preference. This requires no extra programming on my part.

So far, so good.

Now, I have a secondary document type that gets created by cmd-shift-N. This creates, thanks to my custom DocumentController, a new window each time as well, as expected.

The challenge: Achieve the same with an additional modifier

I want the user to be able to type cmd-shift-option-N to create a new Tab inside an existing document window, just like cmd-option-N does for the primary document type.

However, it seems that the framework only handles a plain cmd-option-N for auto-creating Tabs. For other modifier combinations (like here, with shift), this doesn't appear to work: If I have set up a menu only for cmd-shift-N, then pressing cmd-shift-option-N will just beep. And if I add an explicit menu for cmd-shift-option-N, it creates a new window and not a Tab as desired.

So I need to emulate the behavior of turning the new document into a Tab instead of into a new Window. How do I do that, with the global tabbing more set to not create tabs?

1

There are 1 answers

2
Thomas Tempelmann On

By trial-and-error I came up with the following solution.

This requires a NSDocument subclass, let's call it 'CustomDocument`.

In it, I add a flag that controls whether I want to add the new window as a tab:

@interface CustomDocument : NSDocument
   @property BOOL foceIntoTab;
@end

Then I need to implement a custom handler for creating the window controller, in which I first determine if there's already a window to which I'd add the other one, then create the new controller, and finally add it as a tab:

- (void)makeWindowControllers // override
{
    __block NSWindow *frontWindow = nil;
    if (self.forceIntoTab) {
        // Find the topmost window that's of my document type
        [NSApp enumerateWindowsWithOptions:NSWindowListOrderedFrontToBack usingBlock:^(NSWindow *window, BOOL *stop) {
            if ([window.windowController.document isKindOfClass:CustomDocument.class]) {
                frontWindow = window;
                *stop = YES;
            }
        }];
    }

    NSWindowController *windowController = [[NSStoryboard storyboardWithName:@"Main" bundle:nil] instantiateControllerWithIdentifier:@"Custom Controller"];

    if (frontWindow) {
        [frontWindow.tabGroup addWindow:windowController.window];
        [windowController.window orderFront:frontWindow];
        [windowController.window makeKeyWindow];
    }

    NSViewController *viewController = windowController.contentViewController;
    [viewController setRepresentedObject:self];
    [self addWindowController:windowController];
}

What's left is to set forceIntoTab when creating a new window via the menu bar. I could do this, for instance, in my custom DocumentController class:

- (nullable __kindof NSDocument *)makeUntitledDocumentOfType:(NSString *)typeName error:(NSError **)outError // override
{
    CustomDocument *doc = [super makeUntitledDocumentOfType:typeName error:outError];
    doc.forceIntoTab = YES;
    return doc;
}