Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSToolbar created programmatically starts empty and won't save

I'm currently trying to write a Mac application. In doing so, I'm having some peculiar problems when trying to setup an NSToolbar.

Although I have setup all the components as per the API documentation, when the application loads, the toolbar always starts empty. When I open the customisation pane, the toolbar items are there, and I can drag them into the toolbar, but when I quit the application and restart the changes are gone.

Note: I am aware that many of you will be of the opinion that the best way to solve this is to use interface builder rather than doing it in code. That is not the answer I am looking for - I chose to make this application without IB in order to better understand the internals of a Cocoa app.

I have verified (with NSLogs) that neither the toolbarAllowedItemIdentifiers nor the toolbarDefaultItemIdentifiers delegate methods get called when the toolbar is first initialised, but they do get called when you enter the customisation pane.

Below, please find a minimal, verifiable and complete example version of a basic app that demonstrates this bug. Anyone that can shed some light on this matter would be hugely appreciated!

Thanks

Defines.h

#define UNUSED(x) (void)(x)
#define TOOLBAR_ONE @"ONE"
#define TOOLBAR_TWO @"TWO"
#define TOOLBAR_IDENT @"TOOLBAR"

#define WINDOW_MASK NSTitledWindowMask | \
   NSClosableWindowMask | \
   NSResizableWindowMask | \
   NSMiniaturizableWindowMask

main.m

#import "BWAppDelegate.h"
#import <AppKit/AppKit.h>

int main(void) {
   @autoreleasepool {
      NSApplication* application = [NSApplication sharedApplication];
      BWAppDelegate* delegate    = [[BWAppDelegate alloc] init];

      application.delegate = delegate;
      [application run];

      return EXIT_SUCCESS;
   }
}

BWAppDelegate.h

#import <Cocoa/Cocoa.h>

#import "BWMainToolbarDelegate.h"

@interface BWAppDelegate : NSObject<NSApplicationDelegate>

@property (atomic, strong) NSWindow*              window;
@property (atomic, strong) BWMainToolbarDelegate* toolbarDelegate;

@end

BWAppDelegate.m

#import <AVFoundation/AVFoundation.h>
#import <AppKit/AppKit.h>
#import <Cocoa/Cocoa.h>

#import "BWAppDelegate.h"
#import "Defines.h"

@implementation BWAppDelegate

@synthesize window, toolbarDelegate;

- (void) applicationDidFinishLaunching: (NSNotification*) aNotification
{
   UNUSED(aNotification);
   NSRect dims = NSMakeRect(0, 0, 300, 300);

   self.window = [[NSWindow alloc] initWithContentRect:dims
                                             styleMask:WINDOW_MASK
                                               backing:NSBackingStoreBuffered
                                                 defer:NO];
   [self.window makeKeyAndOrderFront:nil];
   self.window.toolbar = [[NSToolbar alloc] initWithIdentifier:TOOLBAR_IDENT];
   toolbarDelegate     = [[BWMainToolbarDelegate alloc] initWithToolbar:self.window.toolbar];
}

@end

BWMainToolbarDelegate.h

#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>

@interface BWMainToolbarDelegate : NSObject<NSToolbarDelegate>

- (instancetype) initWithToolbar: (NSToolbar*) theToolbar;

- (NSArray*) toolbarAllowedItemIdentifiers: (NSToolbar*) toolbar;
- (NSArray*) toolbarDefaultItemIdentifiers: (NSToolbar*) toolbar;
- (NSToolbarItem*)   toolbar: (NSToolbar*) toolbar
       itemForItemIdentifier: (NSString*) identifier
   willBeInsertedIntoToolbar: (BOOL) flag;

@end

BWMainToolbarDelegate.m

#import "BWMainToolbarDelegate.h"
#import "Defines.h"

@implementation BWMainToolbarDelegate {
   NSToolbar* toolbar;
}

- (instancetype) initWithToolbar: (NSToolbar*) theToolbar
{
   self = [super init];
   if(self) {
      toolbar = theToolbar;
      toolbar.displayMode = NSToolbarDisplayModeIconAndLabel;
      toolbar.allowsUserCustomization = YES;
      toolbar.autosavesConfiguration = YES;
      toolbar.delegate    = self;
   }
   return self;
}

- (NSArray*) toolbarAllowedItemIdentifiers: (NSToolbar*) theToolbar
{
   UNUSED(theToolbar);
   return @[TOOLBAR_ONE, TOOLBAR_TWO];
}

- (NSArray*) toolbarDefaultItemIdentifiers: (NSToolbar*) theToolbar
{
   UNUSED(theToolbar);
   return @[TOOLBAR_ONE, TOOLBAR_TWO];
}

- (NSToolbarItem*)   toolbar: (NSToolbar*) theToolbar
       itemForItemIdentifier: (NSString*) identifier
   willBeInsertedIntoToolbar: (BOOL) flag
{
   UNUSED(flag);
   NSToolbarItem* returnVal = nil;
   NSString*      label;

   if([theToolbar.identifier isEqualToString:TOOLBAR_IDENT]) {

      if([identifier isEqualToString:TOOLBAR_ONE]) {
         returnVal = [[NSToolbarItem alloc] initWithItemIdentifier:TOOLBAR_ONE];
         label     = @"Toolbar One";
      } else if([identifier isEqualToString:TOOLBAR_TWO]) {
         returnVal = [[NSToolbarItem alloc] initWithItemIdentifier:TOOLBAR_TWO];
         label     = @"Toolbar TWO";
      }
   }

   returnVal.label        = label;
   returnVal.paletteLabel = label;
   return returnVal;
}

@end
like image 568
Ben Wainwright Avatar asked Dec 11 '22 15:12

Ben Wainwright


1 Answers

Set the delegate of the toolbar before adding the toolbar to the window.

like image 128
Willeke Avatar answered Feb 24 '23 11:02

Willeke