27 @
typedef CPSegmentSwitchTracking
44 CPSegmentSwitchTracking _trackingMode;
46 unsigned _trackingSegment;
47 BOOL _trackingHighlighted;
52 return "segmented-control";
62 @"bezel-inset": CGInsetMakeZero(),
63 @"content-inset": CGInsetMakeZero(),
68 @"divider-thickness": 1.0,
72 - (id)initWithFrame:(CGRect)aRect
81 _selectedSegment = -1;
84 _trackingHighlighted = NO;
85 _trackingSegment = -1;
95 - (void)setControlSize:(CPControlSize)aControlSize
98 [
self _sizeToControlSize];
106 return [[_segments objectAtIndex:_selectedSegment] tag];
110 - (void)setSegments:(CPArray)segments
112 [_segments removeAllObjects];
113 [_themeStates removeAllObjects];
119 - (void)insertSegments:(CPArray)segments atIndexes:(
CPIndexSet)indices
121 if ([segments count] == 0)
125 count = [indices
count];
128 [newStates addObject:CPThemeStateNormal];
130 [_segments insertObjects:segments atIndexes:indices];
131 [_themeStates insertObjects:newStates atIndexes:indices];
137 if ([indices count] == 0)
140 [_segments removeObjectsAtIndexes:indices];
141 [_themeStates removeObjectsAtIndexes:indices];
149 - (void)setSegmentCount:(
unsigned)aCount
151 var prevCount = [_segments count];
153 if (aCount == prevCount)
156 if (aCount > prevCount)
158 var count = aCount - prevCount,
162 [segments addObject:[[_CPSegmentItem alloc] init]];
169 [
self _updateSelectionIfNeeded];
173 - (void)_updateSelectionIfNeeded
175 if (_selectedSegment >= [
self segmentCount])
176 _selectedSegment = -1;
182 - (unsigned)segmentCount
184 return [_segments count];
193 - (void)setSelectedSegment:(
unsigned)aSegment
196 if (_selectedSegment == aSegment)
206 _selectedSegment = -1;
215 - (unsigned)selectedSegment
217 return _selectedSegment;
223 - (BOOL)selectSegmentWithTag:(
int)aTag
227 for (; index < [_segments count]; ++index)
228 if ([[_segments objectAtIndex:index]
tag] == aTag)
238 - (BOOL)_selectSegmentWithLabel:(
CPString)aLabel
242 for (; index < [_segments count]; ++index)
243 if ([[_segments objectAtIndex:index] label] == aLabel)
245 [
self setSelectedSegment:index];
260 - (void)setTrackingMode:(CPSegmentSwitchTracking)aTrackingMode
262 if (_trackingMode == aTrackingMode)
265 _trackingMode = aTrackingMode;
275 [
self setSelected:NO forSegment:index];
286 [
self setSelected:NO forSegment:index];
293 - (CPSegmentSwitchTracking)trackingMode
295 return _trackingMode;
305 - (void)setWidth:(
float)aWidth forSegment:(
unsigned)aSegment
307 [[_segments objectAtIndex:aSegment] setWidth:aWidth];
316 - (float)widthForSegment:(
unsigned)aSegment
318 return [[_segments objectAtIndex:aSegment] width];
327 - (void)setImage:(
CPImage)anImage forSegment:(
unsigned)aSegment
329 [[_segments objectAtIndex:aSegment] setImage:anImage];
341 return [[_segments objectAtIndex:aSegment] image];
350 - (void)setLabel:(
CPString)aLabel forSegment:(
unsigned)aSegment
352 [[_segments objectAtIndex:aSegment] setLabel:aLabel];
364 return [[_segments objectAtIndex:aSegment] label];
373 - (void)setMenu:(
CPMenu)aMenu forSegment:(
unsigned)aSegment
375 [[_segments objectAtIndex:aSegment] setMenu:aMenu];
383 - (
CPMenu)menuForSegment:(
unsigned)aSegment
385 return [[_segments objectAtIndex:aSegment] menu];
395 - (void)setSelected:(BOOL)isSelected forSegment:(
unsigned)aSegment
397 var segment = [_segments objectAtIndex:aSegment];
400 if ([segment
selected] == isSelected)
403 [segment setSelected:isSelected];
410 var oldSelectedSegment = _selectedSegment;
412 _selectedSegment = aSegment;
414 if (_trackingMode ==
CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1 && oldSelectedSegment < _segments.length)
416 [_segments[oldSelectedSegment] setSelected:NO];
417 _themeStates[oldSelectedSegment] = CPThemeStateNormal;
419 [
self drawSegmentBezel:oldSelectedSegment highlight:NO];
424 [
self drawSegmentBezel:aSegment highlight:NO];
426 [
self setNeedsLayout];
427 [
self setNeedsDisplay:YES];
435 - (BOOL)isSelectedForSegment:(
unsigned)aSegment
437 return [[_segments objectAtIndex:aSegment] selected];
446 - (void)setEnabled:(BOOL)shouldBeEnabled forSegment:(
unsigned)aSegment
448 var segment = [_segments objectAtIndex:aSegment];
450 if ([segment
enabled] === shouldBeEnabled)
453 [segment setEnabled:shouldBeEnabled];
469 - (BOOL)isEnabledForSegment:(
unsigned)aSegment
471 return [[_segments objectAtIndex:aSegment] enabled];
479 - (void)setTag:(
int)aTag forSegment:(
unsigned)aSegment
481 [[_segments objectAtIndex:aSegment] setTag:aTag];
488 - (int)tagForSegment:(
unsigned)aSegment
490 return [[_segments objectAtIndex:aSegment] tag];
499 - (void)drawSegmentBezel:(
int)aSegment highlight:(BOOL)shouldHighlight
501 if(aSegment < _themeStates.length)
513 - (float)_leftOffsetForSegment:(
unsigned)segment
516 return [
self currentValueForThemeAttribute:@"bezel-inset"].left;
518 var thickness = [
self currentValueForThemeAttribute:@"divider-thickness"];
520 return [
self _leftOffsetForSegment:segment - 1] + CGRectGetWidth([self frameForSegment:segment - 1]) + thickness;
523 - (unsigned)_indexOfLastSegment
525 var lastSegmentIndex = [_segments count] - 1;
527 if (lastSegmentIndex < 0)
528 lastSegmentIndex = 0;
530 return lastSegmentIndex;
533 - (CGRect)rectForEphemeralSubviewNamed:(
CPString)aName
540 if (aName ===
"left-segment-bezel")
542 return CGRectMake(bezelInset.left, bezelInset.top, contentInset.left, height);
544 else if (aName ===
"right-segment-bezel")
546 return CGRectMake(CGRectGetWidth([
self bounds]) - contentInset.right,
551 else if (aName.indexOf(
"segment-bezel") === 0)
553 var segment = parseInt(aName.substring(
"segment-bezel-".length), 10),
554 frame = CGRectCreateCopy([
self frameForSegment:segment]);
558 frame.origin.x += contentInset.left;
559 frame.size.width -= contentInset.left;
562 if (segment === [
self segmentCount] - 1)
563 frame.size.width = CGRectGetWidth([
self bounds]) - contentInset.right -
frame.origin.x;
567 else if (aName.indexOf(
"divider-bezel") === 0)
569 var segment = parseInt(aName.substring(
"divider-bezel-".length), 10),
570 width = CGRectGetWidth([
self frameForSegment:segment]),
571 left = [
self _leftOffsetForSegment:segment],
574 return CGRectMake(left +
width, bezelInset.top, thickness, height);
576 else if (aName.indexOf(
"segment-content") === 0)
578 var segment = parseInt(aName.substring(
"segment-content-".length), 10);
588 if ([aName hasPrefix:
@"segment-content"])
589 return [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
591 return [[
CPView alloc] initWithFrame:CGRectMakeZero()];
594 - (void)layoutSubviews
596 if ([
self segmentCount] <= 0)
599 var themeState = _themeStates[0],
601 isControlSizeSmall = [
self hasThemeState:CPThemeStateControlSizeSmall],
602 isControlSizeMini = [
self hasThemeState:CPThemeStateControlSizeMini];
606 if (isControlSizeSmall)
608 else if (isControlSizeMini)
618 [leftBezelView setBackgroundColor:leftCapColor];
620 var themeState = _themeStates[_themeStates.length - 1];
624 if (isControlSizeSmall)
626 else if (isControlSizeMini)
636 [rightBezelView setBackgroundColor:rightCapColor];
638 for (var i = 0, count = _themeStates.length; i < count; i++)
640 var themeState = _themeStates[i];
644 if (isControlSizeSmall)
646 else if (isControlSizeMini)
656 [bezelView setBackgroundColor:bezelColor];
659 var segment = _segments[i],
664 [contentView setText:[segment label]];
665 [contentView setImage:[segment image]];
676 if ([segment image] && [segment
label])
678 else if ([segment image])
679 [contentView setImagePosition:CPImageOnly];
685 var borderState = _themeStates[i].and(_themeStates[i + 1]);
689 if (isControlSizeSmall)
691 else if (isControlSizeMini)
701 [borderView setBackgroundColor:borderColor];
711 - (void)drawSegment:(
int)aSegment highlight:(BOOL)shouldHighlight
722 - (void)tileWithChangedSegment:(CPInteger)aSegment
727 if (aSegment < 0 || (segmentCount > 0 && aSegment >= segmentCount))
731 for (var i = aSegment; i < segmentCount; i++)
732 [_segments[i] setFrame:CGRectMakeZero()];
741 - (CGSize)intrinsicContentSize
747 if (segmentCount > 0)
748 width = CGRectGetMaxX([
self frameForSegment:(segmentCount - 1)]);
750 return CGSizeMake(
width, [
self valueForThemeAttribute:
@"min-size"].height);
757 - (CGRect)frameForSegment:(
unsigned)aSegment
759 var segment = [_segments objectAtIndex:aSegment],
760 frame = [segment frame];
762 if (CGRectEqualToRect(
frame, CGRectMakeZero()))
765 [segment setFrame:frame];
771 - (CGRect)bezelFrameForSegment:(
unsigned)aSegment
773 var left = [
self _leftOffsetForSegment:aSegment],
782 contentInsetWidth = contentInset.left + contentInset.right,
784 segment = _segments[aSegment],
785 label = [segment label],
786 image = [segment image];
788 width = (label ? [label sizeWithFont:[
self font]].width : 4.0) + (image ? [image size].width : 0) + contentInsetWidth;
791 return CGRectMake(left, top,
width, height);
794 - (CGRect)contentFrameForSegment:(
unsigned)aSegment
798 width = CGRectGetWidth([
self frameForSegment:aSegment]),
799 left = [
self _leftOffsetForSegment:aSegment];
801 return CGRectMake(left + contentInset.left, contentInset.top,
width - contentInset.left - contentInset.right, height - contentInset.top - contentInset.bottom);
809 - (unsigned)testSegment:(CGPoint)aPoint
815 if (CGRectContainsPoint([
self frameForSegment:count], aPoint))
818 if ([
self segmentCount])
820 var adjustedLastFrame = CGRectCreateCopy([
self frameForSegment:(_segments.length - 1)]);
821 adjustedLastFrame.size.width = CGRectGetWidth([
self bounds]) - adjustedLastFrame.origin.x;
823 if (CGRectContainsPoint(adjustedLastFrame, aPoint))
832 if (![
self isEnabled])
849 var type = [anEvent
type],
854 if (_trackingSegment == -1)
857 if (_trackingSegment === [
self testSegment:location])
864 _selectedSegment = _trackingSegment;
881 _trackingSegment = -1;
889 if (trackingSegment > -1 && [
self isEnabledForSegment:trackingSegment])
891 _trackingHighlighted = YES;
892 _trackingSegment = trackingSegment;
899 if (_trackingSegment == -1)
902 var highlighted = [
self testSegment:location] === _trackingSegment;
904 if (highlighted != _trackingHighlighted)
906 _trackingHighlighted = highlighted;
912 [CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
938 originalWidth = frame.size.width;
940 frame.size.width = 0;
944 _segments = [aCoder decodeObjectForKey:CPSegmentedControlSegmentsKey];
945 _segmentStyle = [aCoder decodeIntForKey:CPSegmentedControlSegmentStyleKey];
949 _selectedSegment = [aCoder decodeIntForKey:CPSegmentedControlSelectedKey];
954 _trackingMode = [aCoder decodeIntForKey:CPSegmentedControlTrackingModeKey];
965 dividerExtraSpace = ([_segments count] - 1) * thickness,
966 difference = MAX(originalWidth - [
self frame].size.width - dividerExtraSpace, 0.0),
967 remainingWidth = FLOOR(difference / [
self segmentCount]),
968 widthOfAllSegments = 0;
973 var frame = [_segments[i] frame];
974 frame.size.width += remainingWidth;
976 widthOfAllSegments += CGRectGetWidth(frame);
981 var leftOversPixel = originalWidth - (widthOfAllSegments + dividerExtraSpace);
984 if (leftOversPixel < [
self segmentCount] - 1)
986 for (var i = 0; i < leftOversPixel; i++)
988 [_segments[i] frame].size.width += 1;
1003 [aCoder encodeObject:_segments forKey:CPSegmentedControlSegmentsKey];
1004 [aCoder encodeInt:_selectedSegment forKey:CPSegmentedControlSelectedKey];
1005 [aCoder encodeInt:_segmentStyle forKey:CPSegmentedControlSegmentStyleKey];
1006 [aCoder encodeInt:_trackingMode forKey:CPSegmentedControlTrackingModeKey];
1013 + (Class)_binderClassForBinding:(
CPString)aBinding
1015 if ([
self _isSelectionBinding:aBinding])
1016 return [_CPSegmentedControlBinder class];
1018 return [
super _binderClassForBinding:aBinding];
1021 + (BOOL)_isSelectionBinding:(
CPString)aBinding
1028 return [
self _isSelectionBinding:aBinding];
1035 CPLog.warn(
"Binding " + aBinding +
" needs CPSegmentSwitchTrackingSelectOne tracking mode");
1042 - (void)_reverseSetBinding
1044 [_CPSegmentedControlBinder _reverseSetValueFromExclusiveBinderForObject:self];
1050 @implementation _CPSegmentedControlBinder :
CPBinder
1055 - (void)_updatePlaceholdersWithOptions:(
CPDictionary)options
1057 [
super _updatePlaceholdersWithOptions:options];
1059 [
self _setPlaceholder:CPSegmentedControlNoSelectionPlaceholder forMarker:CPMultipleValuesMarker isDefault:YES];
1060 [
self _setPlaceholder:CPSegmentedControlNoSelectionPlaceholder forMarker:CPNoSelectionMarker isDefault:YES];
1061 [
self _setPlaceholder:CPSegmentedControlNoSelectionPlaceholder forMarker:CPNotApplicableMarker isDefault:YES];
1062 [
self _setPlaceholder:CPSegmentedControlNoSelectionPlaceholder forMarker:CPNullMarker isDefault:YES];
1065 - (void)setPlaceholderValue:(
id)aValue withMarker:(
CPString)aMarker forBinding:(
CPString)aBinding
1068 [_source setSelected:NO forSegment:[_source selectedSegment]];
1070 [
self setValue:aValue forBinding:aBinding];
1073 - (void)setValue:(
id)aValue forBinding:(
CPString)aBinding
1076 [_source setSelectedSegment:aValue];
1078 [_source selectSegmentWithTag:aValue];
1080 [_source _selectSegmentWithLabel:aValue];
1083 - (id)valueForBinding:(
CPString)aBinding
1085 var selectedIndex = [_source selectedSegment];
1088 return selectedIndex;
1090 return [_source tagForSegment:selectedIndex];
1092 return [_source labelForSegment:selectedIndex];
1097 @implementation _CPSegmentItem :
CPObject
1112 if (
self = [super
init])
1122 frame = CGRectMakeZero();
1137 @implementation _CPSegmentItem (CPCoding)
1139 - (id)initWithCoder:(
CPCoder)aCoder
1141 self = [
super init];
1145 image = [aCoder decodeObjectForKey:CPSegmentItemImageKey];
1146 label = [aCoder decodeObjectForKey:CPSegmentItemLabelKey];
1147 menu = [aCoder decodeObjectForKey:CPSegmentItemMenuKey];
1148 selected = [aCoder decodeBoolForKey:CPSegmentItemSelectedKey];
1149 enabled = [aCoder decodeBoolForKey:CPSegmentItemEnabledKey];
1150 tag = [aCoder decodeIntForKey:CPSegmentItemTagKey];
1151 width = [aCoder decodeFloatForKey:CPSegmentItemWidthKey];
1153 frame = CGRectMakeZero();
1159 - (void)encodeWithCoder:(
CPCoder)aCoder
1161 [aCoder encodeObject:image forKey:CPSegmentItemImageKey];
1162 [aCoder encodeObject:label forKey:CPSegmentItemLabelKey];
1163 [aCoder encodeObject:menu forKey:CPSegmentItemMenuKey];
1164 [aCoder encodeBool:selected forKey:CPSegmentItemSelectedKey];
1165 [aCoder encodeBool:enabled forKey:CPSegmentItemEnabledKey];
1166 [aCoder encodeInt:tag forKey:CPSegmentItemTagKey];
1167 [aCoder encodeFloat:width forKey:CPSegmentItemWidthKey];