8

Specifically, it behaves inconsistently regarding text field focus.

I have an LSUIElement popping up a status menu. Within that menu there is a view containing a text field. The text field needs to be selectable -- not necessarily selected by default, but whichever.

When the status item is clicked, it triggers

[NSApp activateIgnoringOtherApps:YES];

And it works, about half the time.* The other half the status menu seems to consider itself "in the background" and won't let me put focus on the text field even by clicking on it. (I know the status item click-trigger is firing b/c there's an NSLog on it.)

Is this a bug in the way Apple handles these status items, or am I mishandling activateIgnoringOtherApps?

*In fact, it seems to fail only the first time after another app is activated. After that it works fine.

The complete snippet:

-(void)statusItemClicked:(id)sender {
    //show the popup menu associated with the status item.
    [statusItem popUpStatusItemMenu:statusMenu];

    //activate *after* showing the popup menu to obtain focus for the text field.
    [NSApp activateIgnoringOtherApps:YES];

}
iconmaster
  • 175
  • 1
  • 10

2 Answers2

2

Finally came up with a workaround for this.

Instead of popping the menu in your click handler, activate the app then schedule an NSTimer with no delay that pops the menu:

-(void)pop:(NSTimer *)timer {
    [statusItem popUpStatusItemMenu:theMenu];
}

-(void)statusItemClicked:sender {
    [NSApp activateIgnoringOtherApps:YES];
    [NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(pop:) userInfo:nil repeats:NO];
}

pop: is called on the next frame so the delay is imperceptible but long enough for activateIgnoringOtherApps: to do whatever was preventing it from working as expected when popping the menu in the same frame.

Shaun Inman
  • 1,968
  • 1
  • 19
  • 28
-1

I know from experience that you have to call activateIgnoringOtherApps: after you've popped up the menu that contains your text field. So you would need to do it in this order:

- (void)statusItemClicked:sender {
    [statusItem popUpStatusItemMenu:theMenu];
    [NSApp activateIgnoringOtherApps:YES]; // FYI, NSApp is shorthand for [NSApplication sharedApplication]
}

Based on what you've said, it sounds like your application is activating too late, so that it's not getting activated the first time you click on the item, but it is already activated on subsequent clicks.

Alex
  • 26,829
  • 3
  • 55
  • 74
  • It's definitely activating too late, but the code was already structured as you suggest. It almost seems like the activate method isn't getting called until that status menu has been dismissed (NSLog testing seems to bear this out). I don't understand why that'd be happening. – iconmaster Dec 08 '09 at 16:03
  • So does `activateIgnoringOtherApps:` get called immediately after `popUpStatusItemMenu:`, or does that method not return until the menu is dismissed? You might try setting a breakpoint and running through the debugger to see what happens. – Alex Dec 08 '09 at 16:37
  • Yup, using breakpoints it definitely gets called only after the menu is dismissed. Adding my code to the first post, though it looks almost exactly like your suggestion. – iconmaster Dec 08 '09 at 17:13
  • Does it work if you reverse those two lines? If you activate the application before you pop up the menu? Obviously it doesn't do you any good to activate the app only after the menu is dismissed, and I can't think of another hook that would be called when the menu appears. – Alex Dec 08 '09 at 18:57
  • Reversing them still leaves the text filed unselectable, as well as throwing some errors after the menu is dismissed. statusItemClicked is being set as statusMenu's selector from within awakeFromNib -- could that be part of the delay? – iconmaster Dec 08 '09 at 19:54