Split uiCombobox on OS X.
This commit is contained in:
parent
996ba99b0f
commit
363916855c
|
@ -7,6 +7,7 @@
|
||||||
#define uiColorButtonSignature 0x436F6C42
|
#define uiColorButtonSignature 0x436F6C42
|
||||||
#define uiComboboxSignature 0x436F6D62
|
#define uiComboboxSignature 0x436F6D62
|
||||||
#define uiDateTimePickerSignature 0x44545069
|
#define uiDateTimePickerSignature 0x44545069
|
||||||
|
#define uiEditableComboboxSignature 0x45644362
|
||||||
#define uiEntrySignature 0x456E7472
|
#define uiEntrySignature 0x456E7472
|
||||||
#define uiFontButtonSignature 0x466F6E42
|
#define uiFontButtonSignature 0x466F6E42
|
||||||
#define uiGroupSignature 0x47727062
|
#define uiGroupSignature 0x47727062
|
||||||
|
|
|
@ -15,6 +15,7 @@ MFILES += \
|
||||||
darwin/debug.m \
|
darwin/debug.m \
|
||||||
darwin/draw.m \
|
darwin/draw.m \
|
||||||
darwin/drawtext.m \
|
darwin/drawtext.m \
|
||||||
|
darwin/editablecombo.m \
|
||||||
darwin/entry.m \
|
darwin/entry.m \
|
||||||
darwin/fontbutton.m \
|
darwin/fontbutton.m \
|
||||||
darwin/group.m \
|
darwin/group.m \
|
||||||
|
|
|
@ -5,37 +5,17 @@
|
||||||
// NSPopUpButton is fine.
|
// NSPopUpButton is fine.
|
||||||
#define comboboxWidth 96
|
#define comboboxWidth 96
|
||||||
|
|
||||||
@interface libui_intrinsicWidthNSComboBox : NSComboBox
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation libui_intrinsicWidthNSComboBox
|
|
||||||
|
|
||||||
- (NSSize)intrinsicContentSize
|
|
||||||
{
|
|
||||||
NSSize s;
|
|
||||||
|
|
||||||
s = [super intrinsicContentSize];
|
|
||||||
s.width = comboboxWidth;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
struct uiCombobox {
|
struct uiCombobox {
|
||||||
uiDarwinControl c;
|
uiDarwinControl c;
|
||||||
BOOL editable;
|
|
||||||
NSPopUpButton *pb;
|
NSPopUpButton *pb;
|
||||||
NSArrayController *pbac;
|
NSArrayController *pbac;
|
||||||
NSComboBox *cb;
|
|
||||||
NSView *handle; // for uiControlHandle()
|
|
||||||
void (*onSelected)(uiCombobox *, void *);
|
void (*onSelected)(uiCombobox *, void *);
|
||||||
void *onSelectedData;
|
void *onSelectedData;
|
||||||
};
|
};
|
||||||
|
|
||||||
@interface comboboxDelegateClass : NSObject<NSComboBoxDelegate> {
|
@interface comboboxDelegateClass : NSObject {
|
||||||
struct mapTable *comboboxes;
|
struct mapTable *comboboxes;
|
||||||
}
|
}
|
||||||
- (void)comboBoxSelectionDidChange:(NSNotification *)note;
|
|
||||||
- (IBAction)onSelected:(id)sender;
|
- (IBAction)onSelected:(id)sender;
|
||||||
- (void)registerCombobox:(uiCombobox *)c;
|
- (void)registerCombobox:(uiCombobox *)c;
|
||||||
- (void)unregisterCombobox:(uiCombobox *)c;
|
- (void)unregisterCombobox:(uiCombobox *)c;
|
||||||
|
@ -57,98 +37,57 @@ struct uiCombobox {
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: does not trigger when text changed
|
|
||||||
// TODO not perfect either:
|
|
||||||
// - triggered when keyboard navigating the open menu
|
|
||||||
// - does not trigger when the text is changed to a menu item (which normally selects that item; IDK how to inhibit that behavior - TODO)
|
|
||||||
- (void)comboBoxSelectionDidChange:(NSNotification *)note
|
|
||||||
{
|
|
||||||
[self onSelected:[note object]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)onSelected:(id)sender
|
- (IBAction)onSelected:(id)sender
|
||||||
{
|
{
|
||||||
uiCombobox *c;
|
uiCombobox *c;
|
||||||
|
|
||||||
c = (uiCombobox *) mapGet(self->comboboxes, sender);
|
c = uiCombobox(mapGet(self->comboboxes, sender));
|
||||||
(*(c->onSelected))(c, c->onSelectedData);
|
(*(c->onSelected))(c, c->onSelectedData);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)registerCombobox:(uiCombobox *)c
|
- (void)registerCombobox:(uiCombobox *)c
|
||||||
{
|
{
|
||||||
mapSet(self->comboboxes, c->handle, c);
|
mapSet(self->comboboxes, c->pb, c);
|
||||||
if (c->editable)
|
|
||||||
[c->cb setDelegate:self];
|
|
||||||
else {
|
|
||||||
[c->pb setTarget:self];
|
[c->pb setTarget:self];
|
||||||
[c->pb setAction:@selector(onSelected:)];
|
[c->pb setAction:@selector(onSelected:)];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)unregisterCombobox:(uiCombobox *)c
|
- (void)unregisterCombobox:(uiCombobox *)c
|
||||||
{
|
{
|
||||||
if (c->editable)
|
|
||||||
[c->cb setDelegate:nil];
|
|
||||||
else
|
|
||||||
[c->pb setTarget:nil];
|
[c->pb setTarget:nil];
|
||||||
mapDelete(self->comboboxes, c->handle);
|
mapDelete(self->comboboxes, c->pb);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
static comboboxDelegateClass *comboboxDelegate = nil;
|
static comboboxDelegateClass *comboboxDelegate = nil;
|
||||||
|
|
||||||
uiDarwinControlAllDefaultsExceptDestroy(uiCombobox, handle)
|
uiDarwinControlAllDefaultsExceptDestroy(uiCombobox, pb)
|
||||||
|
|
||||||
static void uiComboboxDestroy(uiControl *cc)
|
static void uiComboboxDestroy(uiControl *cc)
|
||||||
{
|
{
|
||||||
uiCombobox *c = uiCombobox(cc);
|
uiCombobox *c = uiCombobox(cc);
|
||||||
|
|
||||||
[comboboxDelegate unregisterCombobox:c];
|
[comboboxDelegate unregisterCombobox:c];
|
||||||
if (!c->editable) {
|
|
||||||
[c->pb unbind:@"contentObjects"];
|
[c->pb unbind:@"contentObjects"];
|
||||||
[c->pb unbind:@"selectedIndex"];
|
[c->pb unbind:@"selectedIndex"];
|
||||||
[c->pbac release];
|
[c->pbac release];
|
||||||
}
|
[c->pb release];
|
||||||
[c->handle release];
|
|
||||||
uiFreeControl(uiControl(c));
|
uiFreeControl(uiControl(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
void uiComboboxAppend(uiCombobox *c, const char *text)
|
void uiComboboxAppend(uiCombobox *c, const char *text)
|
||||||
{
|
{
|
||||||
if (c->editable)
|
|
||||||
[c->cb addItemWithObjectValue:toNSString(text)];
|
|
||||||
else
|
|
||||||
[c->pbac addObject:toNSString(text)];
|
[c->pbac addObject:toNSString(text)];
|
||||||
}
|
}
|
||||||
|
|
||||||
intmax_t uiComboboxSelected(uiCombobox *c)
|
intmax_t uiComboboxSelected(uiCombobox *c)
|
||||||
{
|
{
|
||||||
if (c->editable)
|
|
||||||
return [c->cb indexOfSelectedItem];
|
|
||||||
return [c->pb indexOfSelectedItem];
|
return [c->pb indexOfSelectedItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
void uiComboboxSetSelected(uiCombobox *c, intmax_t n)
|
void uiComboboxSetSelected(uiCombobox *c, intmax_t n)
|
||||||
{
|
{
|
||||||
if (c->editable) {
|
|
||||||
// see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ComboBox/Tasks/SettingComboBoxValue.html#//apple_ref/doc/uid/20000256
|
|
||||||
id delegate;
|
|
||||||
|
|
||||||
// this triggers the delegate; turn it off for now
|
|
||||||
delegate = [c->cb delegate];
|
|
||||||
[c->cb setDelegate:nil];
|
|
||||||
|
|
||||||
// this seems to work fine for -1 too
|
|
||||||
[c->cb selectItemAtIndex:n];
|
|
||||||
if (n == -1)
|
|
||||||
[c->cb setObjectValue:@""];
|
|
||||||
else
|
|
||||||
[c->cb setObjectValue:[c->cb objectValueOfSelectedItem]];
|
|
||||||
|
|
||||||
[c->cb setDelegate:delegate];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
[c->pb selectItemAtIndex:n];
|
[c->pb selectItemAtIndex:n];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,29 +102,18 @@ static void defaultOnSelected(uiCombobox *c, void *data)
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
static uiCombobox *finishNewCombobox(BOOL editable)
|
uiCombobox *uiNewCombobox(void)
|
||||||
{
|
{
|
||||||
uiCombobox *c;
|
uiCombobox *c;
|
||||||
|
NSPopUpButtonCell *pbcell;
|
||||||
|
|
||||||
uiDarwinNewControl(uiCombobox, c);
|
uiDarwinNewControl(uiCombobox, c);
|
||||||
|
|
||||||
c->editable = editable;
|
|
||||||
if (c->editable) {
|
|
||||||
c->cb = [[libui_intrinsicWidthNSComboBox alloc] initWithFrame:NSZeroRect];
|
|
||||||
[c->cb setUsesDataSource:NO];
|
|
||||||
[c->cb setButtonBordered:YES];
|
|
||||||
[c->cb setCompletes:NO];
|
|
||||||
uiDarwinSetControlFont(c->cb, NSRegularControlSize);
|
|
||||||
c->handle = c->cb;
|
|
||||||
} else {
|
|
||||||
NSPopUpButtonCell *pbcell;
|
|
||||||
|
|
||||||
c->pb = [[NSPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO];
|
c->pb = [[NSPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO];
|
||||||
[c->pb setPreferredEdge:NSMinYEdge];
|
[c->pb setPreferredEdge:NSMinYEdge];
|
||||||
pbcell = (NSPopUpButtonCell *) [c->pb cell];
|
pbcell = (NSPopUpButtonCell *) [c->pb cell];
|
||||||
[pbcell setArrowPosition:NSPopUpArrowAtBottom];
|
[pbcell setArrowPosition:NSPopUpArrowAtBottom];
|
||||||
// TODO font
|
// TODO font
|
||||||
c->handle = c->pb;
|
|
||||||
|
|
||||||
// NSPopUpButton doesn't work like a combobox
|
// NSPopUpButton doesn't work like a combobox
|
||||||
// - it automatically selects the first item
|
// - it automatically selects the first item
|
||||||
|
@ -203,7 +131,6 @@ static uiCombobox *finishNewCombobox(BOOL editable)
|
||||||
toObject:c->pbac
|
toObject:c->pbac
|
||||||
withKeyPath:@"selectionIndex"
|
withKeyPath:@"selectionIndex"
|
||||||
options:nil];
|
options:nil];
|
||||||
}
|
|
||||||
|
|
||||||
if (comboboxDelegate == nil) {
|
if (comboboxDelegate == nil) {
|
||||||
comboboxDelegate = [comboboxDelegateClass new];
|
comboboxDelegate = [comboboxDelegateClass new];
|
||||||
|
@ -214,13 +141,3 @@ static uiCombobox *finishNewCombobox(BOOL editable)
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
uiCombobox *uiNewCombobox(void)
|
|
||||||
{
|
|
||||||
return finishNewCombobox(NO);
|
|
||||||
}
|
|
||||||
|
|
||||||
uiCombobox *uiNewEditableCombobox(void)
|
|
||||||
{
|
|
||||||
return finishNewCombobox(YES);
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
// 14 august 2015
|
||||||
|
#import "uipriv_darwin.h"
|
||||||
|
|
||||||
|
// NSComboBoxes have no intrinsic width; we'll use the default Interface Builder width for them.
|
||||||
|
#define comboboxWidth 96
|
||||||
|
|
||||||
|
@interface libui_intrinsicWidthNSComboBox : NSComboBox
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation libui_intrinsicWidthNSComboBox
|
||||||
|
|
||||||
|
- (NSSize)intrinsicContentSize
|
||||||
|
{
|
||||||
|
NSSize s;
|
||||||
|
|
||||||
|
s = [super intrinsicContentSize];
|
||||||
|
s.width = comboboxWidth;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
struct uiEditableCombobox {
|
||||||
|
uiDarwinControl c;
|
||||||
|
NSComboBox *cb;
|
||||||
|
void (*onChanged)(uiEditableCombobox *, void *);
|
||||||
|
void *onChangedData;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface editableComboboxDelegateClass : NSObject<NSComboBoxDelegate> {
|
||||||
|
struct mapTable *comboboxes;
|
||||||
|
}
|
||||||
|
- (void)controlTextDidChange:(NSNotification *)note;
|
||||||
|
- (void)comboBoxSelectionDidChange:(NSNotification *)note;
|
||||||
|
- (void)registerCombobox:(uiEditableCombobox *)c;
|
||||||
|
- (void)unregisterCombobox:(uiEditableCombobox *)c;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation editableComboboxDelegateClass
|
||||||
|
|
||||||
|
- (id)init
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self)
|
||||||
|
self->comboboxes = newMap();
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
mapDestroy(self->comboboxes);
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)controlTextDidChange:(NSNotification *)note
|
||||||
|
{
|
||||||
|
uiEditableCombobox *c;
|
||||||
|
|
||||||
|
c = uiEditableCombobox(mapGet(self->comboboxes, [note object]));
|
||||||
|
(*(c->onChanged))(c, c->onChangedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the above doesn't handle when an item is selected; this will
|
||||||
|
- (void)comboBoxSelectionDidChange:(NSNotification *)note
|
||||||
|
{
|
||||||
|
// except this is sent BEFORE the entry is changed, and that doesn't send the above, so
|
||||||
|
// this is via http://stackoverflow.com/a/21059819/3408572 - it avoids the need to manage selected items
|
||||||
|
// this still isn't perfect — I get residual changes to the same value while navigating the list — but it's good enough
|
||||||
|
[self performSelector:@selector(controlTextDidChange:)
|
||||||
|
withObject:note
|
||||||
|
afterDelay:0];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)registerCombobox:(uiEditableCombobox *)c
|
||||||
|
{
|
||||||
|
mapSet(self->comboboxes, c->cb, c);
|
||||||
|
[c->cb setDelegate:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)unregisterCombobox:(uiEditableCombobox *)c
|
||||||
|
{
|
||||||
|
[c->cb setDelegate:nil];
|
||||||
|
mapDelete(self->comboboxes, c->cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
static editableComboboxDelegateClass *comboboxDelegate = nil;
|
||||||
|
|
||||||
|
uiDarwinControlAllDefaultsExceptDestroy(uiEditableCombobox, cb)
|
||||||
|
|
||||||
|
static void uiEditableComboboxDestroy(uiControl *cc)
|
||||||
|
{
|
||||||
|
uiEditableCombobox *c = uiEditableCombobox(cc);
|
||||||
|
|
||||||
|
[comboboxDelegate unregisterCombobox:c];
|
||||||
|
[c->cb release];
|
||||||
|
uiFreeControl(uiControl(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text)
|
||||||
|
{
|
||||||
|
[c->cb addItemWithObjectValue:toNSString(text)];
|
||||||
|
}
|
||||||
|
|
||||||
|
char *uiEditableComboboxText(uiEditableCombobox *c)
|
||||||
|
{
|
||||||
|
return uiDarwinNSStringToText([c->cb stringValue]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text)
|
||||||
|
{
|
||||||
|
NSString *t;
|
||||||
|
|
||||||
|
t = toNSString(text);
|
||||||
|
[c->cb setStringValue:t];
|
||||||
|
// do this just to keep synchronicity with the behavior of the real control
|
||||||
|
// for what it's worth, this automatic behavior when typing is the reason why this is separate from uiCombobox
|
||||||
|
[c->cb selectItemWithObjectValue:t];
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// TODO
|
||||||
|
void uiEditableComboboxSetSelected(uiEditableCombobox *c, intmax_t n)
|
||||||
|
{
|
||||||
|
if (c->editable) {
|
||||||
|
// see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ComboBox/Tasks/SettingComboBoxValue.html#//apple_ref/doc/uid/20000256
|
||||||
|
id delegate;
|
||||||
|
|
||||||
|
// this triggers the delegate; turn it off for now
|
||||||
|
delegate = [c->cb delegate];
|
||||||
|
[c->cb setDelegate:nil];
|
||||||
|
|
||||||
|
// this seems to work fine for -1 too
|
||||||
|
[c->cb selectItemAtIndex:n];
|
||||||
|
if (n == -1)
|
||||||
|
[c->cb setObjectValue:@""];
|
||||||
|
else
|
||||||
|
[c->cb setObjectValue:[c->cb objectValueOfSelectedItem]];
|
||||||
|
|
||||||
|
[c->cb setDelegate:delegate];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[c->pb selectItemAtIndex:n];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void uiEditableComboboxOnChanged(uiEditableCombobox *c, void (*f)(uiEditableCombobox *c, void *data), void *data)
|
||||||
|
{
|
||||||
|
c->onChanged = f;
|
||||||
|
c->onChangedData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void defaultOnChanged(uiEditableCombobox *c, void *data)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
uiEditableCombobox *uiNewEditableCombobox(void)
|
||||||
|
{
|
||||||
|
uiEditableCombobox *c;
|
||||||
|
|
||||||
|
uiDarwinNewControl(uiEditableCombobox, c);
|
||||||
|
|
||||||
|
c->cb = [[libui_intrinsicWidthNSComboBox alloc] initWithFrame:NSZeroRect];
|
||||||
|
[c->cb setUsesDataSource:NO];
|
||||||
|
[c->cb setButtonBordered:YES];
|
||||||
|
[c->cb setCompletes:NO];
|
||||||
|
uiDarwinSetControlFont(c->cb, NSRegularControlSize);
|
||||||
|
|
||||||
|
if (comboboxDelegate == nil) {
|
||||||
|
comboboxDelegate = [editableComboboxDelegateClass new];
|
||||||
|
[delegates addObject:comboboxDelegate];
|
||||||
|
}
|
||||||
|
[comboboxDelegate registerCombobox:c];
|
||||||
|
uiEditableComboboxOnChanged(c, defaultOnChanged, NULL);
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
|
@ -44,6 +44,7 @@ static void onCBChanged(uiCombobox *c, void *data)
|
||||||
printf("%s combobox changed to %d\n",
|
printf("%s combobox changed to %d\n",
|
||||||
(char *) data,
|
(char *) data,
|
||||||
(int) uiComboboxSelected(c));
|
(int) uiComboboxSelected(c));
|
||||||
|
uiEditableComboboxSetText(editable, "changed");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onECBChanged(uiEditableCombobox *c, void *data)
|
static void onECBChanged(uiEditableCombobox *c, void *data)
|
||||||
|
|
2
ui.h
2
ui.h
|
@ -195,7 +195,7 @@ typedef struct uiEditableCombobox uiEditableCombobox;
|
||||||
#define uiEditableCombobox(this) ((uiEditableCombobox *) (this))
|
#define uiEditableCombobox(this) ((uiEditableCombobox *) (this))
|
||||||
_UI_EXTERN void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text);
|
_UI_EXTERN void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text);
|
||||||
_UI_EXTERN char *uiEditableComboboxText(uiEditableCombobox *c);
|
_UI_EXTERN char *uiEditableComboboxText(uiEditableCombobox *c);
|
||||||
_UI_EXTERN void uiEditableComboboxSetText(uiEditableCombobox *c, intmax_t n);
|
_UI_EXTERN void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text);
|
||||||
// TODO what do we call a function that sets the currently selected item and fills the text field with it? editable comboboxes have no consistent concept of selected item
|
// TODO what do we call a function that sets the currently selected item and fills the text field with it? editable comboboxes have no consistent concept of selected item
|
||||||
_UI_EXTERN void uiEditableComboboxOnChanged(uiEditableCombobox *c, void (*f)(uiEditableCombobox *c, void *data), void *data);
|
_UI_EXTERN void uiEditableComboboxOnChanged(uiEditableCombobox *c, void (*f)(uiEditableCombobox *c, void *data), void *data);
|
||||||
_UI_EXTERN uiEditableCombobox *uiNewEditableCombobox(void);
|
_UI_EXTERN uiEditableCombobox *uiNewEditableCombobox(void);
|
||||||
|
|
Loading…
Reference in New Issue