How to change the height of an NSWindow titlebar? - cocoa

I want to change the height of an NSWindow titlebar.
Here are some examples:
And…
I could use an NSToolbar, but the problem is that I can't place views very height (For example: I can't place the segmentedControl higher than in the picture because there is still the titlebar)
If I remove the titlebar I can't place a NSToolbar and the window isn't movable.
Have you any ideas?

This is much easier than one would think. I too went on a quest to do something similar for my app.
Real App Store app:
My App Store app look-alike:
No disrespect to INAppStoreWindow, it is a very good implementation and solid. The only draw back I saw from it though was that there was a lot of drawing code along with hardcoded settings for the TitleBar colors which Apple can adjust at anytime.
So here is how I did it:
A) Create a standard window with a Title Bar, Close, Minimize, Shadow, Resize, Full Screen - Primary Window all set.
Note: You do not need a textured window nor should you set a title
B) Next add a standard toolbar with these settings:
Icon Only
Visible at Launch - ON
Customizable - OFF
Separator - ON
Size - Regular
Remove all the Toolbar Items and add only these in the following order
NSSegmentControl (51 x 24) -- | Flexible Space | -- NSSearchField (150 x 25)
C) In your content View directly under the toolbar add a regular sized NSButton set like so:
Bordered - OFF
Transparent - OFF
Title -
Image -
Position - Text below the button
Font - System Small 11
Ok, pretty easy so far, right?!
In your Window Controller or app delegate....
setup IBOutlet(s) to your NSButton(s)
Note: Remember to hook up your IBOutlet in interface builder
Ok don't be scared we have to write a tiny bit of code now:
In awakeFromNib or windowDidLoad....
Get the content views' superview (aka NSThemeView)
Remove your button from its superView
Set the frame of your button
Add the button back to the theme view
So the code would look similar to this:
NSView *themeView = [self.contentView superview];
NSUInteger adj = 6;
[self.btnFeatured removeFromSuperview];
self.btnFeatured.frame = NSMakeRect( self.btnFeatured.frame.origin.x,
self.window.frame.size.height - self.btnFeatured.frame.size.height - adj,
self.btnFeatured.frame.size.width, self.btnFeatured.frame.size.height);
[themeView addSubview:self.btnFeatured];
That's it! You can use your outlet to enable/disable your button, setup a mask image when selected, enable/disable the toolbar or even hide everything and add a window title. All of this without worry if Apple changes their standard Window Titlebars.
P.S. No private frameworks were used in this posting whatsoever!

INAppStoreWindow is a NSWindow subclass, it tell you how to change the height of title bar.
https://github.com/indragiek/INAppStoreWindow
http://iloveco.de/adding-a-titlebar-accessory-view-to-a-window/
This example tells you how to add buttons in the title bar.

You'd have to subclass NSWindow and do a custom window frame drawing. It's not only about a titlebar. It's about whole window frame (so you can, actually, put close/minimize/zoom buttons at the bottom if you wish).
A good starter is at "Cocoa with love" website.

There are a few new solutions based on INAppStoreWindow and without warning and log message, for anyone who wants to change the height of NStitlebar, change the position of traffic light, add an item(e.g. a NSbutton) on NStitlebar and change its position, please check below.
WAYWindow:
https://github.com/weAreYeah/WAYWindow
NStitlebar_with_item:
https://github.com/ZHANGneuro/NStitlebar_with_item

Related

NSWindow resize indicator not visible

How do I show resize indicators for an NSWindow without Titlebar?
I created a new Xcode project(for Mac app) with storyboard. I just disabled the checkbox Title Bar in Appearance(It hides the Title bar of NSwindow).
The strange thing was, after disabling the TitleBar, NSWindow was not showing resize indicators while mouse was above the window edges. Although if I drag at edges it was resizing.
I guess this is a bug, because if the window can be resized by dragging the mouse over edges, it must show the resize indicators.
As it can be seen in the image, the resize indicators are seen after user drags the window, but many users would think that since there is no resize indicator, the window is not resizable.
I've fixed this issue by subclassing NSWindow and overriding canBecomeKeyWindow to return YES:
#import "MyWindow.h"
#implementation MyWindow
- (BOOL)canBecomeKeyWindow {
return YES;
}
#end
Not updating resize cursors in this case looks like Apple bug. Documentation states "The value of canBecomeKeyWindow property is YES if the window has a title bar or a resize bar, or NO otherwise.", so I expect that canBecomeKeyWindow will return YES for resizable window. But it doesn't.
UPD: Checked on 10.10.5. Hopefully, you will have same behaviour on 10.11.
I have not checked this, but you could set the resize indicators manually. I think I would add four NSTrackingAreas to the windows contentView subclass (one for each side of the window, only few pixels in height/width).
In the mouseEntered() method, create a new NSCursor object for the appropriate mouse position. Remember that the position could change, so use the mouseMoved() method as well.
On mouseExited() reset the cursor.
Again, I have not tried this, but it should work.
PS: Don't forget to file a radar on this ;)

Use combined titlebar + toolbar while preserving title visibility

The System Preferences app feature a combined title bar and toolbar with vertically centered buttons and the title. I am trying to mimic this exactly in my app. I have been able to combine the title bar and toolbar using Interface Builder (on the NSWindow check Title Bar and Unified Title and Toolbar), but this does not center the content vertically. I discovered via this question you can simply set the window's titleVisibility to NSWindowTitleHidden which will vertically center the stoplight buttons. Unfortunately this of course hides the title. How can one vertically center content in the unified titlebar/toolbar and also show the window's title just like System Preferences - either in IB or programmatically?
I ended up setting titleVisibility to NSWindowTitleHidden and manually created an NSView that contains an NSTextField that mimics the standard title appearance, providing that to the window's addTitlebarAccessoryViewController method. Still would like to find a better solution to use the default title appearance, if possible.
I used WAYAppStoreWindow on GitHub to do this. I created a fork of the WAYWindow subproject to vertically centre the document title since this wasn't supported. This means any applied themes/appearances are honoured.

