Merge pull request #2324 from cacaodev/CPSegmentedControl-indexed-mutation

CPSegmentedControl
FIXED: widthForSegment: and frameForSegment: now return the correct values when the segments sizeToFit
Added indexed accessors for the segment object
Improved tile performance
With manual and ojtest
This commit is contained in:
cacaodev
2015-03-16 12:13:03 +01:00
9 changed files with 1463 additions and 3542 deletions
+170 -140
View File
@@ -42,7 +42,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
@implementation CPSegmentedControl : CPControl
{
CPArray _segments;
CPArray _segments @accessors(getter=segments);
CPArray _themeStates;
int _selectedSegment;
@@ -87,6 +87,8 @@ CPSegmentSwitchTrackingMomentary = 2;
_selectedSegment = -1;
_trackingMode = CPSegmentSwitchTrackingSelectOne;
_trackingHighlighted = NO;
_trackingSegment = -1;
}
return self;
@@ -107,7 +109,42 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (int)selectedTag
{
return [_segments[_selectedSegment] tag];
return [[_segments objectAtIndex:_selectedSegment] tag];
}
/*! @ignore */
- (void)setSegments:(CPArray)segments
{
[_segments removeAllObjects];
[_themeStates removeAllObjects];
[self insertSegments:segments atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [segments count])]];
}
/*! @ignore */
- (void)insertSegments:(CPArray)segments atIndexes:(CPIndexSet)indices
{
if ([segments count] == 0)
return;
var newStates = @[],
count = [indices count];
while (count--)
[newStates addObject:CPThemeStateNormal];
[_segments insertObjects:segments atIndexes:indices];
[_themeStates insertObjects:newStates atIndexes:indices];
}
/*! @ignore */
- (void)removeSegmentsAtIndexes:(CPIndexSet)indices
{
if ([indices count] == 0)
return;
[_segments removeObjectsAtIndexes:indices];
[_themeStates removeObjectsAtIndexes:indices];
}
// Specifying the number of segments
@@ -117,41 +154,32 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setSegmentCount:(unsigned)aCount
{
if (_segments.length == aCount)
var prevCount = [_segments count];
if (aCount == prevCount)
return;
var height = CGRectGetHeight([self bounds]),
dividersBefore = MAX(0, _segments.length - 1),
dividersAfter = MAX(0, aCount - 1);
if (_segments.length < aCount)
if (aCount > prevCount)
{
for (var index = _segments.length; index < aCount; ++index)
{
_segments[index] = [[_CPSegmentItem alloc] init];
_themeStates[index] = CPThemeStateNormal;
}
}
else if (aCount < _segments.length)
{
_segments.length = aCount;
_themeStates.length = aCount;
}
var count = aCount - prevCount,
segments = @[];
if (_selectedSegment >= _segments.length)
while (count--)
[segments addObject:[[_CPSegmentItem alloc] init]];
[self insertSegments:segments atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(prevCount, aCount - prevCount)]];
}
else
[self removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(aCount, prevCount - aCount)]];
[self _updateSelectionIfNeeded];
[self tileWithChangedSegment:MAX(MIN(prevCount, aCount) - 1, 0)];
}
- (void)_updateSelectionIfNeeded
{
if (_selectedSegment >= [self segmentCount])
_selectedSegment = -1;
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"],
frame = [self frame],
widthOfAllSegments = 0,
dividerExtraSpace = ([_segments count] - 1) * thickness;
for (var i = 0; i < [_segments count]; i++)
widthOfAllSegments += [_segments[i] width];
[self setFrameSize:CGSizeMake(widthOfAllSegments + dividerExtraSpace, frame.size.height)];
[self tileWithChangedSegment:0];
}
/*!
@@ -159,7 +187,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (unsigned)segmentCount
{
return _segments.length;
return [_segments count];
}
// Specifying Selected Segment
@@ -189,8 +217,8 @@ CPSegmentSwitchTrackingMomentary = 2;
{
var index = 0;
for (; index < _segments.length; ++index)
if (_segments[index].tag == aTag)
for (; index < [_segments count]; ++index)
if ([[_segments objectAtIndex:index] tag] == aTag)
{
[self setSelectedSegment:index];
@@ -204,8 +232,8 @@ CPSegmentSwitchTrackingMomentary = 2;
{
var index = 0;
for (; index < _segments.length; ++index)
if (_segments[index].label == aLabel)
for (; index < [_segments count]; ++index)
if ([[_segments objectAtIndex:index] label] == aLabel)
{
[self setSelectedSegment:index];
@@ -216,7 +244,7 @@ CPSegmentSwitchTrackingMomentary = 2;
}
// Specifying Tracking Mode
/*! @ignore */
- (BOOL)isTracking
{
@@ -234,7 +262,7 @@ CPSegmentSwitchTrackingMomentary = 2;
var index = 0,
selected = NO;
for (; index < _segments.length; ++index)
for (; index < [self segmentCount]; ++index)
if ([_segments[index] selected])
if (selected)
[self setSelected:NO forSegment:index];
@@ -246,7 +274,7 @@ CPSegmentSwitchTrackingMomentary = 2;
{
var index = 0;
for (; index < _segments.length; ++index)
for (; index < [self segmentCount]; ++index)
if ([_segments[index] selected])
[self setSelected:NO forSegment:index];
}
@@ -269,7 +297,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setWidth:(float)aWidth forSegment:(unsigned)aSegment
{
[_segments[aSegment] setWidth:aWidth];
[[_segments objectAtIndex:aSegment] setWidth:aWidth];
[self tileWithChangedSegment:aSegment];
}
@@ -280,7 +308,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (float)widthForSegment:(unsigned)aSegment
{
return [_segments[aSegment] width];
return [[_segments objectAtIndex:aSegment] width];
}
/*!
@@ -291,7 +319,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setImage:(CPImage)anImage forSegment:(unsigned)aSegment
{
[_segments[aSegment] setImage:anImage];
[[_segments objectAtIndex:aSegment] setImage:anImage];
[self tileWithChangedSegment:aSegment];
}
@@ -303,7 +331,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CPImage)imageForSegment:(unsigned)aSegment
{
return [_segments[aSegment] image];
return [[_segments objectAtIndex:aSegment] image];
}
/*!
@@ -314,7 +342,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setLabel:(CPString)aLabel forSegment:(unsigned)aSegment
{
[_segments[aSegment] setLabel:aLabel];
[[_segments objectAtIndex:aSegment] setLabel:aLabel];
[self tileWithChangedSegment:aSegment];
}
@@ -326,7 +354,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CPString)labelForSegment:(unsigned)aSegment
{
return [_segments[aSegment] label];
return [[_segments objectAtIndex:aSegment] label];
}
/*!
@@ -337,7 +365,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setMenu:(CPMenu)aMenu forSegment:(unsigned)aSegment
{
[_segments[aSegment] setMenu:aMenu];
[[_segments objectAtIndex:aSegment] setMenu:aMenu];
}
/*!
@@ -347,7 +375,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CPMenu)menuForSegment:(unsigned)aSegment
{
return [_segments[aSegment] menu];
return [[_segments objectAtIndex:aSegment] menu];
}
/*!
@@ -359,7 +387,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setSelected:(BOOL)isSelected forSegment:(unsigned)aSegment
{
var segment = _segments[aSegment];
var segment = [_segments objectAtIndex:aSegment];
// If we're already in this state, bail.
if ([segment selected] == isSelected)
@@ -376,7 +404,7 @@ CPSegmentSwitchTrackingMomentary = 2;
_selectedSegment = aSegment;
if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1)
if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1 && oldSelectedSegment < _segments.length)
{
[_segments[oldSelectedSegment] setSelected:NO];
_themeStates[oldSelectedSegment] = CPThemeStateNormal;
@@ -399,7 +427,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (BOOL)isSelectedForSegment:(unsigned)aSegment
{
return [_segments[aSegment] selected];
return [[_segments objectAtIndex:aSegment] selected];
}
/*!
@@ -410,10 +438,12 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setEnabled:(BOOL)shouldBeEnabled forSegment:(unsigned)aSegment
{
if ([_segments[aSegment] enabled] === shouldBeEnabled)
var segment = [_segments objectAtIndex:aSegment];
if ([segment enabled] === shouldBeEnabled)
return;
[_segments[aSegment] setEnabled:shouldBeEnabled];
[segment setEnabled:shouldBeEnabled];
if (shouldBeEnabled)
_themeStates[aSegment] = _themeStates[aSegment].without(CPThemeStateDisabled);
@@ -431,7 +461,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (BOOL)isEnabledForSegment:(unsigned)aSegment
{
return [_segments[aSegment] enabled];
return [[_segments objectAtIndex:aSegment] enabled];
}
/*!
@@ -441,7 +471,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setTag:(int)aTag forSegment:(unsigned)aSegment
{
[_segments[aSegment] setTag:aTag];
[[_segments objectAtIndex:aSegment] setTag:aTag];
}
/*!
@@ -450,7 +480,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (int)tagForSegment:(unsigned)aSegment
{
return [_segments[aSegment] tag];
return [[_segments objectAtIndex:aSegment] tag];
}
// Drawings
@@ -475,14 +505,12 @@ CPSegmentSwitchTrackingMomentary = 2;
- (float)_leftOffsetForSegment:(unsigned)segment
{
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
if (segment == 0)
return bezelInset.left;
return [self currentValueForThemeAttribute:@"bezel-inset"].left;
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"];
return [self _leftOffsetForSegment:segment - 1] + [self widthForSegment:segment - 1] + thickness;
return [self _leftOffsetForSegment:segment - 1] + CGRectGetWidth([self frameForSegment:segment - 1]) + thickness;
}
- (unsigned)_indexOfLastSegment
@@ -516,7 +544,7 @@ CPSegmentSwitchTrackingMomentary = 2;
else if (aName.indexOf("segment-bezel") === 0)
{
var segment = parseInt(aName.substring("segment-bezel-".length), 10),
frame = CGRectCreateCopy([_segments[segment] frame]);
frame = CGRectCreateCopy([self frameForSegment:segment]);
if (segment === 0)
{
@@ -524,7 +552,7 @@ CPSegmentSwitchTrackingMomentary = 2;
frame.size.width -= contentInset.left;
}
if (segment === _segments.length - 1)
if (segment === [self segmentCount] - 1)
frame.size.width = CGRectGetWidth([self bounds]) - contentInset.right - frame.origin.x;
return frame;
@@ -532,7 +560,7 @@ CPSegmentSwitchTrackingMomentary = 2;
else if (aName.indexOf("divider-bezel") === 0)
{
var segment = parseInt(aName.substring("divider-bezel-".length), 10),
width = [self widthForSegment:segment],
width = CGRectGetWidth([self frameForSegment:segment]),
left = [self _leftOffsetForSegment:segment],
thickness = [self currentValueForThemeAttribute:@"divider-thickness"];
@@ -558,7 +586,7 @@ CPSegmentSwitchTrackingMomentary = 2;
- (void)layoutSubviews
{
if (_segments.length <= 0)
if ([self segmentCount] <= 0)
return;
var themeState = _themeStates[0],
@@ -677,89 +705,90 @@ CPSegmentSwitchTrackingMomentary = 2;
{
}
- (void)tileWithChangedSegment:(unsigned)aSegment
/*! @ignore */
- (void)tile
{
if (aSegment >= _segments.length)
[self tileWithChangedSegment:0];
}
/*! @ignore */
- (void)tileWithChangedSegment:(CPInteger)aSegment
{
var segmentCount = [self segmentCount];
// Corner case: when segmentCount == 0 and aSegment == 0, we do not return here because we still need to set the new frameSize bellow.
if (aSegment < 0 || (segmentCount > 0 && aSegment >= segmentCount))
return;
var segment = _segments[aSegment],
segmentWidth = [segment width],
themeState = _themeState.hasThemeState(CPThemeStateDisabled) ? _themeStates[aSegment].and(CPThemeStateDisabled) : _themeStates[aSegment],
contentInset = [self valueForThemeAttribute:@"content-inset" inState:themeState],
font = [self font];
// Invalidate frames for segments on the right. They will be lazily computed by -frameForSegment:.
for (var i = aSegment; i < segmentCount; i++)
[_segments[i] setFrame:CGRectMakeZero()];
if (!segmentWidth)
{
if ([segment image] && [segment label])
segmentWidth = [[segment label] sizeWithFont:font].width + [[segment image] size].width + contentInset.left + contentInset.right;
else if (segment.image)
segmentWidth = [[segment image] size].width + contentInset.left + contentInset.right;
else if (segment.label)
segmentWidth = [[segment label] sizeWithFont:font].width + contentInset.left + contentInset.right;
else
segmentWidth = 0.0;
}
var delta = segmentWidth - CGRectGetWidth([segment frame]);
if (!delta)
{
[self setNeedsLayout];
[self setNeedsDisplay:YES];
return;
}
// Update control size
var frame = [self frame];
[self setFrameSize:CGSizeMake(CGRectGetWidth(frame) + delta, CGRectGetHeight(frame))];
// Update segment width
[segment setWidth:segmentWidth];
[segment setFrame:[self frameForSegment:aSegment]];
// Update following segments widths
var index = aSegment + 1;
for (; index < _segments.length; ++index)
{
[_segments[index] frame].origin.x += delta;
[self drawSegmentBezel:index highlight:NO];
[self drawSegment:index highlight:NO];
}
[self drawSegmentBezel:aSegment highlight:NO];
[self drawSegment:aSegment highlight:NO];
[self setFrameSize:[self intrinsicContentSize]];
[self setNeedsLayout];
[self setNeedsDisplay:YES];
}
/*! @ignore */
- (CGSize)intrinsicContentSize
{
// frameForSegment is recursively called backwards. All previously invalidated frames will be recomputed.
var segmentCount = [self segmentCount],
width = 0;
if (segmentCount > 0)
width = CGRectGetMaxX([self frameForSegment:(segmentCount - 1)]);
return CGSizeMake(width, [self valueForThemeAttribute:@"min-size"].height);
}
/*!
Returns the bounding rectangle for the specified segment.
@param aSegment the segment to get the rectangle for
*/
- (CGRect)frameForSegment:(unsigned)aSegment
{
return [self bezelFrameForSegment:aSegment];
var segment = [_segments objectAtIndex:aSegment],
frame = [segment frame];
if (CGRectEqualToRect(frame, CGRectMakeZero()))
{
frame = [self bezelFrameForSegment:aSegment];
[segment setFrame:frame];
}
return frame;
}
- (CGRect)bezelFrameForSegment:(unsigned)aSegment
{
var height = [self currentValueForThemeAttribute:@"min-size"].height,
bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"],
var left = [self _leftOffsetForSegment:aSegment],
top = [self currentValueForThemeAttribute:@"bezel-inset"].top,
width = [self widthForSegment:aSegment],
left = [self _leftOffsetForSegment:aSegment];
height = [self currentValueForThemeAttribute:@"min-size"].height;
return CGRectMake(left, bezelInset.top, width, height);
if (width == 0)
{
var themeState = _themeState.hasThemeState(CPThemeStateDisabled) ? _themeStates[aSegment].and(CPThemeStateDisabled) : _themeStates[aSegment],
contentInset = [self valueForThemeAttribute:@"content-inset" inState:themeState],
contentInsetWidth = contentInset.left + contentInset.right,
segment = _segments[aSegment],
label = [segment label],
image = [segment image];
width = (label ? [label sizeWithFont:[self font]].width : 4.0) + (image ? [image size].width : 0) + contentInsetWidth;
}
return CGRectMake(left, top, width, height);
}
- (CGRect)contentFrameForSegment:(unsigned)aSegment
{
var height = [self currentValueForThemeAttribute:@"min-size"].height,
contentInset = [self currentValueForThemeAttribute:@"content-inset"],
width = [self widthForSegment:aSegment],
width = CGRectGetWidth([self frameForSegment:aSegment]),
left = [self _leftOffsetForSegment:aSegment];
return CGRectMake(left + contentInset.left, contentInset.top, width - contentInset.left - contentInset.right, height - contentInset.top - contentInset.bottom);
@@ -773,19 +802,19 @@ CPSegmentSwitchTrackingMomentary = 2;
- (unsigned)testSegment:(CGPoint)aPoint
{
var location = [self convertPoint:aPoint fromView:nil],
count = _segments.length;
count = [self segmentCount];
while (count--)
if (CGRectContainsPoint([_segments[count] frame], aPoint))
if (CGRectContainsPoint([self frameForSegment:count], aPoint))
return count;
if (_segments.length)
if ([self segmentCount])
{
var adjustedLastFrame = CGRectCreateCopy([_segments[_segments.length - 1] frame]);
var adjustedLastFrame = CGRectCreateCopy([self frameForSegment:(_segments.length - 1)]);
adjustedLastFrame.size.width = CGRectGetWidth([self bounds]) - adjustedLastFrame.origin.x;
if (CGRectContainsPoint(adjustedLastFrame, aPoint))
return _segments.length - 1;
return [self segmentCount] - 1;
}
return -1;
@@ -836,7 +865,7 @@ CPSegmentSwitchTrackingMomentary = 2;
{
[self setSelected:NO forSegment:_trackingSegment];
_selectedSegment = -1;
_selectedSegment = CPNotFound;
}
}
@@ -880,7 +909,7 @@ CPSegmentSwitchTrackingMomentary = 2;
{
[super setFont:aFont];
[self tileWithChangedSegment:0];
[self tile];
}
@end
@@ -912,7 +941,7 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
if ([aCoder containsValueForKey:CPSegmentedControlSelectedKey])
_selectedSegment = [aCoder decodeIntForKey:CPSegmentedControlSelectedKey];
else
_selectedSegment = -1;
_selectedSegment = CPNotFound;
if ([aCoder containsValueForKey:CPSegmentedControlTrackingModeKey])
_trackingMode = [aCoder decodeIntForKey:CPSegmentedControlTrackingModeKey];
@@ -920,23 +949,24 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
_trackingMode = CPSegmentSwitchTrackingSelectOne;
// Here we update the themeStates array for each segments to know if there are selected or not
for (var i = 0; i < _segments.length; i++)
for (var i = 0; i < [self segmentCount]; i++)
_themeStates[i] = [_segments[i] selected] ? CPThemeStateSelected : CPThemeStateNormal;
// We do this in a second loop because it relies on all the themeStates being set first
for (var i = 0; i < _segments.length; i++)
[self tileWithChangedSegment:i];
[self tile];
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"],
dividerExtraSpace = ([_segments count] - 1) * thickness,
difference = MAX(originalWidth - [self frame].size.width - dividerExtraSpace, 0.0),
remainingWidth = FLOOR(difference / _segments.length),
remainingWidth = FLOOR(difference / [self segmentCount]),
widthOfAllSegments = 0;
for (var i = 0; i < _segments.length; i++)
// We do this in a second loop because it relies on all the themeStates being set first
for (var i = 0; i < [self segmentCount]; i++)
{
[self setWidth:[_segments[i] width] + remainingWidth forSegment:i];
widthOfAllSegments += [_segments[i] width];
var frame = [_segments[i] frame];
frame.size.width += remainingWidth;
widthOfAllSegments += CGRectGetWidth(frame);
}
// Here we handle the leftovers pixel, and we will add one pixel to each segment cell till we have the same size as the originalSize.
@@ -944,16 +974,16 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
var leftOversPixel = originalWidth - (widthOfAllSegments + dividerExtraSpace);
// Make sure we don't make an out of range
if (leftOversPixel < _segments.length - 1)
if (leftOversPixel < [self segmentCount] - 1)
{
for (var i = 0; i < leftOversPixel; i++)
{
[self setWidth:[_segments[i] width] + 1 forSegment:i];
[_segments[i] frame].size.width += 1;
}
}
[self setFrameSize:CGSizeMake(originalWidth, [self frame].size.height)];
[self tileWithChangedSegment:0];
[self setFrameSize:CGSizeMake(originalWidth, CGRectGetHeight([self frame]))];
[self tile];
}
return self;
+33 -1
View File
@@ -3,7 +3,7 @@
// TODO: Maybe create one test file for each tracking mode so they can be tested separately without confusion.
@implementation CPSegmentedControlTest : OJTestCase
{
CPSegmentedControlTest _segmentedControl;
CPSegmentedControl _segmentedControl;
}
- (void)setUp
@@ -29,5 +29,37 @@
[self assert:[_segmentedControl selectedSegment] equals:1];
}
- (void)testSegmentWidthAfterLabelChange
{
[_segmentedControl setWidth:0 forSegment:0]; // This is the default.
[_segmentedControl setLabel:@"Label 1" forSegment:0];
[self assert:[_segmentedControl widthForSegment:0] equals:0]; // The width property is still 0 : means sizeToFit content.
[_segmentedControl setWidth:100 forSegment:0];
[self assert:[_segmentedControl widthForSegment:0] equals:100];
//Unfortunatly it is not possible to test frameForSegment: or anything related to the layout.
// -sizeWithFont: uses the DOM and will always return (0,0) in the terminal.
}
- (void)testRemoveSelection
{
[_segmentedControl setSelectedSegment:2];
[_segmentedControl setSegmentCount:2];
[self assert:[_segmentedControl selectedSegment] equals:-1]; // Removed a selected segment. No selection.
}
- (void)testEmpty
{
[self assertNoThrow:function()
{
[_segmentedControl setSegmentCount:0];
}];
[self assert:[_segmentedControl selectedSegment] equals:-1];
}
@end
@@ -1,3 +1,4 @@
/*
* AppController.j
* CPSegmentedControlTest
@@ -5,24 +6,26 @@
* Created by You on January 4, 2013.
* Copyright 2013, Your Company All rights reserved.
*/
@import <AppKit/AppKit.j>
@import <Foundation/CPObject.j>
CPLogRegister(CPLogConsole);
@implementation AppController : CPObject
{
@outlet CPSegmentedControl segmentedControl1;
@outlet CPSegmentedControl segmentedControl2;
@outlet CPTextField passedLabel;
CPWindow theWindow; //this "outlet" is connected automatically by the Cib
@outlet CPWindow theWindow; //this "outlet" is connected automatically by the Cib
}
- (void)awakeFromCib
{
[theWindow setFullPlatformWindow:YES];
[segmentedControl1 setValue:[CPColor redColor] forThemeAttribute:@"divider-bezel-color"];
}
- (IBAction)addSegment:(id)aSender
{
var n = [segmentedControl1 segmentCount];
@@ -36,24 +39,98 @@
- (IBAction)addSegmentWithFixedSize:(id)aSender
{
var width = [[[theWindow contentView] viewWithTag:1] intValue];
var n = [segmentedControl1 segmentCount];
[segmentedControl1 setSegmentCount:n + 1];
[segmentedControl1 setLabel:@"" + n + 1 forSegment:n];
[segmentedControl1 setWidth:100 forSegment:n];
[segmentedControl1 setWidth:width forSegment:n];
[segmentedControl2 setSegmentCount:n + 1];
[segmentedControl2 setLabel:@"" + n + 1 forSegment:n];
[segmentedControl2 setWidth:100 forSegment:n];
[segmentedControl2 setWidth:width forSegment:n];
}
- (IBAction)removeSegment:(id)aSender
{
var n = [segmentedControl1 segmentCount];
var n = [[[theWindow contentView] viewWithTag:4] intValue];
[segmentedControl1 setSegmentCount:n - 1];
[segmentedControl2 setSegmentCount:n - 1];
[segmentedControl1 setSegmentCount:n];
[segmentedControl2 setSegmentCount:n];
}
/*
- (IBAction)removeSelectedSegment:(id)sender
{
var selectedSegment1 = [segmentedControl1 selectedSegment];
[segmentedControl1 removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndex:selectedSegment1]];
var selectedSegment2 = [segmentedControl2 selectedSegment];
[segmentedControl2 removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndex:selectedSegment2]];
}
*/
- (IBAction)changeLabel:(id)sender
{
var segment = [[[theWindow contentView] viewWithTag:2] intValue];
[segmentedControl1 setLabel:(@"New Label " + segment) forSegment:segment];
[segmentedControl2 setLabel:(@"New Label " + segment) forSegment:segment];
CPLog.debug("Segment " + segment + " frame=" + CPStringFromRect([segmentedControl2 frameForSegment:segment]) + " width=" + [segmentedControl2 widthForSegment:0]);
}
- (IBAction)changeImage:(id)sender
{
var image = CPImageInBundle(@"CPImageNameAdvanced.png", 20, 20);
var segment = [[[theWindow contentView] viewWithTag:3] intValue];
[segmentedControl1 setImage:image forSegment:segment];
[segmentedControl2 setImage:image forSegment:segment];
}
- (IBAction)testWidthAndFrame:(id)sender
{
// We test the segment width and frame here and not with ojtest because CPString-sizeWithFont: does
// cannot be tested in the command line.
[segmentedControl1 setSegmentCount:4];
[segmentedControl1 setWidth:0 forSegment:2]; // This is the default
if (![self assertTrue:[segmentedControl1 widthForSegment:2] == 0])
return;
[segmentedControl1 setLabel:"XXXX" forSegment:2];
// The width explicitely set does not change
if (![self assertTrue:[segmentedControl1 widthForSegment:2] == 0])
return;
// The frame sizeToFit horizontaly
if (![self assertTrue:CGRectGetWidth([segmentedControl1 frameForSegment:2]) > 0])
return;
[segmentedControl1 setWidth:200 forSegment:2];
// The width explicitely set does not change
if (![self assertTrue:[segmentedControl1 widthForSegment:2] == 200])
return;
if (![self assertTrue:CGRectGetWidth([segmentedControl1 frameForSegment:2]) == 200])
return;
}
- (BOOL)assertTrue:(BOOL)value
{
[passedLabel setStringValue:value?@"Passed":@"Failed"];
[passedLabel setTextColor:value?[CPColor greenColor]:[CPColor redColor]];
return value;
}
- (IBAction)setTrackingMode:(id)sender
{
var mode = [sender indexOfSelectedItem];
[segmentedControl1 setTrackingMode:mode];
[segmentedControl2 setTrackingMode:mode];
}
@end
@@ -1,500 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>archiveVersion</key>
<string>1</string>
<key>classes</key>
<dict/>
<key>objectVersion</key>
<string>46</string>
<key>objects</key>
<dict>
<key>031E4C438E885E73CAF8F095</key>
<dict>
<key>children</key>
<array>
<string>516D43C09BCF54EA7B027D03</string>
</array>
<key>isa</key>
<string>PBXGroup</string>
<key>name</key>
<string>Sources</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>078D4BE08F1637A95D02423E</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>sourcecode.c.h</string>
<key>name</key>
<string>xcc_general_include.h</string>
<key>path</key>
<string>.XcodeSupport/xcc_general_include.h</string>
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
</dict>
<key>32F240828F06C24BACC9C62B</key>
<dict>
<key>fileRef</key>
<string>B7B84A02A45ECE89FFB2D63E</string>
<key>isa</key>
<string>PBXBuildFile</string>
</dict>
<key>516D43C09BCF54EA7B027D03</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>?</string>
<key>name</key>
<string>AppController.j</string>
<key>path</key>
<string>AppController.j</string>
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
</dict>
<key>574D4419AEFA1A577855B344</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>sourcecode.c.h</string>
<key>name</key>
<string>_Users_Tonio_Desktop_CPSegmentedControlTest_AppController.h</string>
<key>path</key>
<string>.XcodeSupport/_Users_Tonio_Desktop_CPSegmentedControlTest_AppController.h</string>
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
</dict>
<key>9EEC4488135749D200615446</key>
<dict>
<key>children</key>
<array>
<string>9EEC44CB13574A0B00615446</string>
<string>9EEC4496135749D200615446</string>
<string>FC904786B1947C20F37E2DA1</string>
<string>031E4C438E885E73CAF8F095</string>
</array>
<key>isa</key>
<string>PBXGroup</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>9EEC448A135749D200615446</key>
<dict>
<key>attributes</key>
<dict>
<key>LastUpgradeCheck</key>
<string>0440</string>
<key>ORGANIZATIONNAME</key>
<string>280 North, Inc.</string>
</dict>
<key>buildConfigurationList</key>
<string>9EEC448D135749D200615446</string>
<key>compatibilityVersion</key>
<string>Xcode 3.2</string>
<key>developmentRegion</key>
<string>English</string>
<key>hasScannedForEncodings</key>
<string>0</string>
<key>isa</key>
<string>PBXProject</string>
<key>knownRegions</key>
<array>
<string>en</string>
</array>
<key>mainGroup</key>
<string>9EEC4488135749D200615446</string>
<key>productRefGroup</key>
<string>9EEC4494135749D200615446</string>
<key>projectDirPath</key>
<string></string>
<key>projectRoot</key>
<string></string>
<key>targets</key>
<array>
<string>9EEC4492135749D200615446</string>
</array>
</dict>
<key>9EEC448D135749D200615446</key>
<dict>
<key>buildConfigurations</key>
<array>
<string>9EEC44C3135749D300615446</string>
<string>9EEC44C4135749D300615446</string>
</array>
<key>defaultConfigurationIsVisible</key>
<string>0</string>
<key>defaultConfigurationName</key>
<string>Release</string>
<key>isa</key>
<string>XCConfigurationList</string>
</dict>
<key>9EEC448F135749D200615446</key>
<dict>
<key>buildActionMask</key>
<string>2147483647</string>
<key>files</key>
<array>
<string>32F240828F06C24BACC9C62B</string>
</array>
<key>isa</key>
<string>PBXSourcesBuildPhase</string>
<key>runOnlyForDeploymentPostprocessing</key>
<string>0</string>
</dict>
<key>9EEC4490135749D200615446</key>
<dict>
<key>buildActionMask</key>
<string>2147483647</string>
<key>files</key>
<array>
<string>9EEC4498135749D200615446</string>
</array>
<key>isa</key>
<string>PBXFrameworksBuildPhase</string>
<key>runOnlyForDeploymentPostprocessing</key>
<string>0</string>
</dict>
<key>9EEC4491135749D200615446</key>
<dict>
<key>buildActionMask</key>
<string>2147483647</string>
<key>files</key>
<array>
<string>9EEC44CC13574A0B00615446</string>
</array>
<key>isa</key>
<string>PBXResourcesBuildPhase</string>
<key>runOnlyForDeploymentPostprocessing</key>
<string>0</string>
</dict>
<key>9EEC4492135749D200615446</key>
<dict>
<key>buildConfigurationList</key>
<string>9EEC44C5135749D300615446</string>
<key>buildPhases</key>
<array>
<string>9EEC448F135749D200615446</string>
<string>9EEC4490135749D200615446</string>
<string>9EEC4491135749D200615446</string>
</array>
<key>buildRules</key>
<array/>
<key>dependencies</key>
<array/>
<key>isa</key>
<string>PBXNativeTarget</string>
<key>name</key>
<string>Another</string>
<key>productName</key>
<string>Another</string>
<key>productReference</key>
<string>9EEC4493135749D200615446</string>
<key>productType</key>
<string>com.apple.product-type.application</string>
</dict>
<key>9EEC4493135749D200615446</key>
<dict>
<key>explicitFileType</key>
<string>wrapper.application</string>
<key>includeInIndex</key>
<string>0</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>path</key>
<string>Another.app</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
</dict>
<key>9EEC4494135749D200615446</key>
<dict>
<key>children</key>
<array>
<string>9EEC4493135749D200615446</string>
</array>
<key>isa</key>
<string>PBXGroup</string>
<key>name</key>
<string>Products</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>9EEC4496135749D200615446</key>
<dict>
<key>children</key>
<array>
<string>9EEC4497135749D200615446</string>
<string>9EEC4499135749D300615446</string>
</array>
<key>isa</key>
<string>PBXGroup</string>
<key>name</key>
<string>Frameworks</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>9EEC4497135749D200615446</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>wrapper.framework</string>
<key>name</key>
<string>Cocoa.framework</string>
<key>path</key>
<string>System/Library/Frameworks/Cocoa.framework</string>
<key>sourceTree</key>
<string>SDKROOT</string>
</dict>
<key>9EEC4498135749D200615446</key>
<dict>
<key>fileRef</key>
<string>9EEC4497135749D200615446</string>
<key>isa</key>
<string>PBXBuildFile</string>
</dict>
<key>9EEC4499135749D300615446</key>
<dict>
<key>children</key>
<array>
<string>9EEC449A135749D300615446</string>
<string>9EEC449B135749D300615446</string>
<string>9EEC449C135749D300615446</string>
</array>
<key>isa</key>
<string>PBXGroup</string>
<key>name</key>
<string>Other Frameworks</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>9EEC449A135749D300615446</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>wrapper.framework</string>
<key>name</key>
<string>AppKit.framework</string>
<key>path</key>
<string>System/Library/Frameworks/AppKit.framework</string>
<key>sourceTree</key>
<string>SDKROOT</string>
</dict>
<key>9EEC449B135749D300615446</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>wrapper.framework</string>
<key>name</key>
<string>CoreData.framework</string>
<key>path</key>
<string>System/Library/Frameworks/CoreData.framework</string>
<key>sourceTree</key>
<string>SDKROOT</string>
</dict>
<key>9EEC449C135749D300615446</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>wrapper.framework</string>
<key>name</key>
<string>Foundation.framework</string>
<key>path</key>
<string>System/Library/Frameworks/Foundation.framework</string>
<key>sourceTree</key>
<string>SDKROOT</string>
</dict>
<key>9EEC44C3135749D300615446</key>
<dict>
<key>buildSettings</key>
<dict>
<key>ARCHS</key>
<string>$(ARCHS_STANDARD_32_64_BIT)</string>
<key>GCC_C_LANGUAGE_STANDARD</key>
<string>gnu99</string>
<key>GCC_OPTIMIZATION_LEVEL</key>
<string>0</string>
<key>GCC_PREPROCESSOR_DEFINITIONS</key>
<string>DEBUG</string>
<key>GCC_SYMBOLS_PRIVATE_EXTERN</key>
<string>NO</string>
<key>GCC_VERSION</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>GCC_WARN_64_TO_32_BIT_CONVERSION</key>
<string>YES</string>
<key>GCC_WARN_ABOUT_RETURN_TYPE</key>
<string>YES</string>
<key>GCC_WARN_UNUSED_VARIABLE</key>
<string>YES</string>
<key>MACOSX_DEPLOYMENT_TARGET</key>
<string>10.6</string>
<key>ONLY_ACTIVE_ARCH</key>
<string>YES</string>
<key>SDKROOT</key>
<string>macosx</string>
</dict>
<key>isa</key>
<string>XCBuildConfiguration</string>
<key>name</key>
<string>Debug</string>
</dict>
<key>9EEC44C4135749D300615446</key>
<dict>
<key>buildSettings</key>
<dict>
<key>ARCHS</key>
<string>$(ARCHS_STANDARD_32_64_BIT)</string>
<key>GCC_C_LANGUAGE_STANDARD</key>
<string>gnu99</string>
<key>GCC_VERSION</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>GCC_WARN_64_TO_32_BIT_CONVERSION</key>
<string>YES</string>
<key>GCC_WARN_ABOUT_RETURN_TYPE</key>
<string>YES</string>
<key>GCC_WARN_UNUSED_VARIABLE</key>
<string>YES</string>
<key>MACOSX_DEPLOYMENT_TARGET</key>
<string>10.6</string>
<key>SDKROOT</key>
<string>macosx</string>
</dict>
<key>isa</key>
<string>XCBuildConfiguration</string>
<key>name</key>
<string>Release</string>
</dict>
<key>9EEC44C5135749D300615446</key>
<dict>
<key>buildConfigurations</key>
<array>
<string>9EEC44C6135749D300615446</string>
<string>9EEC44C7135749D300615446</string>
</array>
<key>defaultConfigurationIsVisible</key>
<string>0</string>
<key>defaultConfigurationName</key>
<string>Release</string>
<key>isa</key>
<string>XCConfigurationList</string>
</dict>
<key>9EEC44C6135749D300615446</key>
<dict>
<key>buildSettings</key>
<dict>
<key>ALWAYS_SEARCH_USER_PATHS</key>
<string>NO</string>
<key>COMBINE_HIDPI_IMAGES</key>
<string>YES</string>
<key>COPY_PHASE_STRIP</key>
<string>NO</string>
<key>GCC_DYNAMIC_NO_PIC</key>
<string>NO</string>
<key>GCC_ENABLE_OBJC_EXCEPTIONS</key>
<string>YES</string>
<key>GCC_PRECOMPILE_PREFIX_HEADER</key>
<string>YES</string>
<key>GCC_PREFIX_HEADER</key>
<string>Another/Another-Prefix.pch</string>
<key>INFOPLIST_FILE</key>
<string>Another/Another-Info.plist</string>
<key>PRODUCT_NAME</key>
<string>$(TARGET_NAME)</string>
<key>WRAPPER_EXTENSION</key>
<string>app</string>
</dict>
<key>isa</key>
<string>XCBuildConfiguration</string>
<key>name</key>
<string>Debug</string>
</dict>
<key>9EEC44C7135749D300615446</key>
<dict>
<key>buildSettings</key>
<dict>
<key>ALWAYS_SEARCH_USER_PATHS</key>
<string>NO</string>
<key>COMBINE_HIDPI_IMAGES</key>
<string>YES</string>
<key>COPY_PHASE_STRIP</key>
<string>YES</string>
<key>DEBUG_INFORMATION_FORMAT</key>
<string>dwarf-with-dsym</string>
<key>GCC_ENABLE_OBJC_EXCEPTIONS</key>
<string>YES</string>
<key>GCC_PRECOMPILE_PREFIX_HEADER</key>
<string>YES</string>
<key>GCC_PREFIX_HEADER</key>
<string>Another/Another-Prefix.pch</string>
<key>INFOPLIST_FILE</key>
<string>Another/Another-Info.plist</string>
<key>PRODUCT_NAME</key>
<string>$(TARGET_NAME)</string>
<key>WRAPPER_EXTENSION</key>
<string>app</string>
</dict>
<key>isa</key>
<string>XCBuildConfiguration</string>
<key>name</key>
<string>Release</string>
</dict>
<key>9EEC44CB13574A0B00615446</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>folder</string>
<key>name</key>
<string>CappuccinoResources</string>
<key>path</key>
<string>Resources</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>9EEC44CC13574A0B00615446</key>
<dict>
<key>fileRef</key>
<string>9EEC44CB13574A0B00615446</string>
<key>isa</key>
<string>PBXBuildFile</string>
</dict>
<key>B7B84A02A45ECE89FFB2D63E</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>sourcecode.c.objc</string>
<key>name</key>
<string>_Users_Tonio_Desktop_CPSegmentedControlTest_AppController.m</string>
<key>path</key>
<string>.XcodeSupport/_Users_Tonio_Desktop_CPSegmentedControlTest_AppController.m</string>
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
</dict>
<key>FC904786B1947C20F37E2DA1</key>
<dict>
<key>children</key>
<array>
<string>078D4BE08F1637A95D02423E</string>
<string>574D4419AEFA1A577855B344</string>
<string>B7B84A02A45ECE89FFB2D63E</string>
</array>
<key>isa</key>
<string>PBXGroup</string>
<key>name</key>
<string>Classes</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
</dict>
<key>rootObject</key>
<string>9EEC448A135749D200615446</string>
</dict>
</plist>
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:CPSegmentedControlTest.xcodeproj">
</FileRef>
</Workspace>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
+2 -3
View File
@@ -50,10 +50,9 @@
// HACK
for (var i = 0; i < _segments.length; i++)
{
_themeStates[i] = _segments[i].selected ? CPThemeStateSelected : CPThemeStateNormal;
[self tileWithChangedSegment:i];
}
[self tile];
[self setEnabled:[cell isEnabled]];
}