26 @global CPTextFieldDidFocusNotification
27 @global CPTextFieldDidBlurNotification
31 var CPTokenFieldDOMInputElement = nil,
32 CPTokenFieldDOMPasswordInputElement = nil,
33 CPTokenFieldDOMStandardInputElement = nil,
34 CPTokenFieldInputOwner = nil,
35 CPTokenFieldTextDidChangeValue = nil,
36 CPTokenFieldInputResigning = NO,
37 CPTokenFieldInputDidBlur = NO,
38 CPTokenFieldInputIsActive = NO,
39 CPTokenFieldCachedSelectStartFunction = nil,
40 CPTokenFieldCachedDragFunction = nil,
41 CPTokenFieldFocusInput = NO,
43 CPTokenFieldBlurHandler = nil;
59 CPRange _selectedRange;
61 _CPAutocompleteMenu _autocompleteMenu;
64 CPTimeInterval _completionDelay;
70 BOOL _shouldNotifyTarget;
80 + (CPTimeInterval)defaultCompletionDelay
92 return @{
@"editor-inset": CGInsetMakeZero() };
95 - (id)initWithFrame:(CGRect)frame
97 if (
self = [super initWithFrame:
frame])
99 _completionDelay = [[
self class] defaultCompletionDelay];
100 _tokenizingCharacterSet = [[
self class] defaultTokenizingCharacterSet];
102 [
self setBezeled:YES];
108 [
self setNeedsLayout];
118 var
frame = [
self frame];
120 _tokenScrollView = [[
CPScrollView alloc] initWithFrame:CGRectMakeZero()];
121 [_tokenScrollView setHasHorizontalScroller:NO];
122 [_tokenScrollView setHasVerticalScroller:NO];
123 [_tokenScrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
125 var contentView = [[
CPView alloc] initWithFrame:CGRectMakeZero()];
126 [contentView setAutoresizingMask:CPViewWidthSizable];
127 [_tokenScrollView setDocumentView:contentView];
129 [
self addSubview:_tokenScrollView];
132 - (_CPAutocompleteMenu)_autocompleteMenu
134 if (!_autocompleteMenu)
135 _autocompleteMenu = [[_CPAutocompleteMenu alloc] initWithTextField:self];
136 return _autocompleteMenu;
139 - (void)_complete:(_CPAutocompleteMenu)anAutocompleteMenu
141 [
self _autocompleteWithEvent:nil];
144 - (void)_autocompleteWithEvent:(
CPEvent)anEvent
146 if (![
self _editorValue] && (![_autocompleteMenu contentArray] || ![
self hasThemeState:CPThemeStateAutocompleting]))
149 [
self _hideCompletions];
151 var token = [_autocompleteMenu selectedItem],
152 shouldRemoveLastObject = token !==
@"" && [
self _editorValue] !==
@"";
155 token = [
self _editorValue];
160 if (!token || token ===
@"")
162 var character = [anEvent charactersIgnoringModifiers],
163 modifierFlags = [anEvent modifierFlags];
168 [[
self window] selectNextKeyView:self];
170 [[
self window] selectPreviousKeyView:self];
173 [[
self window] makeFirstResponder:nil];
177 var objectValue = [
self objectValue];
181 if (shouldRemoveLastObject)
182 [objectValue removeObjectAtIndex:_selectedRange.location];
185 token = [
self _representedObjectForEditingString:token];
188 var delegateApprovedObjects = [
self _shouldAddObjects:[CPArray arrayWithObject:token] atIndex:_selectedRange.location],
189 delegateApprovedObjectsCount = [delegateApprovedObjects count];
191 if (delegateApprovedObjects)
193 for (var i = 0; i < delegateApprovedObjectsCount; i++)
195 [objectValue insertObject:[delegateApprovedObjects objectAtIndex:i] atIndex:_selectedRange.location + i];
200 var location = _selectedRange.location;
202 [
self setObjectValue:objectValue];
204 if (delegateApprovedObjectsCount)
205 location += delegateApprovedObjectsCount;
208 [
self _inputElement].value =
@"";
209 [
self setNeedsLayout];
211 [
self _controlTextDidChange];
214 - (void)_autocomplete
216 [
self _autocompleteWithEvent:nil];
219 - (void)_selectToken:(_CPTokenFieldToken)token byExtendingSelection:(BOOL)extend
221 var indexOfToken = [[
self _tokens] indexOfObject:token];
226 _selectedRange =
CPMakeRange([[
self _tokens] count], 0);
233 [
self setNeedsLayout];
236 - (void)_deselectToken:(_CPTokenFieldToken)token
238 var indexOfToken = [[
self _tokens] indexOfObject:token];
241 _selectedRange =
CPMakeRange(MAX(indexOfToken, _selectedRange.location), MIN(_selectedRange.length, indexOfToken - _selectedRange.location));
243 [
self setNeedsLayout];
246 - (void)_deleteToken:(_CPTokenFieldToken)token
248 var indexOfToken = [[
self _tokens] indexOfObject:token],
249 objectValue = [
self objectValue];
253 if (indexOfToken < _selectedRange.location)
254 _selectedRange.location--;
256 [
self _deselectToken:token];
261 [objectValue removeObjectAtIndex:indexOfToken];
262 [
self setObjectValue:objectValue];
263 _selectedRange = selection;
265 [
self setNeedsLayout];
266 [
self _controlTextDidChange];
269 - (void)_controlTextDidChange
271 var binderClass = [[
self class] _binderClassForBinding:CPValueBinding],
272 theBinding = [binderClass getBinding:CPValueBinding forObject:self];
275 [theBinding reverseSetValueFor:@"objectValue"];
279 _shouldNotifyTarget = YES;
282 - (void)_removeSelectedTokens:(
id)sender
284 var tokens = [
self objectValue];
286 for (var i = _selectedRange.length - 1; i >= 0; i--)
287 [tokens removeObjectAtIndex:_selectedRange.location + i];
289 var collapsedSelection = _selectedRange.location;
291 [
self setObjectValue:tokens];
294 _selectedRange =
CPMakeRange(collapsedSelection, 0);
296 [
self _controlTextDidChange];
299 - (void)_updatePlaceholderState
301 if (([[
self _tokens] count] === 0) && ![
self hasThemeState:CPThemeStateEditing])
302 [
self setThemeState:CPTextFieldStatePlaceholder];
304 [
self unsetThemeState:CPTextFieldStatePlaceholder];
311 - (BOOL)becomeFirstResponder
314 if (CPTokenFieldInputOwner && [CPTokenFieldInputOwner window] !== [
self window])
315 [[CPTokenFieldInputOwner window] makeFirstResponder:nil];
319 [
self _setObserveWindowKeyNotifications:YES];
321 [
self scrollRectToVisible:[
self bounds]];
323 if ([[
self window] isKeyWindow])
324 return [
self _becomeFirstKeyResponder];
329 - (BOOL)_becomeFirstKeyResponder
333 if (![
self _isWithinUsablePlatformRect])
336 [
self setThemeState:CPThemeStateEditing];
338 [
self _updatePlaceholderState];
340 [
self setNeedsLayout];
344 var
string = [
self stringValue],
345 element = [
self _inputElement],
346 font = [
self currentValueForThemeAttribute:@"font"];
349 element.style.color = [[
self currentValueForThemeAttribute:@"text-color"] cssString];
350 element.style.font = [font cssString];
351 element.style.zIndex = 1000;
353 switch ([
self alignment])
356 element.style.textAlign =
"center";
360 element.style.textAlign =
"right";
364 element.style.textAlign =
"left";
367 var contentRect = [
self contentRectForBounds:[
self bounds]];
369 element.style.top = CGRectGetMinY(contentRect) + "px";
370 element.style.left = (CGRectGetMinX(contentRect) - 1) + "px";
371 element.style.width = CGRectGetWidth(contentRect) + "px";
372 element.style.height = [font defaultLineHeightForFont] + "px";
374 window.setTimeout(
function()
376 [_tokenScrollView documentView]._DOMElement.appendChild(element);
381 window.setTimeout(
function()
384 CPTokenFieldInputOwner =
self;
390 [[[
self window] platformWindow] _propagateCurrentDOMEvent:YES];
392 CPTokenFieldInputIsActive = YES;
394 if (document.attachEvent)
396 CPTokenFieldCachedSelectStartFunction = document.body.onselectstart;
397 CPTokenFieldCachedDragFunction = document.body.ondrag;
399 document.body.ondrag =
function () {};
400 document.body.onselectstart =
function () {};
408 - (BOOL)resignFirstResponder
410 [
self _autocomplete];
413 [
self _setObserveWindowKeyNotifications:NO];
415 [
self _resignFirstKeyResponder];
417 if (_shouldNotifyTarget)
419 _shouldNotifyTarget = NO;
422 if ([
self sendsActionOnEndEditing])
431 - (void)_resignFirstKeyResponder
433 [
self unsetThemeState:CPThemeStateEditing];
435 [
self _updatePlaceholderState];
436 [
self setNeedsLayout];
440 var element = [
self _inputElement];
442 CPTokenFieldInputResigning = YES;
445 if (!CPTokenFieldInputDidBlur)
446 CPTokenFieldBlurHandler();
448 CPTokenFieldInputDidBlur = NO;
449 CPTokenFieldInputResigning = NO;
451 if (element.parentNode == [_tokenScrollView documentView]._DOMElement)
452 element.parentNode.removeChild(element);
454 CPTokenFieldInputIsActive = NO;
456 if (document.attachEvent)
458 CPTokenFieldCachedSelectStartFunction = nil;
459 CPTokenFieldCachedDragFunction = nil;
461 document.body.ondrag = CPTokenFieldCachedDragFunction
462 document.body.onselectstart = CPTokenFieldCachedSelectStartFunction
470 _mouseDownEvent = anEvent;
472 [
self _selectToken:nil byExtendingSelection:NO];
479 _mouseDownEvent = nil;
482 - (void)_mouseDownOnToken:(_CPTokenFieldToken)aToken withEvent:(
CPEvent)anEvent
484 _mouseDownEvent = anEvent;
487 - (void)_mouseUpOnToken:(_CPTokenFieldToken)aToken withEvent:(
CPEvent)anEvent
489 if (_mouseDownEvent && CGPointEqualToPoint([_mouseDownEvent locationInWindow], [anEvent locationInWindow]))
491 [
self _selectToken:aToken byExtendingSelection:[anEvent modifierFlags] & CPShiftKeyMask];
492 [[
self window] makeFirstResponder:self];
494 _shouldScrollTo = aToken;
505 return [
super objectValue];
510 return [[
self objectValue] componentsJoinedByString:@","];
515 var objectValue = [];
517 for (var i = 0, count = [[
self _tokens] count]; i < count; i++)
519 var token = [[
self _tokens] objectAtIndex:i];
521 if ([token isKindOfClass:[
CPString class]])
524 [objectValue addObject:[token representedObject]];
529 if ([
self _editorValue])
531 var token = [
self _representedObjectForEditingString:[
self _editorValue]];
532 [objectValue insertObject:token atIndex:_selectedRange.location];
540 - (void)setObjectValue:(
id)aValue
542 if (aValue !== nil && ![aValue isKindOfClass:[CPArray
class]])
549 if (aValue === superValue || [aValue isEqualToArray:superValue])
552 var contentView = [_tokenScrollView documentView],
553 oldTokens = [
self _tokens],
559 for (var i = 0, count = [aValue count]; i < count; i++)
562 var tokenObject = aValue[i],
563 tokenValue = [
self _displayStringForRepresentedObject:tokenObject],
566 for (var j = 0, oldCount = [oldTokens count]; j < oldCount; j++)
568 var oldToken = oldTokens[j];
569 if ([oldToken representedObject] == tokenObject)
572 [oldTokens removeObjectAtIndex:j];
578 if (newToken === nil)
580 newToken = [_CPTokenFieldToken new];
581 [newToken setTokenField:self];
582 [newToken setRepresentedObject:tokenObject];
583 [newToken setStringValue:tokenValue];
584 [newToken setEditable:[
self isEditable]];
585 [contentView addSubview:newToken];
588 newTokens.push(newToken);
593 for (var j = 0, oldCount = [oldTokens count]; j < oldCount; j++)
594 [oldTokens[j] removeFromSuperview];
607 [
self _selectToken:nil byExtendingSelection:NO];
609 [
self _updatePlaceholderState];
612 [
self setNeedsLayout];
613 [
self setNeedsDisplay:YES];
616 - (void)setEnabled:(BOOL)shouldBeEnabled
621 for (var i = 0, count = [[
self _tokens] count]; i < count; i++)
623 var token = [[
self _tokens] objectAtIndex:i];
625 if ([token respondsToSelector:
@selector(setEnabled:)])
626 [token setEnabled:shouldBeEnabled];
630 - (void)setEditable:(BOOL)shouldBeEditable
632 [
super setEditable:shouldBeEditable];
634 [[
self _tokens] makeObjectsPerformSelector:@selector(setEditable:) withObject:shouldBeEditable];
637 - (BOOL)sendAction:(
SEL)anAction to:(
id)anObject
639 _shouldNotifyTarget = NO;
645 - (BOOL)_setStringValue:(
CPString)aValue
653 - (DOMElement)_inputElement
655 if (!CPTokenFieldDOMInputElement)
657 CPTokenFieldDOMInputElement = document.createElement(
"input");
658 CPTokenFieldDOMInputElement.style.position =
"absolute";
659 CPTokenFieldDOMInputElement.style.border =
"0px";
660 CPTokenFieldDOMInputElement.style.padding =
"0px";
661 CPTokenFieldDOMInputElement.style.margin =
"0px";
662 CPTokenFieldDOMInputElement.style.whiteSpace =
"pre";
663 CPTokenFieldDOMInputElement.style.background =
"transparent";
664 CPTokenFieldDOMInputElement.style.outline =
"none";
666 CPTokenFieldBlurHandler =
function(anEvent)
668 return CPTextFieldBlurFunction(
670 CPTokenFieldInputOwner,
671 CPTokenFieldInputOwner ? [CPTokenFieldInputOwner._tokenScrollView documentView]._DOMElement : nil,
672 CPTokenFieldDOMInputElement,
673 CPTokenFieldInputResigning,
674 @ref(CPTokenFieldInputDidBlur));
678 CPTokenFieldDOMInputElement.onblur = CPTokenFieldBlurHandler;
680 CPTokenFieldDOMStandardInputElement = CPTokenFieldDOMInputElement;
685 if ([CPTokenFieldInputOwner isSecure])
686 CPTokenFieldDOMInputElement.type =
"password";
688 CPTokenFieldDOMInputElement.type =
"text";
690 return CPTokenFieldDOMInputElement;
693 return CPTokenFieldDOMInputElement;
699 if (![
self hasThemeState:CPThemeStateEditing])
701 return [
self _inputElement].value;
704 - (void)moveUp:(
id)sender
706 [[
self _autocompleteMenu] selectPrevious];
707 [[[
self window] platformWindow] _propagateCurrentDOMEvent:NO];
710 - (void)moveDown:(
id)sender
712 [[
self _autocompleteMenu] selectNext];
713 [[[
self window] platformWindow] _propagateCurrentDOMEvent:NO];
716 - (void)insertNewline:(
id)sender
718 if ([
self hasThemeState:CPThemeStateAutocompleting])
720 [
self _autocompleteWithEvent:[CPApp currentEvent]];
725 [[
self window] makeFirstResponder:nil];
729 - (void)insertTab:(
id)sender
731 var anEvent = [CPApp currentEvent];
732 if ([
self hasThemeState:CPThemeStateAutocompleting])
734 [
self _autocompleteWithEvent:anEvent];
740 [[
self window] selectNextKeyView:self];
742 [[
self window] selectPreviousKeyView:self];
751 if ([_tokenizingCharacterSet characterIsMember:[characters substringToIndex:1]])
753 [
self _autocompleteWithEvent:[CPApp currentEvent]];
758 if (_selectedRange.length)
760 [
self _removeSelectedTokens:self];
771 [[[
self window] platformWindow] _propagateCurrentDOMEvent:YES];
775 - (void)cancelOperation:(
id)sender
777 [
self _hideCompletions];
780 - (void)moveLeft:(
id)sender
783 if ((_selectedRange.location > 0 || _selectedRange.length) && [
self _editorValue] ==
"")
785 if (_selectedRange.length)
787 _selectedRange.length = 0;
789 _selectedRange.location--;
790 [
self setNeedsLayout];
796 [[[
self window] platformWindow] _propagateCurrentDOMEvent:YES];
800 - (void)moveLeftAndModifySelection:(
id)sender
802 if (_selectedRange.location > 0 && [
self _editorValue] ==
"")
804 _selectedRange.location--;
806 _selectedRange.length++;
807 [
self setNeedsLayout];
813 [[[
self window] platformWindow] _propagateCurrentDOMEvent:YES];
817 - (void)moveRight:(
id)sender
820 if ((_selectedRange.location < [[
self _tokens] count] || _selectedRange.length) && [
self _editorValue] ==
"")
822 if (_selectedRange.length)
825 _selectedRange.location =
CPMaxRange(_selectedRange);
826 _selectedRange.length = 0;
831 _selectedRange.location = MIN([[
self _tokens] count], _selectedRange.location + _selectedRange.length + 1);
834 [
self setNeedsLayout];
840 [[[
self window] platformWindow] _propagateCurrentDOMEvent:YES];
844 - (void)moveRightAndModifySelection:(
id)sender
846 if (
CPMaxRange(_selectedRange) < [[
self _tokens] count] && [
self _editorValue] ==
"")
849 _selectedRange.length++;
850 [
self setNeedsLayout];
856 [[[
self window] platformWindow] _propagateCurrentDOMEvent:YES];
860 - (void)deleteBackward:(
id)sender
864 if ([
self _editorValue] ==
@"")
866 [
self _hideCompletions];
870 if (_selectedRange.location > 0)
872 var tokenView = [[
self _tokens] objectAtIndex:(_selectedRange.location - 1)];
873 [
self _selectToken:tokenView byExtendingSelection:NO];
877 [
self _removeSelectedTokens:nil];
882 [[[
self window] platformWindow] _propagateCurrentDOMEvent:YES];
886 - (void)deleteForward:(
id)sender
890 if ([
self _editorValue] ==
@"")
893 [
self _hideCompletions];
897 if (_selectedRange.location < [[
self _tokens] count])
898 [
self _deleteToken:[[
self _tokens] objectAtIndex:[_selectedRange.location]]];
901 [
self _removeSelectedTokens:nil];
906 [[[
self window] platformWindow] _propagateCurrentDOMEvent:YES];
910 - (void)_selectText:(
id)sender immediately:(BOOL)immediately
919 if (([
self isEditable] || [
self isSelectable]))
921 [
super _selectText:sender immediately:immediately];
924 [
self _autocomplete];
925 _selectedRange =
CPMakeRange(0, [[
self _tokens] count]);
927 [
self setNeedsLayout];
934 CPTokenFieldTextDidChangeValue = [
self stringValue];
942 [
self interpretKeyEvents:[anEvent]];
950 if ([
self stringValue] !== CPTokenFieldTextDidChangeValue)
956 [[[
self window] platformWindow] _propagateCurrentDOMEvent:YES];
961 if ([aNotification
object] !==
self)
967 [
self _delayedShowCompletions];
969 _selectedRange.length = 0;
972 [
self setNeedsLayout];
982 - (void)setCompletionDelay:(CPTimeInterval)delay
984 _completionDelay = delay;
987 - (CPTimeInterval)completionDelay
989 return _completionDelay;
995 - (void)layoutSubviews
997 [
super layoutSubviews];
999 [_tokenScrollView setFrame:[
self rectForEphemeralSubviewNamed:"content-view"]];
1001 var textFieldContentView = [
self layoutEphemeralSubviewNamed:@"content-view"
1002 positioned:CPWindowAbove
1003 relativeToEphemeralSubviewNamed:@"bezel-view"];
1005 if (textFieldContentView)
1006 [textFieldContentView setHidden:[
self stringValue] !== @""];
1008 var
frame = [
self frame],
1009 contentView = [_tokenScrollView documentView],
1010 tokens = [
self _tokens];
1013 if (![tokens isKindOfClass:[CPArray
class]])
1017 var contentRect = CGRectMakeCopy([contentView bounds]),
1018 contentOrigin = contentRect.origin,
1019 contentSize = contentRect.size,
1020 offset = CGPointMake(contentOrigin.x, contentOrigin.y),
1021 spaceBetweenTokens = CGSizeMake(2.0, 2.0),
1022 isEditing = [[
self window] firstResponder] ==
self,
1023 tokenToken = [_CPTokenFieldToken new],
1024 font = [
self currentValueForThemeAttribute:@"font"],
1026 editorInset = [
self currentValueForThemeAttribute:@"editor-inset"];
1029 offset.y += CEIL(spaceBetweenTokens.height / 2.0);
1032 [tokenToken sizeToFit];
1034 var tokenHeight = CGRectGetHeight([tokenToken bounds]);
1036 var fitAndFrame =
function(
width, height)
1038 var r = CGRectMake(0, 0,
width, height);
1040 if (offset.x +
width >= contentSize.width && offset.x > contentOrigin.x)
1042 offset.x = contentOrigin.x;
1043 offset.y += height + spaceBetweenTokens.height;
1046 r.origin.x = offset.x;
1047 r.origin.y = offset.y;
1050 var scrollHeight = offset.y + tokenHeight + CEIL(spaceBetweenTokens.height / 2.0);
1051 if (CGRectGetHeight([contentView bounds]) < scrollHeight)
1052 [contentView setFrameSize:CGSizeMake(CGRectGetWidth([_tokenScrollView bounds]), scrollHeight)];
1054 offset.x +=
width + spaceBetweenTokens.width;
1059 var placeEditor =
function(useRemainingWidth)
1061 var element = [
self _inputElement],
1064 if (_selectedRange.length === 0)
1069 textWidth = [(element.value || @"") + "X" sizeWithFont:font].width;
1071 if (useRemainingWidth)
1072 textWidth = MAX(contentSize.width - offset.x - 1, textWidth);
1075 _inputFrame = fitAndFrame(textWidth, tokenHeight);
1077 _inputFrame.size.height = lineHeight;
1079 element.style.left = (_inputFrame.origin.x + editorInset.left) +
"px";
1080 element.style.top = (_inputFrame.origin.y + editorInset.top) +
"px";
1081 element.style.width = _inputFrame.size.width +
"px";
1082 element.style.height = _inputFrame.size.height +
"px";
1085 if (_selectedRange.length == 0)
1086 [[_tokenScrollView documentView] scrollPoint:CGPointMake(0, _inputFrame.origin.y)];
1089 for (var i = 0, count = [tokens count]; i < count; i++)
1091 if (isEditing && !_selectedRange.length && i ==
CPMaxRange(_selectedRange))
1094 var tokenView = [tokens objectAtIndex:i];
1097 if ([tokenView isKindOfClass:[
CPString class]])
1100 [tokenView setHighlighted:CPLocationInRange(i, _selectedRange)];
1101 [tokenView sizeToFit];
1103 var size = [contentView bounds].size,
1104 tokenViewSize = [tokenView bounds].size,
1105 tokenFrame = fitAndFrame(tokenViewSize.width, tokenViewSize.height);
1107 [tokenView setFrame:tokenFrame];
1109 [tokenView setButtonType:_buttonType];
1112 if (isEditing && !_selectedRange.length &&
CPMaxRange(_selectedRange) >= [tokens count])
1117 if (isEditing && _selectedRange.length)
1120 var inputElement = [
self _inputElement];
1121 inputElement.style.display =
"none";
1125 var inputElement = [
self _inputElement];
1126 inputElement.style.display =
"block";
1127 if (document.activeElement !== inputElement)
1128 inputElement.focus();
1132 var scrollHeight = offset.y + tokenHeight;
1133 if (CGRectGetHeight([contentView bounds]) > scrollHeight)
1134 [contentView setFrameSize:CGSizeMake(CGRectGetWidth([_tokenScrollView bounds]), scrollHeight)];
1139 if (!(isEditing && _selectedRange.length == 0))
1141 var scrollToToken = _shouldScrollTo;
1144 scrollToToken = tokens[_selectedRange.location]
1146 scrollToToken = tokens[MAX(0, CPMaxRange(_selectedRange) - 1)];
1147 [
self _scrollTokenViewToVisible:scrollToToken];
1154 - (BOOL)_scrollTokenViewToVisible:(_CPTokenFieldToken)aToken
1159 return [[_tokenScrollView documentView] scrollPoint:CGPointMake(0, [aToken frameOrigin].y)];
1175 - (CPArray)_completionsForSubstring:(
CPString)substring indexOfToken:(
int)tokenIndex indexOfSelectedItem:(
int)selectedIndex
1177 if ([[
self delegate] respondsToSelector:
@selector(tokenField:completionsForSubstring:indexOfToken:indexOfSelectedItem:)])
1179 return [[
self delegate] tokenField:self completionsForSubstring:substring indexOfToken:tokenIndex indexOfSelectedItem:selectedIndex];
1188 - (CGPoint)_completionOrigin:(_CPAutocompleteMenu)anAutocompleteMenu
1190 var relativeFrame = _inputFrame ? [[_tokenScrollView documentView] convertRect:_inputFrame toView:self ] : [
self bounds];
1191 return CGPointMake(CGRectGetMinX(relativeFrame), CGRectGetMaxY(relativeFrame));
1202 - (
CPString)_displayStringForRepresentedObject:(
id)representedObject
1204 if ([[
self delegate] respondsToSelector:
@selector(tokenField:displayStringForRepresentedObject:)])
1206 var stringForRepresentedObject = [[
self delegate] tokenField:self displayStringForRepresentedObject:representedObject];
1207 if (stringForRepresentedObject !== nil)
1209 return stringForRepresentedObject;
1213 return representedObject;
1225 - (CPArray)_shouldAddObjects:(CPArray)tokens atIndex:(
int)index
1227 var delegate = [
self delegate];
1228 if ([delegate respondsToSelector:
@selector(tokenField:shouldAddObjects:atIndex:)])
1230 var approvedObjects = [delegate tokenField:self shouldAddObjects:tokens atIndex:index];
1231 if (approvedObjects !== nil)
1232 return approvedObjects;
1247 - (id)_representedObjectForEditingString:(
CPString)aString
1249 var delegate = [
self delegate];
1250 if ([delegate respondsToSelector:
@selector(tokenField:representedObjectForEditingString:)])
1252 var token = [delegate tokenField:self representedObjectForEditingString:aString];
1253 if (token !== nil && token !== undefined)
1262 - (BOOL)_hasMenuForRepresentedObject:(
id)aRepresentedObject
1264 var delegate = [
self delegate];
1265 if ([delegate respondsToSelector:
@selector(tokenField:hasMenuForRepresentedObject:)] &&
1266 [delegate respondsToSelector:
@selector(tokenField:menuForRepresentedObject:)])
1267 return [delegate tokenField:self hasMenuForRepresentedObject:aRepresentedObject];
1272 - (
CPMenu)_menuForRepresentedObject:(
id)aRepresentedObject
1274 var delegate = [
self delegate];
1275 if ([delegate respondsToSelector:
@selector(tokenField:hasMenuForRepresentedObject:)] &&
1276 [delegate respondsToSelector:
@selector(tokenField:menuForRepresentedObject:)])
1278 var hasMenu = [delegate tokenField:self hasMenuForRepresentedObject:aRepresentedObject];
1280 return [delegate tokenField:self menuForRepresentedObject:aRepresentedObject] || nil;
1300 - (void)_delayedShowCompletions
1302 [[
self _autocompleteMenu] _delayedShowCompletions];
1305 - (void)_hideCompletions
1307 [_autocompleteMenu _hideCompletions];
1311 - (void)setButtonType:(
int)aButtonType
1313 if (_buttonType === aButtonType)
1316 _buttonType = aButtonType;
1317 [
self setNeedsLayout];
1324 _CPTokenFieldTokenCloseButton _deleteButton;
1325 _CPTokenFieldTokenDisclosureButton _disclosureButton;
1327 id _representedObject;
1333 return "tokenfield-token";
1336 - (BOOL)acceptsFirstResponder
1341 - (id)initWithFrame:(CGRect)frame
1343 if (
self = [super initWithFrame:frame])
1345 [
self setEditable:NO];
1346 [
self setHighlighted:NO];
1347 [
self setBezeled:YES];
1348 [
self setButtonType:CPTokenFieldDisclosureButtonType];
1361 _tokenField = tokenField;
1364 - (id)representedObject
1366 return _representedObject;
1369 - (void)setRepresentedObject:(
id)representedObject
1371 _representedObject = representedObject;
1372 [
self setNeedsLayout];
1375 - (void)setEditable:(BOOL)shouldBeEditable
1377 [
super setEditable:shouldBeEditable];
1378 [
self setNeedsLayout];
1381 - (BOOL)setThemeState:(CPThemeState)aState
1383 var r = [
super setThemeState:aState];
1386 if (aState & CPThemeStateHovered)
1388 [_disclosureButton setThemeState:CPThemeStateHovered];
1389 [_deleteButton setThemeState:CPThemeStateHovered];
1395 - (BOOL)unsetThemeState:(CPThemeState)aState
1397 var r = [
super unsetThemeState:aState];
1400 if (aState & CPThemeStateHovered)
1402 [_disclosureButton unsetThemeState:CPThemeStateHovered];
1403 [_deleteButton unsetThemeState:CPThemeStateHovered];
1409 - (CGSize)_minimumFrameSize
1411 var size = CGSizeMakeZero(),
1412 minSize = [
self currentValueForThemeAttribute:@"min-size"],
1413 contentInset = [
self currentValueForThemeAttribute:@"content-inset"];
1416 size.height = minSize.height;
1417 size.width = MAX(minSize.width, [([
self stringValue] ||
@" ") sizeWithFont:[
self font]].width + contentInset.left + contentInset.right);
1422 - (void)setButtonType:(
int)aButtonType
1424 if (_buttonType === aButtonType)
1427 _buttonType = aButtonType;
1433 [_deleteButton removeFromSuperview];
1434 _deleteButton = nil;
1437 if (!_disclosureButton)
1439 _disclosureButton = [[_CPTokenFieldTokenDisclosureButton alloc] initWithFrame:CGRectMakeZero()];
1440 [
self addSubview:_disclosureButton];
1445 if (_disclosureButton)
1447 [_disclosureButton removeFromSuperview];
1448 _disclosureButton = nil;
1453 _deleteButton = [[_CPTokenFieldTokenCloseButton alloc] initWithFrame:CGRectMakeZero()];
1454 [
self addSubview:_deleteButton];
1455 [_deleteButton setTarget:self];
1456 [_deleteButton setAction:@selector(_delete:)];
1460 [
self setNeedsLayout];
1463 - (void)layoutSubviews
1465 [
super layoutSubviews];
1467 var bezelView = [
self layoutEphemeralSubviewNamed:@"bezel-view"
1468 positioned:CPWindowBelow
1469 relativeToEphemeralSubviewNamed:@"content-view"];
1471 if (bezelView && _tokenField)
1473 switch (_buttonType)
1476 var shouldBeEnabled = [
self hasMenu];
1477 [_disclosureButton setHidden:!shouldBeEnabled];
1479 if (shouldBeEnabled)
1480 [_disclosureButton setMenu:[
self menu]];
1482 var
frame = [bezelView frame],
1483 buttonOffset = [_disclosureButton currentValueForThemeAttribute:@"offset"],
1484 buttonSize = [_disclosureButton currentValueForThemeAttribute:@"min-size"];
1486 [_disclosureButton setFrame:CGRectMake(CGRectGetMaxX(frame) - buttonOffset.x, CGRectGetMinY(frame) + buttonOffset.y, buttonSize.width, buttonSize.height)];
1489 [_deleteButton setEnabled:[
self isEditable] && [
self isEnabled]];
1491 var frame = [bezelView frame],
1492 buttonOffset = [_deleteButton currentValueForThemeAttribute:@"offset"],
1493 buttonSize = [_deleteButton currentValueForThemeAttribute:@"min-size"];
1495 [_deleteButton setFrame:CGRectMake(CGRectGetMaxX(frame) - buttonOffset.x, CGRectGetMinY(frame) + buttonOffset.y, buttonSize.width, buttonSize.height)];
1501 - (void)mouseDown:(
CPEvent)anEvent
1503 [_tokenField _mouseDownOnToken:self withEvent:anEvent];
1506 - (void)mouseUp:(
CPEvent)anEvent
1508 [_tokenField _mouseUpOnToken:self withEvent:anEvent];
1511 - (void)_delete:(
id)sender
1513 if ([
self isEditable])
1514 [_tokenField _deleteToken:self];
1519 return [_tokenField _hasMenuForRepresentedObject:_representedObject];
1524 return [_tokenField _menuForRepresentedObject:_representedObject];
1528 @implementation _CPTokenFieldTokenCloseButton :
CPButton
1537 [attributes setObject:CGPointMake(15, 5) forKey:@"offset"];
1544 return "tokenfield-token-close-button";
1547 - (void)mouseEntered:(
CPEvent)anEvent
1552 - (void)mouseExited:(
CPEvent)anEvent
1558 @implementation _CPTokenFieldTokenDisclosureButton :
CPPopUpButton
1567 [attributes setObject:CGPointMake(15, 5) forKey:@"offset"];
1574 return "tokenfield-token-disclosure-button";
1577 - (id)initWithFrame:(CGRect)aFrame
1579 if (
self = [
self initWithFrame:aFrame pullsDown:YES])
1581 [
self setBordered:YES];
1582 [
super setTitle:@""];
1593 - (void)synchronizeTitleAndSelectedItem
1598 - (void)mouseEntered:(
CPEvent)anEvent
1603 - (void)mouseExited:(
CPEvent)anEvent
1623 _tokenizingCharacterSet = [aCoder decodeObjectForKey:CPTokenFieldTokenizingCharacterSetKey] || [[
self class] defaultTokenizingCharacterSet];
1624 _completionDelay = [aCoder decodeDoubleForKey:CPTokenFieldCompletionDelayKey] || [[
self class] defaultCompletionDelay];
1629 [
self setNeedsLayout];
1630 [
self setNeedsDisplay:YES];
1640 [aCoder encodeInt:_tokenizingCharacterSet forKey:CPTokenFieldTokenizingCharacterSetKey];
1641 [aCoder encodeDouble:_completionDelay forKey:CPTokenFieldCompletionDelayKey];
1642 [aCoder encodeInt:_buttonType forKey:CPTokenFieldButtonTypeKey];
1654 return _tokenizingCharacterSet;
1662 _tokenizingCharacterSet = aValue;
1676 - (void)setButtonType:(
int)aValue
1678 _buttonType = aValue;