It is important. I'm fully aware the way we do it is a hack and it was not that unexpected to see it stop working at some point...
Then instead of doing this:
NSView * theContentView = myWindow.contentView;
NSMenu * theCustomizeMenu = theContentView.superview.menu;
You can try can try enumerating subviews from the content view superview until you find it. Ideally you should isolate this hack in a category because the code is really nasty. Something like this:
//Public
@interface NSWindow (FindCustomizeToolbarMenu)
-(nullable NSMenu*)findToolbarContextMenu;
@end
**//Put all the in the .m**
#import "NSWindow+FindCustomizeToolbarMenu.h"
#import <objc/runtime.h>
@interface NSMenu (NSToolbarMenuMarkerHack)
@property (nonatomic) BOOL isCustomizeToolbarMenu;
@end
@implementation NSMenu (NSToolbarMenuMarkerHack)
static char const * const ToolbarMenuHackKey = "ToolbarMenuHackKey";
-(BOOL)isCustomizeToolbarMenu
{
NSNumber *result = objc_getAssociatedObject(self, ToolbarMenuHackKey);
return (result != nil) ? result.boolValue : NO;
}
-(void)setIsCustomizeToolbarMenu:(BOOL)isCustomizeToolbarMenu
{
objc_setAssociatedObject(self,
ToolbarMenuHackKey,
@(isCustomizeToolbarMenu),
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
@implementation NSWindow (FindCustomizeToolbarMenu)
-(NSMenu*)searchForCustomizeToolbarMenuInSubviews:(NSArray<NSView*>*)subviews
{
if (subviews.count == 0) { return nil; }
NSMenu *foundMenu = nil;
for (NSView *aSubview in subviews)
{
NSMenu *theMenu = aSubview.menu;
if (theMenu.isCustomizeToolbarMenu
|| [theMenu.itemArray.lastObject.title isEqualToString:@"Customize Toolbar…"]) // <--Should compare a localized string! If you append on the found menu later this check will fail because "Customize Toolbar…" will no longer be the last object!
{
foundMenu.isCustomizeToolbarMenu = YES; //Mark it in case you mutate the menu later and "Customize Toolbar…" is not the last menu item.
foundMenu = theMenu;
break;
}
}
//Found it?
if (foundMenu != nil)
{
return foundMenu;
}
else
{
//Didn't find it. Dig through descendant views.
for (NSView *aSubviewToDigIn in subviews)
{
NSMenu *nested = [self searchForCustomizeToolbarMenuInSubviews:aSubviewToDigIn.subviews];
if (nested != nil)
{
foundMenu = nested;
break;
}
}
return foundMenu;
}
}
-(NSMenu*)findToolbarContextMenu
{
NSView *theContentView = self.contentView;
NSView *contentViewSuperview = theContentView.superview;
NSMutableArray *theSuperViewSubviews = [contentViewSuperview.subviews mutableCopy];
[theSuperViewSubviews removeObject:theContentView]; //Don't need to search the content view since we know it doesn't have it.
NSMenu *foundMenu = [self searchForCustomizeToolbarMenuInSubviews:theSuperViewSubviews];
return foundMenu;
}
This is very ugly and fragile and untested so inspect the code and use at your own risk. But in theory you should be able to do something like this using the category:
NSMenu *toolbarMenu = [self.window findToolbarContextMenu];
[toolbarMenu addItemWithTitle:@"CUSTOM" action:nil keyEquivalent:@""];
You could try filing a Feedback and ask Apple to improve the API but I have my doubts about that. You'd at least have to wait a year for them to add it and there's a good chance they never will.