diff --git a/AppKit/CPTabView.j b/AppKit/CPTabView.j index 7a9f86697..31c6d1c51 100644 --- a/AppKit/CPTabView.j +++ b/AppKit/CPTabView.j @@ -48,7 +48,7 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24; CPNumber selectedIndex; CPTabViewType type; - + id delegate; unsigned delegateSelectors; } @@ -59,20 +59,23 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24; if (self) { items = [CPArray array]; - + tabs = [[CPSegmentedControl alloc] initWithFrame:CGRectMake(0, 0, 0, HEIGHT_OF_SEGMENTED_CONTROL)]; [tabs setHitTests:NO]; - + box = [[CPBox alloc] initWithFrame:CGRectMake(0, HEIGHT_OF_SEGMENTED_CONTROL / 2, CGRectGetWidth(aFrame), CGRectGetHeight(aFrame) - HEIGHT_OF_SEGMENTED_CONTROL)]; - + selectedIndex = CPNotFound; - + [self setTabViewType:CPTopTabsBezelBorder]; [self setBackgroundColor:[CPColor colorWithCalibratedWhite:0.95 alpha:1.0]]; - + [self addSubview:box]; [self addSubview:tabs]; + + [box setAutoresizingMask:CPViewWidthSizable]; + [tabs setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin]; } return self; } @@ -96,7 +99,7 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24; - (void)insertTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(unsigned)anIndex { [items insertObject:aTabViewItem atIndex:anIndex]; - + [self _updateItems]; [self _repositionTabs]; @@ -120,7 +123,7 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24; [self _updateItems]; [self _repositionTabs]; - + if (delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector) [delegate tabViewDidChangeNumberOfTabViewItems:self]; } @@ -181,7 +184,7 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24; { if ([items count] === 0) return; // throw? - + [self selectTabViewItemAtIndex:0]; } @@ -193,7 +196,7 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24; { if ([items count] === 0) return; // throw? - + [self selectTabViewItemAtIndex:[items count] - 1]; } @@ -205,12 +208,13 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24; { if (selectedIndex === CPNotFound) return; - + var nextIndex = selectedIndex + 1; - + if (nextIndex === [items count]) - return; // does nothing. According to spec at (http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Reference/ApplicationKit/Classes/NSTabView_Class/Reference/Reference.html#//apple_ref/occ/instm/NSTabView/selectNextTabViewItem:) - + // does nothing. According to spec at (http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Reference/ApplicationKit/Classes/NSTabView_Class/Reference/Reference.html#//apple_ref/occ/instm/NSTabView/selectNextTabViewItem:) + return; + [self selectTabViewItemAtIndex:nextIndex]; } @@ -222,12 +226,12 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24; { if (selectedIndex === CPNotFound) return; - + var previousIndex = selectedIndex - 1; if (previousIndex < 0) return; // does nothing. See above. - + [self selectTabViewItemAtIndex:previousIndex]; } @@ -250,16 +254,18 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24; if (anIndex === selectedIndex) return; - + + var aTabViewItem = [self tabViewItemAtIndex:anIndex]; + if ((delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) && ![delegate tabView:self shouldSelectTabViewItem:aTabViewItem]) return; if (delegateSelectors & CPTabViewWillSelectTabViewItemSelector) [delegate tabView:self willSelectTabViewItem:aTabViewItem]; - + [tabs selectSegmentWithTag:anIndex]; [self _setSelectedIndex:anIndex]; - + if (delegateSelectors & CPTabViewDidSelectTabViewItemSelector) [delegate tabView:self didSelectTabViewItem:aTabViewItem]; } @@ -281,18 +287,19 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24; { if (type === aTabViewType) return; - + if ((type === CPTopTabsBezelBorder || type === CPBottomTabsBezelBorder) && (aTabViewType !== CPTopTabsBezelBorder && aTabViewType !== CPBottomTabsBezelBorder)) [tabs removeFromSuperview]; - + if ((type === CPNoTabsBezelBorder || type === CPNoTabsLineBorder || type === CPNoTabsNoBorder) && (aTabViewType !== CPNoTabsBezelBorder && aTabViewType !== CPNoTabsBezelBorder && aTabViewType !== CPNoTabsNoBorder)) [self addSubview:tabs]; - + type = aTabViewType; - - switch (type) { + + switch (type) + { case CPTopTabsBezelBorder: case CPBottomTabsBezelBorder: case CPNoTabsBezelBorder: @@ -357,7 +364,7 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24; - (void)mouseDown:(CPEvent)anEvent { var segmentIndex = [tabs testSegment:[tabs convertPoint:[anEvent locationInWindow] fromView:nil]]; - + if (segmentIndex != CPNotFound) { [self selectTabViewItemAtIndex:segmentIndex]; @@ -369,7 +376,7 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24; { var horizontalCenterOfSelf = CGRectGetWidth([self bounds]) / 2, verticalCenterOfTabs = CGRectGetHeight([tabs bounds]) / 2; - + if (type === CPBottomTabsBezelBorder) [tabs setCenter:CGPointMake(horizontalCenterOfSelf, CGRectGetHeight([self bounds]) - verticalCenterOfTabs)]; else @@ -379,7 +386,7 @@ var HEIGHT_OF_SEGMENTED_CONTROL = 24; - (void)_setSelectedIndex:(CPNumber)index { selectedIndex = index; - + [box setContentView:[[items objectAtIndex:selectedIndex] view]]; } @@ -432,7 +439,7 @@ var CPTabViewItemsKey = "CPTabViewItemsKey", - (void)encodeWithCoder:(CPCoder)aCoder { [super encodeWithCoder:aCoder]; - + [aCoder encodeObject:items forKey:CPTabViewItemsKey];; [aCoder encodeObject:[self selectedTabViewItem] forKey:CPTabViewSelectedItemKey]; diff --git a/AppKit/CPTokenField.j b/AppKit/CPTokenField.j index 85379894d..70e70c2c0 100755 --- a/AppKit/CPTokenField.j +++ b/AppKit/CPTokenField.j @@ -491,31 +491,48 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting", if (aValue === superValue || [aValue isEqualToArray:superValue]) return; - var objectValue = [aValue copy], - contentView = [_tokenScrollView documentView]; + var contentView = [_tokenScrollView documentView]; - // Because we do not know for sure which tokens are removed we remove them all - for (var i = 0, count = [[self _tokens] count]; i < count; i++) - [[[self _tokens] objectAtIndex:i] removeFromSuperview]; - - objectValue = []; + // Preserve as many existing tokens as possible to reduce redraw flickering. + var oldTokens = [self _tokens], + newTokens = []; if (aValue !== nil) { - // Re-add all tokens for (var i = 0, count = [aValue count]; i < count; i++) { - var token = [aValue objectAtIndex:i], - tokenView = [[_CPTokenFieldToken alloc] init]; + // Do we have this token among the old ones? + var tokenValue = aValue[i], + newToken = nil; - [tokenView setTokenField:self]; - [tokenView setStringValue:token]; - [objectValue addObject:tokenView]; + for (var j = 0, oldCount = [oldTokens count]; j < oldCount; j++) + { + var oldToken = oldTokens[j]; + if ([oldToken stringValue] == tokenValue) + { + // Yep. Reuse it. + [oldTokens removeObjectAtIndex:j]; + newToken = oldToken; + break; + } + } - [contentView addSubview:tokenView]; + if (newToken === nil) + { + newToken = [[_CPTokenFieldToken alloc] init]; + [newToken setTokenField:self]; + [newToken setStringValue:tokenValue]; + [contentView addSubview:newToken]; + } + + newTokens.push(newToken); } } + // Remove any now unused tokens. + for (var j = 0, oldCount = [oldTokens count]; j < oldCount; j++) + [oldTokens[j] removeFromSuperview]; + /* [CPTextField setObjectValue] will try to set the _inputElement.value to the new objectValue, if the _inputElement exists. This is wrong for us @@ -524,7 +541,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting", Instead do what CPControl setObjectValue would. */ - _value = objectValue; + _value = newTokens; // Reset the selection. [self _selectToken:nil byExtendingSelection:NO]; diff --git a/AppKit/Themes/Aristo/Resources/token-center.png b/AppKit/Themes/Aristo/Resources/token-center.png index 3ea58da30..9ec12f972 100644 Binary files a/AppKit/Themes/Aristo/Resources/token-center.png and b/AppKit/Themes/Aristo/Resources/token-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/token-close-highlighted.png b/AppKit/Themes/Aristo/Resources/token-close-highlighted.png index 310b8b993..5ca9f34be 100644 Binary files a/AppKit/Themes/Aristo/Resources/token-close-highlighted.png and b/AppKit/Themes/Aristo/Resources/token-close-highlighted.png differ diff --git a/AppKit/Themes/Aristo/Resources/token-highlighted-center.png b/AppKit/Themes/Aristo/Resources/token-highlighted-center.png index ce60ddbf0..2f2ab1c7e 100644 Binary files a/AppKit/Themes/Aristo/Resources/token-highlighted-center.png and b/AppKit/Themes/Aristo/Resources/token-highlighted-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/token-highlighted-left.png b/AppKit/Themes/Aristo/Resources/token-highlighted-left.png index 30f2cc1b9..60bc6b4b2 100644 Binary files a/AppKit/Themes/Aristo/Resources/token-highlighted-left.png and b/AppKit/Themes/Aristo/Resources/token-highlighted-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/token-highlighted-right.png b/AppKit/Themes/Aristo/Resources/token-highlighted-right.png index 13fc317d6..2aabf627c 100644 Binary files a/AppKit/Themes/Aristo/Resources/token-highlighted-right.png and b/AppKit/Themes/Aristo/Resources/token-highlighted-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/token-left.png b/AppKit/Themes/Aristo/Resources/token-left.png index 036c89d13..00cf12196 100644 Binary files a/AppKit/Themes/Aristo/Resources/token-left.png and b/AppKit/Themes/Aristo/Resources/token-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/token-right.png b/AppKit/Themes/Aristo/Resources/token-right.png index 177b39316..da8582333 100644 Binary files a/AppKit/Themes/Aristo/Resources/token-right.png and b/AppKit/Themes/Aristo/Resources/token-right.png differ diff --git a/AppKit/Themes/Aristo/ThemeDescriptors.j b/AppKit/Themes/Aristo/ThemeDescriptors.j index 70a4b2413..606fc40ae 100755 --- a/AppKit/Themes/Aristo/ThemeDescriptors.j +++ b/AppKit/Themes/Aristo/ThemeDescriptors.j @@ -824,8 +824,8 @@ var themedButtonValues = nil, [@"content-inset", CGInsetMake(7.0, 0.0, 4.0, 0.0)], // Placeholder is displayed as regular text, not tokens; requires a different inset. [@"content-inset", CGInsetMake(9.0, 0.0, 5.0, 2.0), CPTextFieldStatePlaceholder], - [@"content-inset", CGInsetMake(7.0, 5.0, 4.0, 6.0), CPThemeStateBezeled], - [@"content-inset", CGInsetMake(9.0, 7.0, 5.0, 8.0), CPThemeStateBezeled | CPTextFieldStatePlaceholder], + [@"content-inset", CGInsetMake(7.0, 5.0, 5.0, 6.0), CPThemeStateBezeled], + [@"content-inset", CGInsetMake(9.0, 7.0, 6.0, 8.0), CPThemeStateBezeled | CPTextFieldStatePlaceholder], ]; [self registerThemeValues:overrides forView:tokenfield inherit:themedTextFieldValues]; @@ -835,21 +835,21 @@ var themedButtonValues = nil, + (_CPTokenFieldToken)themedTokenFieldToken { - var token = [[_CPTokenFieldToken alloc] initWithFrame:CGRectMake(0.0, 0.0, 60.0, 19.0)], + var token = [[_CPTokenFieldToken alloc] initWithFrame:CGRectMake(0.0, 0.0, 60.0, 18.0)], bezelColor = PatternColor( [ - ["token-left.png", 11.0, 19.0], - ["token-center.png", 1.0, 19.0], - ["token-right.png", 11.0, 19.0] + ["token-left.png", 11.0, 18.0], + ["token-center.png", 1.0, 18.0], + ["token-right.png", 11.0, 18.0] ], PatternIsHorizontal), bezelHighlightedColor = PatternColor( [ - ["token-highlighted-left.png", 11.0, 19.0], - ["token-highlighted-center.png", 1.0, 19.0], - ["token-highlighted-right.png", 11.0, 19.0] + ["token-highlighted-left.png", 11.0, 18.0], + ["token-highlighted-center.png", 1.0, 18.0], + ["token-highlighted-right.png", 11.0, 18.0] ], PatternIsHorizontal), @@ -865,11 +865,11 @@ var themedButtonValues = nil, [@"text-color", textHighlightedColor, CPThemeStateHighlighted], [@"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(2.0, 24.0, 2.0, 16.0), CPThemeStateBezeled], // Minimum height == maximum height since tokens are fixed height. - [@"min-size", CGSizeMake(0.0, 19.0)], - [@"max-size", CGSizeMake(-1.0, 19.0)], + [@"min-size", CGSizeMake(0.0, 18.0)], + [@"max-size", CGSizeMake(-1.0, 18.0)], [@"vertical-alignment", CPCenterTextAlignment], ]; diff --git a/Tests/AppKit/CPTabViewTest.j b/Tests/AppKit/CPTabViewTest.j new file mode 100644 index 000000000..9bba62b40 --- /dev/null +++ b/Tests/AppKit/CPTabViewTest.j @@ -0,0 +1,65 @@ +@import +@import + + + +@implementation CPTabView (TEST) + +- (CPSegmentedControl)tabs +{ + return tabs; +} + +@end + + +@implementation CPTabViewTest : OJTestCase +{ + CPTabView _tableView; + CPTabViewItem _tabItem1; + CPTabViewItem _tabItem2; +} + +- (void)setUp +{ + _tabView = [[CPTabView alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]; + + _tabItem1 = [[CPTabViewItem alloc] initWithIdentifier:@"id1"]; + [_tabItem1 setLabel:@"Item A"]; + [_tabItem1 setView:[[CPView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]] + + _tabItem2 = [[CPTabViewItem alloc] initWithIdentifier:@"id2"]; + [_tabItem2 setLabel:@"Item B"]; + [_tabItem2 setView:[[CPView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]] + + [_tabView addTabViewItem:_tabItem1]; + [_tabView addTabViewItem:_tabItem2]; +} + +- (void)testCreate +{ + [self assertNotNull:_tabView]; + +} + +- (void)testMiddle +{ + var tabs = [_tabView tabs]; + [self assert:([_tabView frameSize].width / 2) equals:CPRectGetMidX([tabs frame])]; +} + +- (void)testMiddleAfterMoveFrame +{ + var tabs = [_tabView tabs]; + [_tabView setFrame:CPRectMake(10, 100, 1000, 200)]; + [self assert:([_tabView frameSize].width / 2) equals:CPRectGetMidX([tabs frame])]; +} + +- (void)testMiddleAfterMoveBound +{ + var tabs = [_tabView tabs]; + [_tabView setBounds:CPRectMake(100, 100, 20, 300)]; + [self assert:([tabs boundsSize].width / 2) equals:CPRectGetMidX([tabs bounds])]; +} + +@end \ No newline at end of file