views:

622

answers:

3

I'd like to customize the way I draw the window title bar on OS X. Specifically, I'd like to do something like the Twitterrific app where there is a custom close button, no min/max buttons, and the window title text is right-justified. Unlike Twitterrific, I'm not looking to custom draw the entire window (though I'm not completely opposed to that either).

I've already seen the RoundWindow sample on Cocoa With Love as well as the RoundTransparentWindow example Apple provides, but neither seems appropriate.

+1  A: 

cocoadev provides some more detail on how best to implement your own NSWindow subclass, complete with a description of most of the common pitfalls.

The gist of it is to create a subclass of NSWindow, and set its styleMask to NSBorderlessWindowMask in the init method:

- (id) initWithContentRect: (NSRect) contentRect
                 styleMask: (unsigned int) aStyle
                   backing: (NSBackingStoreType) bufferingType
                     defer: (BOOL) flag
{
    if ((self = [super initWithContentRect: contentRect
                                 styleMask: NSBorderlessWindowMask
                                   backing: bufferingType
                                     defer: flag]) == nil) { return nil; }

    [super setMovableByWindowBackground:YES];
    [super setLevel:NSNormalWindowLevel];
    [super setHasShadow:YES];
    // etc.

    return self;
}

Note that you should probably return YES for canbecomeKeyWindow in order to make your window behave like a normal window.

- (BOOL) canBecomeKeyWindow
{
    return YES;
}

You can then create a custom NSView subclass, fill the entire window with an instance of said class, and then perform all of the appropriate window drawing from within that custom view.

The whole thing can get a bit painful. You will have to re-implement most of the normal window behaviours such as resizing by dragging the bottom right corner.

e.James
Thanks, eJames. You're right that this approach (which I had started down previously) can get painful. I was hoping to avoid it since I'm not looking for complete customization and I don't want to have to implement all of the window's standard functionality. This approach is basically what is used in the sample's I referenced in my question. One strange aspect of this approach is that the window's shadow doesn't seem to work correctly. It's not as dark as the shadow on a standard window and it doesn't change when the focus changes. I think this might be a bug with the borderless window style.
sam
The shadow problem is bizarre. It sounds like Leibowitzn's answer might be the way to go for you. Good luck with it!
e.James
The shadow problem is a long standing issue. Apple doesn't draw a darker shadow for borderless windows that have keyboard focus. Take a look at Stickies for example. According to an Apple the recommended way to work around this is to use a normal window type and then do all your own drawing.
Leibowitzn
+3  A: 

If you don't want to use a borderless window class then you can do a couple of things.

First, you can customize the close/min/max buttons buy using -[NSWindow standardWindowButton:]. Once you get the button you can position it/remove it/etc...

You can customize the title by setting the title to @"". Then you can add a NSTextField to draw your own title by doing the following [[[NSWindow contentView] superview] addSubview:textField].

This is probably the easiest way to do things.

Another way to do this is to customize the view that draws all the window title bar, etc...

NSWindow's content view's is inside a "theme view". You can subclass the theme view and do your own drawing. The only problem is that the theme view is a private class so you'll have to be careful.

Leibowitzn
Thank you, Leibowitzn. Can you provide some more detail on how to set up the NSTextField? How do I set it's frame so that it is positioned properly in the window and in where in my code would be the best place to do this?
sam
FWIW, I did it in the app controller's awakeFromNib method. I positioned the textfield's frame relative to the window's frame. This probably isn't the best way to do it, but I used notifications to know when the window becomes/resigns the main window and used that to change the color of the text. Thanks again for the idea.
sam
A: 

There's an example of a custom window implementation in the CoreData Stickies sample project.

NSResponder