diff --git a/AppKit/CPTokenField.j b/AppKit/CPTokenField.j index 092cbc87a..f5894a010 100755 --- a/AppKit/CPTokenField.j +++ b/AppKit/CPTokenField.j @@ -27,13 +27,14 @@ @import @import +@import "_CPAutocompleteMenu.j" @import "CPButton.j" +@import "CPPopUpButton.j" @import "CPScrollView.j" @import "CPTableView.j" @import "CPText.j" @import "CPTextField.j" @import "CPWindow_Constants.j" -@import "_CPAutocompleteMenu.j" @global CPApp @@ -59,6 +60,9 @@ var CPScrollDestinationNone = 0, CPScrollDestinationLeft = 1, CPScrollDestinationRight = 2; +CPTokenFieldDisclosureButtonType = 0; +CPTokenFieldDeleteButtonType = 1; + @implementation CPTokenField : CPTextField { CPScrollView _tokenScrollView; @@ -76,6 +80,8 @@ var CPScrollDestinationNone = 0, CPEvent _mouseDownEvent; BOOL _shouldNotifyTarget; + + int _buttonType @accessors(property=buttonType); } + (CPCharacterSet)defaultTokenizingCharacterSet @@ -104,6 +110,7 @@ var CPScrollDestinationNone = 0, { _completionDelay = [[self class] defaultCompletionDelay]; _tokenizingCharacterSet = [[self class] defaultTokenizingCharacterSet]; + _buttonType = CPTokenFieldDisclosureButtonType; [self setBezeled:YES]; [self _init]; @@ -561,7 +568,7 @@ var CPScrollDestinationNone = 0, if (newToken === nil) { - newToken = [[_CPTokenFieldToken alloc] init]; + newToken = [_CPTokenFieldToken new]; [newToken setTokenField:self]; [newToken setRepresentedObject:tokenObject]; [newToken setStringValue:tokenValue]; @@ -1089,6 +1096,8 @@ var CPScrollDestinationNone = 0, tokenFrame = fitAndFrame(tokenViewSize.width, tokenViewSize.height); [tokenView setFrame:tokenFrame]; + + [tokenView setButtonType:_buttonType]; } if (isEditing && !_selectedRange.length && _CPMaxRange(_selectedRange) >= [tokens count]) @@ -1241,6 +1250,30 @@ var CPScrollDestinationNone = 0, return aString; } +- (BOOL)_hasMenuForRepresentedObject:(id)aRepresentedObject +{ + var delegate = [self delegate]; + if ([delegate respondsToSelector:@selector(tokenField:hasMenuForRepresentedObject:)] && + [delegate respondsToSelector:@selector(tokenField:menuForRepresentedObject:)]) + return [delegate tokenField:self hasMenuForRepresentedObject:aRepresentedObject]; + + return NO; +} + +- (CPMenu)_menuForRepresentedObject:(id)aRepresentedObject +{ + var delegate = [self delegate]; + if ([delegate respondsToSelector:@selector(tokenField:hasMenuForRepresentedObject:)] && + [delegate respondsToSelector:@selector(tokenField:menuForRepresentedObject:)]) + { + var hasMenu = [delegate tokenField:self hasMenuForRepresentedObject:aRepresentedObject]; + if (hasMenu) + return [delegate tokenField:self menuForRepresentedObject:aRepresentedObject] || nil; + } + + return nil; +} + // We put the string on the pasteboard before calling this delegate method. // By default, we write the NSStringPboardType as well as an array of NSStrings. // - (BOOL)tokenField:(NSTokenField *)tokenField writeRepresentedObjects:(NSArray *)objects toPasteboard:(NSPasteboard *)pboard; @@ -1265,13 +1298,25 @@ var CPScrollDestinationNone = 0, [_autocompleteMenu _hideCompletions]; } + +- (void)setButtonType:(int)aButtonType +{ + if (_buttonType === aButtonType) + return; + + _buttonType = aButtonType; + [self setNeedsLayout]; +} + @end @implementation _CPTokenFieldToken : CPTextField { - _CPTokenFieldTokenCloseButton _deleteButton; - CPTokenField _tokenField; - id _representedObject; + _CPTokenFieldTokenCloseButton _deleteButton; + _CPTokenFieldTokenDisclosureButton _disclosureButton; + CPTokenField _tokenField; + id _representedObject; + int _buttonType; } + (CPString)defaultThemeClass @@ -1288,12 +1333,10 @@ var CPScrollDestinationNone = 0, { if (self = [super initWithFrame:frame]) { - _deleteButton = [[_CPTokenFieldTokenCloseButton alloc] initWithFrame:CGRectMakeZero()]; - [self addSubview:_deleteButton]; - [self setEditable:NO]; [self setHighlighted:NO]; [self setBezeled:YES]; + [self setButtonType:CPTokenFieldDisclosureButtonType]; } return self; @@ -1317,6 +1360,7 @@ var CPScrollDestinationNone = 0, - (void)setRepresentedObject:(id)representedObject { _representedObject = representedObject; + [self setNeedsLayout]; } - (void)setEditable:(BOOL)shouldBeEditable @@ -1329,9 +1373,12 @@ var CPScrollDestinationNone = 0, { var r = [super setThemeState:aState]; - // Share hover state with the delete button. - if (r && aState === CPThemeStateHovered) - [_deleteButton setThemeState:aState]; + // Share hover state with the disclosure and delete buttons. + if (aState & CPThemeStateHovered) + { + [_disclosureButton setThemeState:CPThemeStateHovered]; + [_deleteButton setThemeState:CPThemeStateHovered]; + } return r; } @@ -1340,9 +1387,12 @@ var CPScrollDestinationNone = 0, { var r = [super unsetThemeState:aState]; - // Share hover state with the delete button. - if (r && aState === CPThemeStateHovered) - [_deleteButton unsetThemeState:aState]; + // Share hover state with the disclosure and delete button. + if (aState & CPThemeStateHovered) + { + [_disclosureButton unsetThemeState:CPThemeStateHovered]; + [_deleteButton unsetThemeState:CPThemeStateHovered]; + } return r; } @@ -1360,6 +1410,47 @@ var CPScrollDestinationNone = 0, return size; } +- (void)setButtonType:(int)aButtonType +{ + if (_buttonType === aButtonType) + return; + + _buttonType = aButtonType; + + if (_buttonType === CPTokenFieldDisclosureButtonType) + { + if (_deleteButton) + { + [_deleteButton removeFromSuperview]; + _deleteButton = nil; + } + + if (!_disclosureButton) + { + _disclosureButton = [[_CPTokenFieldTokenDisclosureButton alloc] initWithFrame:CGRectMakeZero()]; + [self addSubview:_disclosureButton]; + } + } + else + { + if (_disclosureButton) + { + [_disclosureButton removeFromSuperview]; + _disclosureButton = nil; + } + + if (!_deleteButton) + { + _deleteButton = [[_CPTokenFieldTokenCloseButton alloc] initWithFrame:CGRectMakeZero()]; + [self addSubview:_deleteButton]; + [_deleteButton setTarget:self]; + [_deleteButton setAction:@selector(_delete:)]; + } + } + + [self setNeedsLayout]; +} + - (void)layoutSubviews { [super layoutSubviews]; @@ -1368,17 +1459,33 @@ var CPScrollDestinationNone = 0, positioned:CPWindowBelow relativeToEphemeralSubviewNamed:@"content-view"]; - if (bezelView) + if (bezelView && _tokenField) { - [_deleteButton setTarget:self]; - [_deleteButton setAction:@selector(_delete:)]; - [_deleteButton setEnabled:[self isEditable]]; + switch (_buttonType) + { + case CPTokenFieldDisclosureButtonType: + var shouldBeEnabled = [self hasMenu]; + [_disclosureButton setHidden:!shouldBeEnabled]; - var frame = [bezelView frame], - buttonOffset = [_deleteButton currentValueForThemeAttribute:@"offset"], - buttonSize = [_deleteButton currentValueForThemeAttribute:@"min-size"]; + if (shouldBeEnabled) + [_disclosureButton setMenu:[self menu]]; - [_deleteButton setFrame:_CGRectMake(CGRectGetMaxX(frame) - buttonOffset.x, CGRectGetMinY(frame) + buttonOffset.y, buttonSize.width, buttonSize.height)]; + var frame = [bezelView frame], + buttonOffset = [_disclosureButton currentValueForThemeAttribute:@"offset"], + buttonSize = [_disclosureButton currentValueForThemeAttribute:@"min-size"]; + + [_disclosureButton setFrame:_CGRectMake(CGRectGetMaxX(frame) - buttonOffset.x, CGRectGetMinY(frame) + buttonOffset.y, buttonSize.width, buttonSize.height)]; + break; + case CPTokenFieldDeleteButtonType: + [_deleteButton setEnabled:[self isEditable] && [self isEnabled]]; + + var frame = [bezelView frame], + buttonOffset = [_deleteButton currentValueForThemeAttribute:@"offset"], + buttonSize = [_deleteButton currentValueForThemeAttribute:@"min-size"]; + + [_deleteButton setFrame:_CGRectMake(CGRectGetMaxX(frame) - buttonOffset.x, CGRectGetMinY(frame) + buttonOffset.y, buttonSize.width, buttonSize.height)]; + break; + } } } @@ -1398,11 +1505,18 @@ var CPScrollDestinationNone = 0, [_tokenField _deleteToken:self]; } +- (BOOL)hasMenu +{ + return [_tokenField _hasMenuForRepresentedObject:_representedObject]; +} + +- (CPMenu)menu +{ + return [_tokenField _menuForRepresentedObject:_representedObject]; +} + @end -/* - Theming hook. -*/ @implementation _CPTokenFieldTokenCloseButton : CPButton { } @@ -1421,11 +1535,73 @@ var CPScrollDestinationNone = 0, return "tokenfield-token-close-button"; } +- (void)mouseEntered:(CPEvent)anEvent +{ + // Don't toggle hover state from within the button - we use the hover state of the token field as a whole. +} + +- (void)mouseExited:(CPEvent)anEvent +{ + // Don't toggle hover state from within the button - we use the hover state of the token field as a whole. +} + +@end + +@implementation _CPTokenFieldTokenDisclosureButton : CPPopUpButton +{ +} + ++ (id)themeAttributes +{ + var attributes = [CPButton themeAttributes]; + + [attributes setObject:_CGPointMake(15, 5) forKey:@"offset"]; + + return attributes; +} + ++ (CPString)defaultThemeClass +{ + return "tokenfield-token-disclosure-button"; +} + +- (id)initWithFrame:(CGRect)aFrame +{ + if (self = [self initWithFrame:aFrame pullsDown:YES]) + { + [self setBordered:YES]; + [super setTitle:@""]; + } + + return self; +} + +- (void)setTitle:(CPString)aTitle +{ + // skip +} + +- (void)synchronizeTitleAndSelectedItem +{ + // skip +} + +- (void)mouseEntered:(CPEvent)anEvent +{ + // Don't toggle hover state from within the button - we use the hover state of the token field as a whole. +} + +- (void)mouseExited:(CPEvent)anEvent +{ + // Don't toggle hover state from within the button - we use the hover state of the token field as a whole. +} + @end var CPTokenFieldTokenizingCharacterSetKey = "CPTokenFieldTokenizingCharacterSetKey", - CPTokenFieldCompletionDelayKey = "CPTokenFieldCompletionDelay"; + CPTokenFieldCompletionDelayKey = "CPTokenFieldCompletionDelay", + CPTokenFieldButtonTypeKey = "CPTokenFieldButtonTypeKey"; @implementation CPTokenField (CPCoding) @@ -1437,6 +1613,7 @@ var CPTokenFieldTokenizingCharacterSetKey = "CPTokenFieldTokenizingCharacterSe { _tokenizingCharacterSet = [aCoder decodeObjectForKey:CPTokenFieldTokenizingCharacterSetKey] || [[self class] defaultTokenizingCharacterSet]; _completionDelay = [aCoder decodeDoubleForKey:CPTokenFieldCompletionDelayKey] || [[self class] defaultCompletionDelay]; + _buttonType = [aCoder decodeIntForKey:CPTokenFieldButtonTypeKey] || CPTokenFieldDisclosureButtonType; [self _init]; @@ -1453,6 +1630,7 @@ var CPTokenFieldTokenizingCharacterSetKey = "CPTokenFieldTokenizingCharacterSe [aCoder encodeInt:_tokenizingCharacterSet forKey:CPTokenFieldTokenizingCharacterSetKey]; [aCoder encodeDouble:_completionDelay forKey:CPTokenFieldCompletionDelayKey]; + [aCoder encodeInt:_buttonType forKey:CPTokenFieldButtonTypeKey]; } @end diff --git a/AppKit/Themes/Aristo/Resources/token-disclosure-highlighted.png b/AppKit/Themes/Aristo/Resources/token-disclosure-highlighted.png new file mode 100644 index 000000000..1c05a511d Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/token-disclosure-highlighted.png differ diff --git a/AppKit/Themes/Aristo/Resources/token-disclosure.png b/AppKit/Themes/Aristo/Resources/token-disclosure.png new file mode 100644 index 000000000..09ef8ef92 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/token-disclosure.png differ diff --git a/AppKit/Themes/Aristo/ThemeDescriptors.j b/AppKit/Themes/Aristo/ThemeDescriptors.j index c3c63d853..203073b1f 100755 --- a/AppKit/Themes/Aristo/ThemeDescriptors.j +++ b/AppKit/Themes/Aristo/ThemeDescriptors.j @@ -1174,6 +1174,32 @@ var themedButtonValues = nil, return token; } ++ (_CPTokenFieldTokenDisclosureButton)themedTokenFieldDisclosureButton +{ + var button = [[_CPTokenFieldTokenDisclosureButton alloc] initWithFrame:CGRectMake(0, 0, 9, 9)], + + arrowImage = PatternColor("token-disclosure.png", 7.0, 6.0), + arrowImageHiglighted = PatternColor("token-disclosure-highlighted.png", 7.0, 6.0), + + themeValues = + [ + [@"content-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateNormal], + + [@"bezel-color", nil, CPThemeStateBordered], + [@"bezel-color", arrowImage, CPThemeStateBordered | CPThemeStateHovered], + [@"bezel-color", arrowImageHiglighted, CPThemeStateBordered | CPThemeStateHovered | CPThemeStateHighlighted], + + [@"min-size", CGSizeMake(7.0, 6.0)], + [@"max-size", CGSizeMake(7.0, 6.0)], + + [@"offset", CGPointMake(17, 7)] + ]; + + [self registerThemeValues:themeValues forView:button]; + + return button; +} + + (_CPTokenFieldTokenCloseButton)themedTokenFieldTokenCloseButton { var button = [[_CPTokenFieldTokenCloseButton alloc] initWithFrame:CGRectMake(0, 0, 9, 9)], diff --git a/AppKit/Themes/Aristo2/Resources/token-disclosure-highlighted.png b/AppKit/Themes/Aristo2/Resources/token-disclosure-highlighted.png new file mode 100644 index 000000000..1c05a511d Binary files /dev/null and b/AppKit/Themes/Aristo2/Resources/token-disclosure-highlighted.png differ diff --git a/AppKit/Themes/Aristo2/Resources/token-disclosure.png b/AppKit/Themes/Aristo2/Resources/token-disclosure.png new file mode 100644 index 000000000..09ef8ef92 Binary files /dev/null and b/AppKit/Themes/Aristo2/Resources/token-disclosure.png differ diff --git a/AppKit/Themes/Aristo2/ThemeDescriptors.j b/AppKit/Themes/Aristo2/ThemeDescriptors.j index 71234cc63..6590a2197 100644 --- a/AppKit/Themes/Aristo2/ThemeDescriptors.j +++ b/AppKit/Themes/Aristo2/ThemeDescriptors.j @@ -709,7 +709,7 @@ var themedButtonValues = nil, [@"text-color", textDisabledColor, CPThemeStateDisabled], [@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBezeled], - [@"content-inset", CGInsetMake(1.0, 24.0, 2.0, 16.0), CPThemeStateBezeled], + [@"content-inset", CGInsetMake(1.0, 20.0, 2.0, 20.0), CPThemeStateBezeled], // Minimum height == maximum height since tokens are fixed height. [@"min-size", CGSizeMake(0.0, 19.0)], @@ -723,6 +723,32 @@ var themedButtonValues = nil, return token; } ++ (_CPTokenFieldTokenDisclosureButton)themedTokenFieldDisclosureButton +{ + var button = [[_CPTokenFieldTokenDisclosureButton alloc] initWithFrame:CGRectMake(0, 0, 9, 9)], + + arrowImage = PatternColor("token-disclosure.png", 7.0, 6.0), + arrowImageHiglighted = PatternColor("token-disclosure-highlighted.png", 7.0, 6.0), + + themeValues = + [ + [@"content-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateNormal], + + [@"bezel-color", nil, CPThemeStateBordered], + [@"bezel-color", arrowImage, CPThemeStateBordered | CPThemeStateHovered], + [@"bezel-color", arrowImageHiglighted, CPThemeStateBordered | CPThemeStateHovered | CPThemeStateHighlighted], + + [@"min-size", CGSizeMake(7.0, 6.0)], + [@"max-size", CGSizeMake(7.0, 6.0)], + + [@"offset", CGPointMake(16, 7)] + ]; + + [self registerThemeValues:themeValues forView:button]; + + return button; +} + + (_CPTokenFieldTokenCloseButton)themedTokenFieldTokenCloseButton { var button = [[_CPTokenFieldTokenCloseButton alloc] initWithFrame:CGRectMake(0, 0, 9, 9)], @@ -732,16 +758,17 @@ var themedButtonValues = nil, themeValues = [ - [@"bezel-color", bezelColor, CPThemeStateBordered], - [@"bezel-color", bezelHighlightedColor, CPThemeStateBordered | CPThemeStateHighlighted], + [@"bezel-color", bezelColor, CPThemeStateBordered | CPThemeStateHovered], + [@"bezel-color", [bezelColor colorWithAlphaComponent:0], CPThemeStateBordered | CPThemeStateDisabled], + [@"bezel-color", bezelHighlightedColor, CPThemeStateBordered | CPThemeStateHighlighted], [@"min-size", CGSizeMake(8.0, 8.0)], [@"max-size", CGSizeMake(8.0, 8.0)], - [@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBordered], - [@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBordered | CPThemeStateHighlighted], + [@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBordered], + [@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBordered | CPThemeStateHighlighted], - [@"offset", CGPointMake(17, 6), CPThemeStateBordered] + [@"offset", CGPointMake(16, 6), CPThemeStateBordered] ]; [self registerThemeValues:themeValues forView:button]; diff --git a/AppKit/_CPAutocompleteMenu.j b/AppKit/_CPAutocompleteMenu.j index a58071ea0..ed7349181 100644 --- a/AppKit/_CPAutocompleteMenu.j +++ b/AppKit/_CPAutocompleteMenu.j @@ -115,8 +115,13 @@ var _CPAutocompleteMenuMaximumHeight = 307; - (void)setIndexOfSelectedItem:(int)anIndex { - [tableView selectRowIndexes:[CPIndexSet indexSetWithIndex:anIndex] byExtendingSelection:NO]; - [tableView scrollRowToVisible:anIndex]; + if (anIndex == CPNotFound) + [tableView selectRowIndexes:[CPIndexSet indexSet] byExtendingSelection:NO]; + else + { + [tableView selectRowIndexes:[CPIndexSet indexSetWithIndex:anIndex] byExtendingSelection:NO]; + [tableView scrollRowToVisible:anIndex]; + } } - (int)indexOfSelectedItem diff --git a/Tests/Manual/CPTokenFieldTest/AppController.j b/Tests/Manual/CPTokenFieldTest/AppController.j index a50e8d46d..55050d116 100644 --- a/Tests/Manual/CPTokenFieldTest/AppController.j +++ b/Tests/Manual/CPTokenFieldTest/AppController.j @@ -88,10 +88,23 @@ var STATES = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorad [tokenFieldD setDelegate:self]; var tokenFieldDEditable = [CPCheckBox checkBoxWithTitle:"Editable"]; - [tokenFieldDEditable setFrame:CGRectMake(525, CGRectGetMinY([tokenFieldD frame]), 200, 30)]; + [tokenFieldDEditable sizeToFit]; + [tokenFieldDEditable setFrameOrigin:CGPointMake(525, 5 + CGRectGetMinY([tokenFieldD frame]))]; [tokenFieldDEditable bind:CPValueBinding toObject:tokenFieldD withKeyPath:@"editable" options:nil]; [contentView addSubview:tokenFieldDEditable]; + var tokenFieldDMenus = [CPCheckBox checkBoxWithTitle:"Menus"]; + [tokenFieldDMenus sizeToFit]; + [tokenFieldDMenus setFrameOrigin:CGPointMake(CGRectGetMaxX([tokenFieldDEditable frame]) + 5, 5 + CGRectGetMinY([tokenFieldD frame]))]; + [tokenFieldDMenus bind:CPValueBinding toObject:self withKeyPath:@"tokenFieldDHasMenus" options:nil]; + [contentView addSubview:tokenFieldDMenus]; + + var tokenFieldDClose = [CPCheckBox checkBoxWithTitle:"Close Buttons"]; + [tokenFieldDClose sizeToFit]; + [tokenFieldDClose setFrameOrigin:CGPointMake(CGRectGetMaxX([tokenFieldDMenus frame]) + 5, 5 + CGRectGetMinY([tokenFieldD frame]))]; + [tokenFieldDClose bind:CPValueBinding toObject:self withKeyPath:@"tokenFieldDHasCloseButtons" options:nil]; + [contentView addSubview:tokenFieldDClose]; + allPersons = [ [Person personWithFirstName:@"Luc" lastName:@"Vauvillier"], [Person personWithFirstName:@"John" lastName:@"Doe"], @@ -113,6 +126,37 @@ var STATES = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorad } +- (void)setTokenFieldDHasMenus:(BOOL)shouldHaveMenus +{ + if (shouldHaveMenus) + { + [[tokenFieldD objectValue] enumerateObjectsUsingBlock:function(aPerson) + { + var menu = [CPMenu new]; + for (var i = 0; i < 3; i++) + [menu addItem:[[CPMenuItem alloc] initWithTitle:[CPString stringWithFormat:@"%@ Item %i", aPerson, i] action:nil keyEquivalent:nil]]; + aPerson.menu = menu; + }]; + } + else + [[tokenFieldD objectValue] makeObjectsPerformSelector:@selector(setMenu:) withObject:nil]; +} + +- (BOOL)tokenFieldDHasMenus +{ + return !![[[tokenFieldD objectValue] firstObject] menu]; +} + +- (void)setTokenFieldDHasCloseButtons:(BOOL)shouldHaveCloseButtons +{ + [tokenFieldD setButtonType:shouldHaveCloseButtons ? CPTokenFieldDeleteButtonType : CPTokenFieldDisclosureButtonType]; +} + +- (BOOL)tokenFieldDHasCloseButtons +{ + return !![tokenFieldD buttonType] === CPTokenFieldDeleteButtonType; +} + - (void)getObjectValues:(id)sender { alert([tokenFieldD objectValue]); @@ -177,6 +221,16 @@ var STATES = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorad return tokens; } +- (BOOL)tokenField:(CPTokenField)aTokenField hasMenuForRepresentedObject:(id)aRepresentedObject +{ + return !!aRepresentedObject.menu; +} + +- (CPMenu)tokenField:(CPTokenField)aTokenField menuForRepresentedObject:(id)aRepresentedObject +{ + return aRepresentedObject.menu; +} + @end // A sample of a custom Object @@ -185,6 +239,8 @@ var STATES = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorad { CPString _firstName; CPString _lastName; + + CPMenu menu @accessors; } + (id)personWithFirstName:(CPString)aFirstName lastName:(CPString)aLastName diff --git a/Tests/Manual/CPTokenFieldTest/Info.plist b/Tests/Manual/CPTokenFieldTest/Info.plist index 19d666d21..90f6c7eff 100644 --- a/Tests/Manual/CPTokenFieldTest/Info.plist +++ b/Tests/Manual/CPTokenFieldTest/Info.plist @@ -4,6 +4,8 @@ CPApplicationDelegateClass AppController + CPDefaultTheme + Aristo2 CPBundleName CPTokenFieldTest CPPrincipalClass