// 16 july 2014 #import "objc_darwin.h" #import "_cgo_export.h" #import #define toNSButton(x) ((NSButton *) (x)) #define toNSTextField(x) ((NSTextField *) (x)) #define toNSView(x) ((NSView *) (x)) #define toNSPopover(x) ((NSPopover *) (x)) #define toNSBox(x) ((NSBox *) (x)) @interface goControlDelegate : NSObject { @public void *gocontrol; } @end @implementation goControlDelegate - (IBAction)buttonClicked:(id)sender { buttonClicked(self->gocontrol); } - (IBAction)checkboxToggled:(id)sender { checkboxToggled(self->gocontrol); } - (void)controlTextDidChange:(NSNotification *)note { textfieldChanged(self->gocontrol); } @end id newButton(void) { NSButton *b; b = [[NSButton alloc] initWithFrame:NSZeroRect]; [b setButtonType:NSMomentaryPushInButton]; [b setBordered:YES]; [b setBezelStyle:NSRoundedBezelStyle]; setStandardControlFont((id) b); return (id) b; } void buttonSetDelegate(id button, void *b) { goControlDelegate *d; d = [goControlDelegate new]; d->gocontrol = b; [toNSButton(button) setTarget:d]; [toNSButton(button) setAction:@selector(buttonClicked:)]; } const char *buttonText(id button) { return [[toNSButton(button) title] UTF8String]; } void buttonSetText(id button, char *text) { [toNSButton(button) setTitle:[NSString stringWithUTF8String:text]]; } id newCheckbox(void) { NSButton *c; c = [[NSButton alloc] initWithFrame:NSZeroRect]; [c setButtonType:NSSwitchButton]; [c setBordered:NO]; setStandardControlFont((id) c); return (id) c; } void checkboxSetDelegate(id checkbox, void *b) { goControlDelegate *d; d = [goControlDelegate new]; d->gocontrol = b; [toNSButton(checkbox) setTarget:d]; [toNSButton(checkbox) setAction:@selector(checkboxToggled:)]; } BOOL checkboxChecked(id c) { if ([toNSButton(c) state] == NSOnState) return YES; return NO; } void checkboxSetChecked(id c, BOOL checked) { NSInteger state; state = NSOnState; if (checked == NO) state = NSOffState; [toNSButton(c) setState:state]; } // also good for labels static id finishNewTextField(NSTextField *t, BOOL bordered) { // same for text fields, password fields, and labels setStandardControlFont((id) t); // these three are the same across text fields, password fields, and labels; the only difference is the setBezeled: value, and it's only different on labels // THE ORDER OF THESE CALLS IS IMPORTANT; CHANGE IT AND THE BORDERS WILL DISAPPEAR [t setBordered:NO]; [t setBezelStyle:NSTextFieldSquareBezel]; [t setBezeled:bordered]; // smart quotes and other autocorrect features are handled by the window; see newWindow() in window_darwin.m for details // Interface Builder does this to make the text box behave properly // this disables both word wrap AND ellipsizing in one fell swoop // however, we need to send it to the control's cell, not to the control directly [[t cell] setLineBreakMode:NSLineBreakByClipping]; // Interface Builder also sets this to allow horizontal scrolling // it also sets this for labels, despite those not being scrollable [[t cell] setScrollable:YES]; return (id) t; } id newTextField(void) { NSTextField *t; t = [[NSTextField alloc] initWithFrame:NSZeroRect]; return finishNewTextField(t, YES); } id newPasswordField(void) { NSSecureTextField *t; t = [[NSSecureTextField alloc] initWithFrame:NSZeroRect]; return finishNewTextField(toNSTextField(t), YES); } void textfieldSetDelegate(id textfield, void *t) { goControlDelegate *d; d = [goControlDelegate new]; d->gocontrol = t; [toNSTextField(textfield) setDelegate:d]; } // also good for labels const char *textFieldText(id t) { return [[toNSTextField(t) stringValue] UTF8String]; } // also good for labels void textFieldSetText(id t, char *text) { [toNSTextField(t) setStringValue:[NSString stringWithUTF8String:text]]; } id textfieldOpenInvalidPopover(id textfield, char *reason) { // step 1: set up the display NSTextField *label; NSTextAttachmentCell *cell; NSTextAttachment *attachment; NSAttributedString *strImage; NSAttributedString *strText; NSFont *font; NSMutableAttributedString *str; // method thanks to Anne in http://stackoverflow.com/a/5303517/3408572 // TODO improve appearance label = toNSTextField(newLabel()); cell = [[NSTextAttachmentCell alloc] initImageCell:[NSImage imageNamed:NSImageNameCaution]]; attachment = [NSTextAttachment new]; [attachment setAttachmentCell:cell]; strImage = [NSAttributedString attributedStringWithAttachment:attachment]; font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]; strText = [[NSAttributedString alloc] initWithString:[NSString stringWithUTF8String:reason] attributes:[[font fontDescriptor] fontAttributes]]; str = [[NSMutableAttributedString alloc] initWithAttributedString:strImage]; [str appendAttributedString:strText]; [label setAttributedStringValue:str]; // step 2: set up the popover NSPopover *popover; NSViewController *vc; vc = [NSViewController new]; [vc setView:label]; popover = [NSPopover new]; [popover setContentViewController:vc]; [label sizeToFit]; [popover setContentSize:[label frame].size]; // step 3: show the popover // NSMaxYEdge is the bottom edge when looking (maximum edge in window coordinates) [popover showRelativeToRect:NSZeroRect ofView:toNSView(textfield) preferredEdge:NSMaxYEdge]; return (id) popover; } void textfieldCloseInvalidPopover(id popover) { [toNSPopover(popover) close]; [toNSPopover(popover) release]; } id newLabel(void) { NSTextField *l; l = [[NSTextField alloc] initWithFrame:NSZeroRect]; [l setEditable:NO]; [l setSelectable:NO]; [l setDrawsBackground:NO]; return finishNewTextField(l, NO); } id newGroup(id container) { NSBox *group; group = [[NSBox alloc] initWithFrame:NSZeroRect]; [group setBorderType:NSLineBorder]; [group setBoxType:NSBoxPrimary]; [group setTransparent:NO]; // can't use setSmallControlFont() here because the selector is different [group setTitleFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; [group setTitlePosition:NSAtTop]; [group setContentView:toNSView(container)]; return (id) group; } const char *groupText(id group) { return [[toNSBox(group) title] UTF8String]; } void groupSetText(id group, char *text) { [toNSBox(group) setTitle:[NSString stringWithUTF8String:text]]; }