NSToolbar special area

I like to try to completely take over the area where the NSToolbar resides so I can put my own custom controls, views and background. The advantages of using this area are:
Any sliding panels appear below the toolbar area instead of just the title bar.
In Lion, the toolbar area comes down along with the menu bar when the mouse is at the top of the screen.
I have tried using a borderless window, and implementing my own custom views within it but unfortunately I lose the above advantages as well as having a few other minor problems.
My current method is to use the undocumented method '_toolbarView' with the NSToolbar and add my custom view into its subviews. This works fine as I can turn off toolbar customisation. Unfortunately, the size of the toolbar is initialised with the items within that toolbar. Does anyone know if I can change the size of toolbar without adding a fake ToolbarItem?
Maybe there's also a better way of doing this that I am currently unaware of.
Thanks for any suggestions and comments.
No need to use any undocumented APIs. Just create a toolbar item with a custom view:
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag {
NSToolbarItem *item = [[[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier] autorelease];
…
[item setView:myCustomToolbarView];
…
}
You can control your custom toolbar’s size using the item’s minSize and maxSize properties (e. g. in your NSWindowDelegate’s -windowDidResize:).
Remember to also update the toolbar display mode so it doesn't show item labels:
[toolbar setDisplayMode: NSToolbarDisplayModeIconOnly];

Change color of title bar in cocoa

This must have been asked before, but after Googling I still can't find the answer.
How do you change the color of the title bar (The bar that you can click and drag around with the close, minimize and maximize buttons) to a different color than the default gray in Cocoa?
If you set the background color of a "textured" window (a distinction that isn't really all that visible in Snow Leopard) that color will be applied to the titlebar as well. This is what Firefox does.
I would recommend though not having a real titlebar (i.e. setting your window to have no titlebar) and using +[NSWindow standardWindowButton:forStyleMask:] and putting your own buttons in the "titlebar". This allows you more control and is way way less hacky.
If it's a panel, you can change it to black by instantiating it as a HUD window.
Otherwise, you can't. Ever notice how there aren't any Aqua windows with different-colored title bars roaming around in other apps? This is why.
The only other way to change the appearance of the title bar (without relying on private implementation details such as the existence of a frame view) is to make the window borderless and create its title bar and window buttons from the ground up.
If you go with Colin's approach of making the window textured in interface builder (check box in the attributes of the window), here's the line to change the background color of the window you'd put in this function of the appDelegate.m file
//In this function --->
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
//type this
[_window setBackgroundColor: NSColor.whiteColor];
If you don't mind private API, you could subclass NSThemeFrame.
Setting title bar appears as transparent
self.window.titlebarAppearsTransparent = YES;
And setting window background color as you wish

Cocoa: [statusItem setView:myView] makes a white bar menu item no matter what

In my small app for Mac OS X I display some info in system menubar. I use
statusItem = [
[[NSStatusBar systemStatusBar]
statusItemWithLength:NSVariableStatusItemLength]
retain
];
It works very nice and I can change the text with
[statusItem setTitle:[NSString stringWithString:#"Woo-hoo"]];
But it uses the default menu font which is too big for my relatively unimportant info. So I decided to reimplement it with a custom view. I created a view in Interface Builder.
Unfortunately, however, when I set it as a view for my menu item with
[statusItem setView:myView];
it just displays a white bar in the menu instead of my thing. I tried to
[statusItem
drawStatusBarBackgroundInRect:[myView frame]
withHighlight:NO];
with no success.
In trying to figure out whether a problem is with the view itself or with the way I assign it to the menubar, I created a window and did
[myTestWindow setContentView:myView];
This one worked seamlessly. This makes me think my view is OK :-)
So, what else can I try to make the menu item display my own view?
Thanks!
It happened to be some weird side-effects of window-view autosizing setup in Interface Builder (let’s call them size-effects). In the Inspector you can setup how subviews get resized upon superview sizing. And so it was somehow broken in my case, such that when window gets small enough (menuitem-high), my elements just got drawn outside of the window’s frame.
I re-configured the sizing in IB, eliminating all the automatics I don’t need, and now it works perfectly: the view from IB gets displayed inside a menu item.
What is the height of the frame of the view? Maybe your view is taller than the menubar and you are drawing outside of it. The current menubar is 22 pixels, but you should ask the systemStatusBar for it's thickness, just in case it ever changes.
Try drawing a frame around your view to see if you are getting anything.
[[NSColor blueColor] set];
NSBezierPath *path = [NSBezierPath bezierPathWithRect:self.bounds];
[path setLineWidth:4.0f];
[path stroke];
If you get just an 'L' shape (the bottom left corner) of blue then the view is too large. If you get a rectangle but still no text then you may not be drawing the text inside the view, look at the coordinates you are drawing the text at (and review View Geometry). Putting the view in a window may have worked because it is larger.
For an example of using text in a status menu view take a look at Matt Gemmell's NSStatusItemTest project.
EDIT:
Sorry, somehow I missed where you said you created the view in IB. I did a quick test and I can see the white box you mentioned.
The docs for NSStatusItem's setView: states
The custom view is responsible for
drawing itself and providing its own
behaviors, such as processing mouse
clicks and sending action messages.
And status item views go into a special (apple private) window called NSStatusBarWindow that may have different internal behavior than normal windows and certainly seems to not support views from IB.
So yes, I think you need to create a custom NSView subclass and do your own drawing in drawrect:.

Resources