Show NSPopover from NSTokenField Token on Click
-
02-06-2021 - |
Question
I am attempting to show an NSPopover
from an NSTokenField
token when the token is clicked.
These tokens have a built in way to show a menu, so from a design standpoint, it's not unlike that action.
However, there does not appear to be any (good) way to execute arbitrary code when a token is clicked.
I have managed to slip some code into - tokenField:menuForRepresentedObject:
, but it's far from elegant.
Assuming that - tokenField:menuForRepresentedObject:
is the only way to execute code when a token is clicked, I still have another problem: getting the NSRect
that represents the token, so that the NSPopover
can maintain a visual relationship with said token. I've tried to do some string juggling, figure out how tokens come first, etc., but it is far from reliable, and even requires an undocumented method.
Bottom Line: How do I show an NSPopover
from the selected token in an NSTokenField
?
Solution 2
This is what I ended up doing. I'm working on an open-source NSTokenField
alternative with this capability built in.
// in an NSWindowController
- (NSMenu *)tokenField:(NSTokenField *)tokenField menuForRepresentedObject:(id)representedObject
{
NSRect displayRect = NSMakeRect(([NSEvent mouseLocation].x - 2.5),
([NSEvent mouseLocation].y - 2.5),
5, 5);
displayRect = [self.window convertRectFromScreen: displayRect];
// establish popover from displayRect ...
}
It looks pretty great, despite feeling very hacked (and being ocasionally 1px off).
OTHER TIPS
I think it can't be done (see my endeavours here). The problem is that an individual token is not exposed in such a way that you can reference its bounds in order to hook it up with a popover.
Another option is to use the ability of NSMenuItem to support an arbitrary view:
- (NSMenu *)tokenField:(NSTokenField *)tokenField menuForRepresentedObject:(id)representedObject {
NSMenu *menu = [[NSMenu alloc] init];
NSMenuItem *item = [NSMenuItem alloc] init];
[item setRepresentedObject: representedObject];
NSNib *nib = [[NSNib alloc] initWithNibNamed: @"token" bundle: nil];
[nib instantiateWithOwner: item topLevelObjects: nil];
[menu addItem: item];
return menu;
}
Then you can create a User Interface where the File Owner is an NSMenuItem and it is connected to a custom view with everything you wanted in the Popover. You can use the representedObject for binding to values in the nib.
I'm at the very early stages of doing this. I haven't hooked up my NSManagedObjects to the Token Field yet, and I'm just using a string array for testing, but doing it this way does get a Popover like effect, except for the little corner pointer. Seems much nicer and simpler than many of the other hacks I've seen to do this while researching this.