Is it possible to draw in the label area of NSToolbar?

▼魔方 西西 提交于 2019-12-02 22:09:22

The Xcode status view is not an NSToolbarItem is a custom NSView inserted in the NSToolbar.

If you log

    NSLog(@" %@", [[self.window.contentView superview] subviews]);

You will get

NSToolbarView doesn't autoresize it's subviews so you have issues with centering it. And [self.window.contentView superview] doesn't contain the toolbarview when full screen.

You can add the view you want in the center of the toolbar to the [self.window.contentView superview] when not in fullscreen and position it properly. It will autoresize and stay centered. When switching to full screen remove it from [self.window.contentView superview] and add it to NSToolbarView in the center that way it stays in the toolbar and it also moves down with the toolbar when you reveal the status bar.

You can get the toolbar view by iterating thru the subviews or with a private method

[[self.window toolbar] performSelector:@selector(_toolbarView)];

Update: I did a little more digging with the debugger and I found out that this is what Xcode does. At least while not in full screen.

    thealch3m1st$ sudo lldb 
(lldb) process attach -p 11478
Process 11478 stopped
Executable module set to "/Applications/Xcode.app/Contents/MacOS/Xcode".
Architecture set to: x86_64.
(lldb) po [NSApplication sharedApplication]
(id) $0 = 0x000000040013f5e0 <IDEApplication: 0x40013f5e0>
(lldb) po [$0 mainWindow]
(id) $1 = 0x0000000000000000 <nil>
(lldb) po [$0 windows]
(id) $2 = 0x0000000408278460 <__NSArrayM 0x408278460>(
<IDEWelcomeWindow: 0x40141c1e0>,
<IDEWorkspaceWindow: 0x401ef2780>,
<NSComboBoxWindow: 0x402019be0>,
<NSWindow: 0x4022adc60>,
<IDEOrganizerWindow: 0x402951b20>
)
(lldb) po [$0 windows]
(id) $3 = 0x0000000408820300 <__NSArrayM 0x408820300>(
<IDEWelcomeWindow: 0x40141c1e0>,
<IDEWorkspaceWindow: 0x401ef2780>,
<NSComboBoxWindow: 0x402019be0>,
<NSWindow: 0x4022adc60>,
<IDEOrganizerWindow: 0x402951b20>
)

(lldb) [$3 objectAtIndex:1]
error: '[$3' is not a valid command.
(lldb) po [$3 objectAtIndex:1]
(id) $4 = 0x0000000401ef2780 <IDEWorkspaceWindow: 0x401ef2780>
(lldb) po [$4 contentView]
(id) $5 = 0x0000000401ef0920 <NSView: 0x401ef0920>
(lldb) po [$5 superview]
(id) $6 = 0x0000000401ef2e20 <NSThemeFrame: 0x401ef2e20>
(lldb) po [$6 subviews]
(id) $7 = 0x0000000401ef3800 <__NSArrayM 0x401ef3800>(
<_NSThemeCloseWidget: 0x401ef3120>,
<_NSThemeWidget: 0x401ef3b80>,
<_NSThemeWidget: 0x401ef40e0>,
<NSView: 0x401ef0920>,
<IDEActivityView: 0x4020cd700>,
<_NSThemeFullScreenButton: 0x402017b20>,
(<NSToolbarView: 0x4020192e0>: Xcode.IDEKit.ToolbarDefinition.Workspace),
<DVTDualProxyWindowTitleView: 0x40225e0a0>,
<NSThemeDocumentButton: 0x402698020>
)

(lldb) po [$7 objectAtIndex:4]
(id) $8 = 0x00000004020cd700 <IDEActivityView: 0x4020cd700>
(lldb) [$8 setHidden:YES]
error: '[$8' is not a valid command.
(lldb) po [$8 setHidden:YES]
(id) $9 = 0x0000000000000000 <nil>
(lldb) continue
Process 11478 resuming
(lldb) 

And the activity view is gone :)

While in full screen however. It doesn't add it to NSToolbarView it adds it to NSNextStepFrame which is NSToolbarView's superview. The toolbar is not contained in the window's contentview superview when in full screen. I think it has something to do with full screen behavior and spaces.

