Launching NSColorPanel in Objective C

842 views Asked by At

I'm writing a library to launch operating system dialogs such as file open/save, message dialogs, and prompts from C applications. I can't figure out how to launch a color picker on Mac, for selecting a RGB(A) color value.

This is as far as I've gotten in the Mac implementation of my library in Objective C. I assume I need to launch a NSColorPanel, but I can't find an example of how to do this online.

int launchColorPicker(float* r, float* g, float* b, float* a) {
    @autoreleasepool {
        NSColorPanel* panel = [NSColorPanel sharedColorPanel];

        // ???

        *r = ...;
        *g = ...;
        *b = ...;
        *a = ...;
        return succeeded;
    } // @autoreleasepool
}
2

There are 2 answers

1
apodidae On

The following is an example of a programmatic NSColorPanel in objc. It may be run in Xcode by replacing main.m with the following code and deleting the pre-existing AppDelegate to avoid duplicate symbols.

#import <Cocoa/Cocoa.h>

@interface CustomView : NSView {
 NSTextField *redFld;
 NSTextField *greenFld;
 NSTextField *blueFld;
 NSColor *myColor;
}
 @property (strong) NSTextField *redFld;
 @property (strong) NSTextField *greenFld;
 @property (strong) NSTextField *blueFld;
@end

 @implementation CustomView
 @synthesize redFld;
 @synthesize greenFld;
 @synthesize blueFld;
 
 - (id)initWithFrame:(NSRect)frameRect {
 if ((self = [super initWithFrame:frameRect]) != nil) {
  myColor = [NSColor whiteColor];
 }
 return self;
 }
 
 - (void)changeColor:(id)sender {
 myColor = [sender color];
 [redFld setStringValue:[NSString stringWithFormat:@"r = %f",[[sender color] redComponent]]];
 [greenFld setStringValue:[NSString stringWithFormat:@"g = %f",[[sender color] greenComponent]]];
 [blueFld setStringValue:[NSString stringWithFormat:@"b = %f",[[sender color] blueComponent]]];
 [self setNeedsDisplay:YES];
}
 
 -(void)drawRect:(NSRect)rect {
 // **** Background **** //
 [myColor set];
 [NSBezierPath fillRect:rect];
}

 // ----- Use this if you want 0,0 (origin) to be top, left ---- //
 // ----- Otherwise origin will be at bottom, left (Unflipped) ----- //
-(BOOL)isFlipped {
 return YES;
}
@end

@interface AppDelegate : NSObject <NSApplicationDelegate> {
 NSWindow *window;
 NSColorPanel *colorPanel;
 NSTextField *redFld;
 NSTextField *greenFld;
 NSTextField *blueFld;
 CustomView *customView;
}
 @end

 @implementation AppDelegate
  
-(void) buildMenu{
 NSMenu *menubar = [NSMenu new];
 [NSApp setMainMenu:menubar];
 NSMenuItem *menuBarItem = [NSMenuItem new];
 [menubar addItem:menuBarItem];
 NSMenu *appMenu = [NSMenu new];
 [menuBarItem setSubmenu:appMenu];
 [appMenu addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"];
}
 
-(void) buildWindow {
 #define _wndW  500
 #define _wndH  550
 
 window = [[NSWindow alloc] initWithContentRect: NSMakeRect( 0, 0, _wndW, _wndH ) styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskClosable backing: NSBackingStoreBuffered defer: NO];
 [window center];
 [window setTitle: @"Test window"];
 [window makeKeyAndOrderFront: nil];
 
// **** Create an instance of CustomView **** //
 customView = [[CustomView alloc]initWithFrame:NSMakeRect( 20, 60, _wndW - 40, _wndH - 120 )];
 [[window contentView] addSubview:customView];

// **** NSColorPanel **** //
 colorPanel = [NSColorPanel sharedColorPanel];
 [NSColorPanel setPickerMode:NSColorPanelModeWheel];
 [NSApp orderFrontColorPanel:colorPanel];
 [colorPanel setTarget:customView];
 [colorPanel setAction:@selector(changeColor:)];
 
// **** Display Fields **** //
 redFld = [[NSTextField alloc] initWithFrame:NSMakeRect( 20, _wndH - 40, 110, 22 )];
 [[window contentView] addSubview:redFld];
 [redFld setStringValue:@"red"];
 [customView setRedFld:redFld];
 
 greenFld = [[NSTextField alloc] initWithFrame:NSMakeRect( 140, _wndH - 40, 110, 22 )];
 [[window contentView] addSubview:greenFld];
 [greenFld setStringValue:@"green"];
 [customView setGreenFld:greenFld];
 
 blueFld = [[NSTextField alloc] initWithFrame:NSMakeRect( 260, _wndH - 40, 110, 22 )];
 [[window contentView] addSubview:blueFld];
 [blueFld setStringValue:@"blue"];
 [customView setBlueFld:blueFld];
 
 // **** Quit btn **** //
 NSButton *quitBtn = [[NSButton alloc]initWithFrame:NSMakeRect( _wndW - 50, 10, 40, 40 )];
 [quitBtn setBezelStyle:NSBezelStyleCircular ];
 [quitBtn setTitle: @"Q" ];
 [quitBtn setAction:@selector(terminate:)];
 [[window contentView] addSubview: quitBtn];
 }
 
 -(void) applicationWillFinishLaunching: (NSNotification *)notification
 {
 [self buildMenu];
 [self buildWindow];
 }
 
 @end

int main() {
 NSApplication *application = [NSApplication sharedApplication];
 AppDelegate *appDelegate = [[AppDelegate alloc] init];
 [application setDelegate:appDelegate];
 [application run];
return 0;
}


0
apodidae On

Just for your information there are several types of color panels, shown at the touch of a button in the following demo. You'll still have to figure out how to get the values from the user's selection.

#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate> {
 NSWindow *window;
}
@end

