Can an NSStatusItem be shrunk to fit?

739 views Asked by At

I have a variable length NSStatusItem which I'd like to stay visible whenever possible, even if that means showing only some of the content, but when my item is wide enough to run into an application's menu bar, it is hidden entirely. Is there a way to tell when this happens so that I can shrink the view to fit available space?

I've experimented with a custom view, overriding all the viewWill* methods, the frame setters, and the display methods, and periodically checking whether the containing window has moved or become hidden. I can't find any way to tell when my item is too long.

2

There are 2 answers

1
hollow7 On BEST ANSWER

This depends on whether your status item application can detect the number of menu items in the OS X menu bar. A quick search through apple documentation shows that there are no public APIs provided by Apple for the purpose of doing this. To my knowledge, there are no private ones available too.

So I would recommend instead that you make your status item small by default and expanded when clicked by the user.

Edit: Actually look at the discussion here: a really clever way to detect if your status item is being hidden. So once you have detected that it is being hidden, you can shrink it so that it reappears.

0
ArtOfWarfare On

Here's a complete working example based on the discussion that hollow7 referenced:

self.statusItem.title = @"Message that will be truncated as necessary.";
while (self.statusItem.title.length > 0) {
    CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenAboveWindow, (CGWindowID)self.statusItemWindow.windowNumber);
    if (CFArrayGetCount(windowList) > 1) {
        CFRelease(windowList);
        self.statusItem.title = [self.statusItem.title substringToIndex:self.statusItem.title.length - 1];
    } else {
        CFRelease(windowList);
        break;
    }
}

A tricky part that remains is getting the NSStatusItem window. So far, I've found two methods for obtaining it.

1 - There's a private method called _window. You can utilize it like this:

self.statusItemWindow = [self.statusItem performSelector:@selector(_window)];

2 - This is a bit more complicated but I think this is more likely to pass Apple's static analysis for private method usage in the Mac App Store:

Set the target and action of the NSStatusItem to a method you control, like this:

self.statusItem.target = self;
self.statusItem.action = @selector(itemClicked:);

Then access the window in the invoked method:

- (void)itemClicked:(id)sender {
    self.statusItemWindow = [[NSApp currentEvent] window];
}