You have to subclass NSToolbarItem:

- (id)initWithItemIdentifier:(NSString *)itemIdentifier {
    self = [super initWithItemIdentifier:itemIdentifier];
    if (self) {
        self.hideLabel = NO;
    }
    return self;
}

- (NSView *)view {
    NSView *view = [super view];

    if (self.hideLabel) {
        CGRect frame = view.frame;
        frame.size.height = 45.0f;
        frame.origin.y = 8.0f;
        view.frame = frame;
    }

    return view;
}

- (NSString *)label {
    return self.hideLabel ? @"" : [super label];
}

Create a toolbar:

NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"Toolbar"];
toolbar.delegate = self;
self.window.toolbar = toolbar;

Use NSToolbarDelegate to fill your toolbar with items:

- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar {
    return [NSArray arrayWithObjects:@"Button", @"LCD", NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSpaceItemIdentifier, nil];
}

- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar {
    return [NSArray arrayWithObjects:@"Button", NSToolbarFlexibleSpaceItemIdentifier, @"LCD", NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSpaceItemIdentifier, nil];
}

- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag {
    MyToolbarItem *item = [[MyToolbarItem alloc] initWithItemIdentifier:itemIdentifier];

    if ([itemIdentifier isEqualToString:@"LCD"]) {
        item.view = self.lcdView;
        item.hideLabel = YES;
    } else if ([itemIdentifier isEqualToString:@"Button"]) {
        item.label = NSLocalizedString(@"Button", nil);
        item.image = [NSImage imageNamed:@"Button"];
        item.hideLabel = NO;
    }

    return item;
}

The lcd view should be (in this case) 32 points high before you hand it over to the toolbar item. If it's bigger, the toolbar will be too high.

The Xcode status view is actually a separate window floating over the toolbar. (This is easily tested: press ⇧⌘4 and press space to take a screen shot of a window, and hover the mouse over it.)

This code installs a window floating on top of the toolbar.

-(void)applicationWillFinishLaunching:(NSNotification *)aNotification {
    NSRect winframe = [self.window frame];
    NSRect viewrect = NSMakeRect(0, 0, 400, 50);
    NSRect winrect = viewrect;
    winrect.origin.x = NSMidX(winframe) - NSMidX(winrect);  
    winrect.origin.y = NSHeight(winframe) - NSHeight(winrect) - 18;

    NSWindow* win = [[[NSWindow alloc] initWithContentRect:winrect styleMask: NSBorderlessWindowMask backing: NSBackingStoreBuffered defer: NO] autorelease];
    [win setBackgroundColor:[NSColor clearColor]];
    [win setOpaque:NO];
    [win setIgnoresMouseEvents:YES];

    MyStatusView* v = [[[MyStatusView alloc] initWithFrame:viewrect] autorelease];
    [win setContentView: v];

    [self.window addChildWindow:win ordered:NSWindowAbove];
}
Andrea Cremaschi

The iTunes-XCode-LCD that extends in the label area is not a NSToolbarItem. Since NSToolbar isn't a NSView, you cannot add a subview to a NSToolbar instance. But you can add a custom view directly in the window frame, that can be accessed through the contentView.superview property path of the NSWindow instance!

I.e. make your own subclass of NSWindowController and put some code like this in the 'windowDidLoad' method:

- (void)windowDidLoad
{  
[super windowDidLoad];

NSImage *image = [NSImage imageNamed:@"lcd"];
NSRect lcdFrameRect = NSMakeRect(self.window.frame.size.width / 2 - image.size.width/2, self.window.frame.size.height - image.size.height - 20, 

                                 image.size.width, image.size.height);
NSImageView *lcdView = [[NSImageView alloc] initWithFrame: lcdFrameRect];
[lcdView setImage: image];
lcdView.autoresizingMask = NSViewMinYMargin | NSViewMinXMargin | NSViewMaxXMargin;

NSView * contentView = self.window.contentView;
[contentView.superview addSubview: lcdView];
}

This code will not work in Lion's full-screen mode, since the frame window isn't drawn when in fullscreen. To fix this, the view can be moved in a floating window, child of the main one (just check the NSWindow addChildWindow:ordered: method).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!