@implementation AppDelegate
 
-(void) myBtnAction:(id)sender {
 [NSColorPanel setPickerMode:[sender tag]];
 [NSApp orderFrontColorPanel:nil];
}
 
-(void) buildMenu {
 NSMenu *menubar = [NSMenu new];
 [NSApp setMainMenu:menubar];
 NSMenuItem *menuBarItem = [NSMenuItem new];
 [menubar addItem:menuBarItem];
 NSMenu *appMenu = [NSMenu new];
 [menuBarItem setSubmenu:appMenu];
 [appMenu addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"];
}
 
-(void) buildWindow {
 #define _wndW  380
 #define _wndH  420
 
 window = [[NSWindow alloc] initWithContentRect: NSMakeRect( 0, 0, _wndW, _wndH )styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskClosable backing: NSBackingStoreBuffered defer: NO];
 [window center];
 [window setTitle: @"NSColorPanel Types"];
 [window makeKeyAndOrderFront: nil];
 
 // ***** NSBox ***** //
 #define _boxW 265
 #define _boxH 340
 NSBox *boxBtn = [[NSBox alloc]initWithFrame:NSMakeRect( 60, 60, _boxW, _boxH )];
 [boxBtn setTitle:@""];
 [boxBtn setBorderType:NSBezelBorder];
 [[window contentView] addSubview: boxBtn];
 
 // **** Radio Group **** //
 NSArray *colorPanelTypes = [NSArray arrayWithObjects:
 @"NSNoModeColorPanel",
 @"NSGrayModeColorPanel",
 @"NSRGBModeColorPanel",
 @"NSCMYKModeColorPanel",
 @"NSHSBModeColorPanel",
 @"NSCustomPaletteModeColorPanel",
 @"NSColorListModeColorPanel",
 @"NSWheelModeColorPanel",
 @"NSCrayonModeColorPanel",
 nil];
 
 NSButton *btn[[colorPanelTypes count]];
 int count;
 count = 0;
 #define _btnW  260
 #define _btnH  24
 #define _spacing  10
 #define _left  10
 #define _top  _boxH - 55
 for (unsigned int row = 0; row < [colorPanelTypes count]; row++ ) {
  btn[count] = [[NSButton alloc]initWithFrame:NSMakeRect( _left, _top - row*(_btnH + _spacing), _btnW, _btnH )];
  [btn[count] setButtonType:NSButtonTypeRadio];
 // **** start at minus one **** //
  [btn[count] setTag: count-1];
  [btn[count] setTitle: [colorPanelTypes objectAtIndex:count]];
  [btn[count] setTarget:self];
  [btn[count] setAction:@selector(myBtnAction:)];
  if (count == 0) {[btn[count] setState:NSControlStateValueOn];}
  [boxBtn addSubview: btn[count]];
  count++;
 }
 
 // **** Quit btn **** //
 NSButton *quitBtn = [[NSButton alloc]initWithFrame:NSMakeRect( _wndW - 50, 10, 40, 40 )];
 [quitBtn setBezelStyle:NSBezelStyleCircular ];
 [quitBtn setTitle: @"Q" ];
 [quitBtn setAction:@selector(terminate:)];
 [[window contentView] addSubview: quitBtn];
}
 
-(void) applicationWillFinishLaunching: (NSNotification *)notification {
 [self buildMenu];
 [self buildWindow];
}
 
@end

int main() {
 NSApplication *application = [NSApplication sharedApplication];
 AppDelegate *appDelegate = [[AppDelegate alloc] init];
 [application setDelegate:appDelegate];
 [application run];
 return 0;
}