Switched from NSPopover for the TextField.Invalid() warning on Mac OS X to a custom window subclass.

This commit is contained in:
Pietro Gagliardi 2014-08-27 12:11:55 -04:00
parent 83f423a43e
commit a9961feb2c
4 changed files with 65 additions and 13 deletions

View File

@ -7,7 +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 toNSWindow(x) ((NSWindow *) (x))
#define toNSBox(x) ((NSBox *) (x)) #define toNSBox(x) ((NSBox *) (x))
@interface goControlDelegate : NSObject <NSTextFieldDelegate> { @interface goControlDelegate : NSObject <NSTextFieldDelegate> {
@ -168,18 +168,18 @@ void textFieldSetText(id t, char *text)
id textfieldOpenInvalidPopover(id textfield, char *reason) id textfieldOpenInvalidPopover(id textfield, char *reason)
{ {
NSPopover *popover; id popover;
popover = (NSPopover *) newWarningPopover(reason); popover = newWarningPopover(reason);
[popover showRelativeToRect:NSZeroRect ofView:toNSView(textfield) preferredEdge:NSMaxYEdge]; warningPopoverShow(popover, textfield);
NSBeep(); NSBeep();
return (id) popover; return (id) popover;
} }
void textfieldCloseInvalidPopover(id popover) void textfieldCloseInvalidPopover(id popover)
{ {
[toNSPopover(popover) close]; [toNSWindow(popover) orderOut:toNSWindow(popover)];
[toNSPopover(popover) release]; [toNSWindow(popover) release];
} }
id newLabel(void) id newLabel(void)

View File

@ -145,5 +145,6 @@ extern void openFile(id, void *);
/* warningpopover_darwin.m */ /* warningpopover_darwin.m */
extern id newWarningPopover(char *); extern id newWarningPopover(char *);
extern void warningPopoverShow(id, id);
#endif #endif

View File

@ -48,7 +48,6 @@ func (t *textfield) OnChanged(f func()) {
func (t *textfield) Invalid(reason string) { func (t *textfield) Invalid(reason string) {
// TODO disable animations if reason is still valid // TODO disable animations if reason is still valid
// TODO don't steal focus
if t.invalid != nil { if t.invalid != nil {
C.textfieldCloseInvalidPopover(t.invalid) C.textfieldCloseInvalidPopover(t.invalid)
t.invalid = nil t.invalid = nil

View File

@ -3,6 +3,48 @@
#include "objc_darwin.h" #include "objc_darwin.h"
#include <Cocoa/Cocoa.h> #include <Cocoa/Cocoa.h>
// We would be able to just use plain old NSPopover here, but alas that steals focus.
// NSPopovers are intended for interactive content, and Apple seems to be diligent in enforcing this rule, as the known techniques for preventing a NSPopover from stealing focus no longer work in 10.9.
// Let's just fake it with a window.
// TODO
// - doesn't get hidden properly when asked to order out
// - doesn't get hidden when changing first responders
// - doesn't get hidden when switching between programs/shown again
// - doesn't animate or have a transparent background; probably should
@interface goWarningPopover : NSWindow
@end
@implementation goWarningPopover
- (id)init
{
self = [super initWithContentRect:NSZeroRect
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:YES];
[self setOpaque:NO];
// [self setAlphaValue:0.1];
[self setHasShadow:YES];
[self setExcludedFromWindowsMenu:YES];
[self setMovableByWindowBackground:NO];
[self setLevel:NSPopUpMenuWindowLevel];
return self;
}
- (BOOL)canBecomeKeyWindow
{
return NO;
}
- (BOOL)canBecomeMainWindow
{
return NO;
}
@end
@interface goWarningView : NSView { @interface goWarningView : NSView {
@public @public
NSImageView *icon; NSImageView *icon;
@ -65,14 +107,24 @@ id newWarningPopover(char *text)
[wv addSubview:wv->label]; [wv addSubview:wv->label];
[wv sizeToFitAndArrange]; [wv sizeToFitAndArrange];
NSPopover *popover; goWarningPopover *popover;
NSViewController *vc;
vc = [NSViewController new]; popover = [[goWarningPopover alloc] init]; // explicitly use our initializer
[vc setView:wv]; [[popover contentView] addSubview:wv];
popover = [NSPopover new];
[popover setContentViewController:vc];
[popover setContentSize:[wv frame].size]; [popover setContentSize:[wv frame].size];
return (id) popover; return (id) popover;
} }
void warningPopoverShow(id popover, id control)
{
goWarningPopover *p = (goWarningPopover *) popover;
NSView *v = (NSView *) control;
NSRect vr;
NSPoint vo;
vr = [v convertRect:[v frame] toView:nil];
vo = [[v window] convertRectToScreen:vr].origin;
[p setFrameOrigin:NSMakePoint(vo.x, vo.y - [p frame].size.height)];
[p orderFront:p];
}