Implemented TextField.Invalid() on Mac OS X.

This commit is contained in:
Pietro Gagliardi 2014-08-20 17:08:47 -04:00
parent 35e788aa7f
commit 8b6b8ec06c
3 changed files with 65 additions and 2 deletions

View File

@ -7,6 +7,7 @@
#define toNSButton(x) ((NSButton *) (x)) #define toNSButton(x) ((NSButton *) (x))
#define toNSTextField(x) ((NSTextField *) (x)) #define toNSTextField(x) ((NSTextField *) (x))
#define toNSView(x) ((NSView *) (x)) #define toNSView(x) ((NSView *) (x))
#define toNSPopover(x) ((NSPopover *) (x))
#define toNSBox(x) ((NSBox *) (x)) #define toNSBox(x) ((NSBox *) (x))
@interface goControlDelegate : NSObject <NSTextFieldDelegate> { @interface goControlDelegate : NSObject <NSTextFieldDelegate> {
@ -162,6 +163,54 @@ void textFieldSetText(id t, char *text)
[toNSTextField(t) setStringValue:[NSString stringWithUTF8String: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) id newLabel(void)
{ {
NSTextField *l; NSTextField *l;

View File

@ -71,6 +71,8 @@ extern id newPasswordField(void);
extern void textfieldSetDelegate(id, void *); extern void textfieldSetDelegate(id, void *);
extern const char *textFieldText(id); extern const char *textFieldText(id);
extern void textFieldSetText(id, char *); extern void textFieldSetText(id, char *);
extern id textfieldOpenInvalidPopover(id, char *);
extern void textfieldCloseInvalidPopover(id);
extern id newLabel(void); extern id newLabel(void);
extern id newGroup(id); extern id newGroup(id);
extern const char *groupText(id); extern const char *groupText(id);

View File

@ -12,6 +12,7 @@ import "C"
type textfield struct { type textfield struct {
_id C.id _id C.id
changed *event changed *event
invalid C.id
} }
func finishNewTextField(id C.id) *textfield { func finishNewTextField(id C.id) *textfield {
@ -24,7 +25,7 @@ func finishNewTextField(id C.id) *textfield {
} }
func newTextField() *textfield { func newTextField() *textfield {
return finishNewTextField(C.newTextField() return finishNewTextField(C.newTextField())
} }
func newPasswordField() *textfield { func newPasswordField() *textfield {
@ -46,7 +47,18 @@ func (t *textfield) OnChanged(f func()) {
} }
func (t *textfield) Invalid(reason string) { func (t *textfield) Invalid(reason string) {
// TODO // TODO disable animations if reason is still valid
// TODO don't steal focus
if t.invalid != nil {
C.textfieldCloseInvalidPopover(t.invalid)
t.invalid = nil
}
if reason == "" {
return
}
creason := C.CString(reason)
defer C.free(unsafe.Pointer(creason))
t.invalid = C.textfieldOpenInvalidPopover(t._id, creason)
} }
//export textfieldChanged //export textfieldChanged