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
118 - (void)insertSegments:(CPArray)segments atIndexes:(
CPIndexSet)indices
120 if ([segments count] == 0)
124 count = [indices
count];
127 [newStates addObject:CPThemeStateNormal];
129 [_segments insertObjects:segments atIndexes:indices];
130 [_themeStates insertObjects:newStates atIndexes:indices];
132 if (_selectedSegment >= [indices firstIndex])
133 _selectedSegment += [indices
count];
139 if ([indices count] == 0)
144 [[_segments objectAtIndex:idx] setSelected:NO];
147 if ([indices containsIndex:_selectedSegment])
148 _selectedSegment = -1;
149 else if ([indices lastIndex] < _selectedSegment)
150 _selectedSegment -= [indices
count];
152 [_segments removeObjectsAtIndexes:indices];
153 [_themeStates removeObjectsAtIndexes:indices];
161 - (void)setSegmentCount:(
unsigned)aCount
163 var prevCount = [_segments count];
165 if (aCount == prevCount)
168 if (aCount > prevCount)
170 var count = aCount - prevCount,
174 [segments addObject:[[_CPSegmentItem alloc] init]];
181 [
self _updateSelectionIfNeeded];
185 - (void)_updateSelectionIfNeeded
187 if (_selectedSegment >= [
self segmentCount])
188 _selectedSegment = -1;
194 - (unsigned)segmentCount
196 return [_segments count];
205 - (void)setSelectedSegment:(
unsigned)aSegment
208 if (_selectedSegment == aSegment)
218 _selectedSegment = -1;
227 - (unsigned)selectedSegment
229 return _selectedSegment;
235 - (BOOL)selectSegmentWithTag:(
int)aTag
239 for (; index < [_segments count]; ++index)
240 if ([[_segments objectAtIndex:index]
tag] == aTag)
250 - (BOOL)_selectSegmentWithLabel:(
CPString)aLabel
254 for (; index < [_segments count]; ++index)
255 if ([[_segments objectAtIndex:index] label] == aLabel)
257 [
self setSelectedSegment:index];
272 - (void)setTrackingMode:(CPSegmentSwitchTracking)aTrackingMode
274 if (_trackingMode == aTrackingMode)
277 _trackingMode = aTrackingMode;
287 [
self setSelected:NO forSegment:index];
298 [
self setSelected:NO forSegment:index];
305 - (CPSegmentSwitchTracking)trackingMode
307 return _trackingMode;
317 - (void)setWidth:(
float)aWidth forSegment:(
unsigned)aSegment
319 [[_segments objectAtIndex:aSegment] setWidth:aWidth];
328 - (float)widthForSegment:(
unsigned)aSegment
330 return [[_segments objectAtIndex:aSegment] width];
339 - (void)setImage:(
CPImage)anImage forSegment:(
unsigned)aSegment
341 [[_segments objectAtIndex:aSegment] setImage:anImage];
353 return [[_segments objectAtIndex:aSegment] image];
362 - (void)setLabel:(
CPString)aLabel forSegment:(
unsigned)aSegment
364 [[_segments objectAtIndex:aSegment] setLabel:aLabel];
376 return [[_segments objectAtIndex:aSegment] label];
385 - (void)setMenu:(
CPMenu)aMenu forSegment:(
unsigned)aSegment
387 [[_segments objectAtIndex:aSegment] setMenu:aMenu];
395 - (
CPMenu)menuForSegment:(
unsigned)aSegment
397 return [[_segments objectAtIndex:aSegment] menu];
407 - (void)setSelected:(BOOL)isSelected forSegment:(
unsigned)aSegment
409 var segment = [_segments objectAtIndex:aSegment];
412 if ([segment
selected] == isSelected)
415 [segment setSelected:isSelected];
422 var oldSelectedSegment = _selectedSegment;
424 _selectedSegment = aSegment;
426 if (_trackingMode ==
CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1 && oldSelectedSegment < _segments.length)
428 [_segments[oldSelectedSegment] setSelected:NO];
429 _themeStates[oldSelectedSegment] = CPThemeStateNormal;
431 [self drawSegmentBezel:oldSelectedSegment highlight:NO];
436 [
self drawSegmentBezel:aSegment highlight:NO];
447 - (BOOL)isSelectedForSegment:(
unsigned)aSegment
449 return [[_segments objectAtIndex:aSegment] selected];
458 - (void)setEnabled:(BOOL)shouldBeEnabled forSegment:(
unsigned)aSegment
460 var segment = [_segments objectAtIndex:aSegment];
462 if ([segment
enabled] === shouldBeEnabled)
465 [segment setEnabled:shouldBeEnabled];
481 - (BOOL)isEnabledForSegment:(
unsigned)aSegment
483 return [[_segments objectAtIndex:aSegment] enabled];
491 - (void)setTag:(
int)aTag forSegment:(
unsigned)aSegment
493 [[_segments objectAtIndex:aSegment] setTag:aTag];
500 - (int)tagForSegment:(
unsigned)aSegment
502 return [[_segments objectAtIndex:aSegment] tag];
511 - (void)drawSegmentBezel:(
int)aSegment highlight:(BOOL)shouldHighlight
513 if (aSegment < _themeStates.length)
525 - (float)_leftOffsetForSegment:(
unsigned)segment
528 return [
self currentValueForThemeAttribute:@"bezel-inset"].left;
530 var thickness = [
self currentValueForThemeAttribute:@"divider-thickness"];
532 return [
self _leftOffsetForSegment:segment - 1] + CGRectGetWidth([self frameForSegment:segment - 1]) + thickness;
535 - (unsigned)_indexOfLastSegment
537 var lastSegmentIndex = [_segments count] - 1;
539 if (lastSegmentIndex < 0)
540 lastSegmentIndex = 0;
542 return lastSegmentIndex;
545 - (CGRect)rectForEphemeralSubviewNamed:(
CPString)aName
547 var height = [
self currentValueForThemeAttribute:@"min-size"].height,
548 contentInset = [
self currentValueForThemeAttribute:@"content-inset"],
549 bezelInset = [
self currentValueForThemeAttribute:@"bezel-inset"],
552 if (aName ===
"left-segment-bezel")
554 return CGRectMake(bezelInset.left, bezelInset.top, contentInset.left, height);
556 else if (aName ===
"right-segment-bezel")
558 return CGRectMake(CGRectGetWidth([
self bounds]) - contentInset.right,
563 else if (aName.indexOf(
"segment-bezel") === 0)
565 var segment = parseInt(aName.substring(
"segment-bezel-".length), 10),
566 frame = CGRectCreateCopy([
self frameForSegment:segment]);
570 frame.origin.x += contentInset.left;
571 frame.size.width -= contentInset.left;
574 if (segment === [
self segmentCount] - 1)
575 frame.size.width = CGRectGetWidth([
self bounds]) - contentInset.right -
frame.origin.x;
579 else if (aName.indexOf(
"divider-bezel") === 0)
581 var segment = parseInt(aName.substring(
"divider-bezel-".length), 10),
582 width = CGRectGetWidth([
self frameForSegment:segment]),
583 left = [
self _leftOffsetForSegment:segment],
584 thickness = [
self currentValueForThemeAttribute:@"divider-thickness"];
586 return CGRectMake(left +
width, bezelInset.top, thickness, height);
588 else if (aName.indexOf(
"segment-content") === 0)
590 var segment = parseInt(aName.substring(
"segment-content-".length), 10);
600 if ([aName hasPrefix:
@"segment-content"])
602 var view = [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
603 [view _setUsesSingleLineMode:YES];
608 return [[
CPView alloc] initWithFrame:CGRectMakeZero()];
611 - (void)layoutSubviews
613 if ([
self segmentCount] <= 0)
616 var themeState = _themeStates[0],
617 isDisabled = [
self hasThemeState:CPThemeStateDisabled],
618 isControlSizeSmall = [
self hasThemeState:CPThemeStateControlSizeSmall],
619 isControlSizeMini = [
self hasThemeState:CPThemeStateControlSizeMini];
623 if (isControlSizeSmall)
625 else if (isControlSizeMini)
628 var leftCapColor = [
self valueForThemeAttribute:@"left-segment-bezel-color"
635 [leftBezelView setBackgroundColor:leftCapColor];
637 var themeState = _themeStates[_themeStates.length - 1];
641 if (isControlSizeSmall)
643 else if (isControlSizeMini)
646 var rightCapColor = [
self valueForThemeAttribute:@"right-segment-bezel-color"
653 [rightBezelView setBackgroundColor:rightCapColor];
655 for (var i = 0, count = _themeStates.length; i < count; i++)
657 var themeState = _themeStates[i];
661 if (isControlSizeSmall)
663 else if (isControlSizeMini)
666 var bezelColor = [
self valueForThemeAttribute:@"center-segment-bezel-color"
673 [bezelView setBackgroundColor:bezelColor];
676 var segment = _segments[i],
681 [contentView setText:[segment label]];
682 [contentView setImage:[segment image]];
684 [contentView setFont:[
self valueForThemeAttribute:@"font" inState:themeState]];
685 [contentView setTextColor:[
self valueForThemeAttribute:@"text-color" inState:themeState]];
686 [contentView setAlignment:[
self valueForThemeAttribute:@"alignment" inState:themeState]];
687 [contentView setVerticalAlignment:[
self valueForThemeAttribute:@"vertical-alignment" inState:themeState]];
688 [contentView setLineBreakMode:[
self valueForThemeAttribute:@"line-break-mode" inState:themeState]];
689 [contentView setTextShadowColor:[
self valueForThemeAttribute:@"text-shadow-color" inState:themeState]];
690 [contentView setTextShadowOffset:[
self valueForThemeAttribute:@"text-shadow-offset" inState:themeState]];
691 [contentView setImageScaling:[
self valueForThemeAttribute:@"image-scaling" inState:themeState]];
693 if ([segment image] && [segment
label])
694 [contentView setImagePosition:[
self valueForThemeAttribute:@"image-position" inState:themeState]];
695 else if ([segment image])
696 [contentView setImagePosition:CPImageOnly];
702 var borderState = _themeStates[i].and(_themeStates[i + 1]);
706 if (isControlSizeSmall)
708 else if (isControlSizeMini)
711 var borderColor = [
self valueForThemeAttribute:@"divider-bezel-color"
712 inState:borderState],
718 [borderView setBackgroundColor:borderColor];
728 - (void)drawSegment:(
int)aSegment highlight:(BOOL)shouldHighlight
739 - (void)tileWithChangedSegment:(CPInteger)aSegment
744 if (aSegment < 0 || (segmentCount > 0 && aSegment >= segmentCount))
749 if (segmentCount > 0)
752 for (var i = aSegment; i < segmentCount; i++)
753 [_segments[i] setFrame:CGRectMakeZero()];
755 width = CGRectGetMaxX([
self frameForSegment:(segmentCount - 1)]);
768 - (CGRect)frameForSegment:(
unsigned)aSegment
770 var segment = [_segments objectAtIndex:aSegment],
771 frame = [segment frame];
773 if (CGRectEqualToRect(
frame, CGRectMakeZero()))
776 [segment setFrame:frame];
782 - (CGRect)bezelFrameForSegment:(
unsigned)aSegment
784 var left = [
self _leftOffsetForSegment:aSegment],
785 top = [
self currentValueForThemeAttribute:@"bezel-inset"].top,
787 height = [
self currentValueForThemeAttribute:@"min-size"].height;
792 contentInset = [
self valueForThemeAttribute:@"content-inset" inState:themeState],
793 contentInsetWidth = contentInset.left + contentInset.right,
795 segment = _segments[aSegment],
796 label = [segment label],
797 image = [segment image];
799 width = (
label ? [label sizeWithFont:[
self font]].width : 4.0) + (image ? [image size].width : 0) + contentInsetWidth;
802 return CGRectMake(left, top,
width, height);
805 - (CGRect)contentFrameForSegment:(
unsigned)aSegment
807 var height = [
self currentValueForThemeAttribute:@"min-size"].height,
808 contentInset = [
self currentValueForThemeAttribute:@"content-inset"],
809 width = CGRectGetWidth([
self frameForSegment:aSegment]),
810 left = [
self _leftOffsetForSegment:aSegment];
812 return CGRectMake(left + contentInset.left, contentInset.top,
width - contentInset.left - contentInset.right, height - contentInset.top - contentInset.bottom);
815 - (CGSize)_minimumFrameSize
818 return CGSizeMake(CGRectGetWidth([
self frame]), [
self currentValueForThemeAttribute:
@"min-size"].height);
826 - (unsigned)testSegment:(CGPoint)aPoint
832 if (CGRectContainsPoint([
self frameForSegment:count], aPoint))
835 if ([
self segmentCount])
837 var adjustedLastFrame = CGRectCreateCopy([
self frameForSegment:(_segments.length - 1)]);
838 adjustedLastFrame.size.width = CGRectGetWidth([
self bounds]) - adjustedLastFrame.origin.x;
840 if (CGRectContainsPoint(adjustedLastFrame, aPoint))
849 if (![
self isEnabled])
866 var type = [anEvent
type],
876 if (_trackingSegment === [
self testSegment:location])
883 _selectedSegment = _trackingSegment;
907 if (trackingSegment >
CPNotFound && [
self isEnabledForSegment:trackingSegment])
909 _trackingHighlighted = YES;
910 _trackingSegment = trackingSegment;
921 var highlighted = [
self testSegment:location] === _trackingSegment;
923 if (highlighted != _trackingHighlighted)
925 _trackingHighlighted = highlighted;
933 [CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
959 originalWidth =
frame.size.width;
961 frame.size.width = 0;
965 _segments = [aCoder decodeObjectForKey:CPSegmentedControlSegmentsKey];
966 _segmentStyle = [aCoder decodeIntForKey:CPSegmentedControlSegmentStyleKey];
970 _selectedSegment = [aCoder decodeIntForKey:CPSegmentedControlSelectedKey];
975 _trackingMode = [aCoder decodeIntForKey:CPSegmentedControlTrackingModeKey];
985 var thickness = [
self currentValueForThemeAttribute:@"divider-thickness"],
986 dividerExtraSpace = ([_segments count] - 1) * thickness,
987 difference = MAX(originalWidth - [
self frame].size.width - dividerExtraSpace, 0.0),
988 remainingWidth = FLOOR(difference / [
self segmentCount]),
989 widthOfAllSegments = 0;
994 var
frame = [_segments[i] frame];
995 frame.size.width += remainingWidth;
997 widthOfAllSegments += CGRectGetWidth(
frame);
1002 var leftOversPixel = originalWidth - (widthOfAllSegments + dividerExtraSpace);
1005 if (leftOversPixel < [
self segmentCount] - 1)
1007 for (var i = 0; i < leftOversPixel; i++)
1009 [_segments[i] frame].size.width += 1;
1024 [aCoder encodeObject:_segments forKey:CPSegmentedControlSegmentsKey];
1025 [aCoder encodeInt:_selectedSegment forKey:CPSegmentedControlSelectedKey];
1026 [aCoder encodeInt:_segmentStyle forKey:CPSegmentedControlSegmentStyleKey];
1027 [aCoder encodeInt:_trackingMode forKey:CPSegmentedControlTrackingModeKey];
1034 + (Class)_binderClassForBinding:(
CPString)aBinding
1036 if ([
self _isSelectionBinding:aBinding])
1037 return [_CPSegmentedControlBinder class];
1039 return [
super _binderClassForBinding:aBinding];
1042 + (BOOL)_isSelectionBinding:(
CPString)aBinding
1044 return (aBinding === CPSelectedIndexBinding || aBinding === CPSelectedLabelBinding || aBinding === CPSelectedTagBinding);
1049 return [
self _isSelectionBinding:aBinding];
1056 CPLog.warn(
"Binding " + aBinding +
" needs CPSegmentSwitchTrackingSelectOne tracking mode");
1060 [
super bind:aBinding toObject:anObject withKeyPath:aKeyPath options:options];
1063 - (void)_reverseSetBinding
1065 [_CPSegmentedControlBinder _reverseSetValueFromExclusiveBinderForObject:self];
1071 @implementation _CPSegmentedControlBinder :
CPBinder 1076 - (void)_updatePlaceholdersWithOptions:(
CPDictionary)options
1078 [
super _updatePlaceholdersWithOptions:options];
1080 [
self _setPlaceholder:CPSegmentedControlNoSelectionPlaceholder forMarker:CPMultipleValuesMarker isDefault:YES];
1081 [
self _setPlaceholder:CPSegmentedControlNoSelectionPlaceholder forMarker:CPNoSelectionMarker isDefault:YES];
1082 [
self _setPlaceholder:CPSegmentedControlNoSelectionPlaceholder forMarker:CPNotApplicableMarker isDefault:YES];
1083 [
self _setPlaceholder:CPSegmentedControlNoSelectionPlaceholder forMarker:CPNullMarker isDefault:YES];
1086 - (void)setPlaceholderValue:(
id)aValue withMarker:(
CPString)aMarker forBinding:(
CPString)aBinding
1089 [_source setSelected:NO forSegment:[_source selectedSegment]];
1091 [
self setValue:aValue forBinding:aBinding];
1094 - (void)setValue:(
id)aValue forBinding:(
CPString)aBinding
1096 if (aBinding == CPSelectedIndexBinding)
1097 [_source setSelectedSegment:aValue];
1098 else if (aBinding == CPSelectedTagBinding)
1099 [_source selectSegmentWithTag:aValue];
1100 else if (aBinding == CPSelectedLabelBinding)
1101 [_source _selectSegmentWithLabel:aValue];
1104 - (id)valueForBinding:(
CPString)aBinding
1106 var selectedIndex = [_source selectedSegment];
1108 if (aBinding == CPSelectedIndexBinding)
1109 return selectedIndex;
1110 else if (aBinding == CPSelectedTagBinding)
1111 return [_source tagForSegment:selectedIndex];
1112 else if (aBinding == CPSelectedLabelBinding)
1113 return [_source labelForSegment:selectedIndex];
1118 @implementation _CPSegmentItem :
CPObject 1133 if (
self = [super
init])
1143 frame = CGRectMakeZero();
1158 @implementation _CPSegmentItem (CPCoding)
1160 - (id)initWithCoder:(
CPCoder)aCoder
1162 self = [
super init];
1166 image = [aCoder decodeObjectForKey:CPSegmentItemImageKey];
1167 label = [aCoder decodeObjectForKey:CPSegmentItemLabelKey];
1168 menu = [aCoder decodeObjectForKey:CPSegmentItemMenuKey];
1169 selected = [aCoder decodeBoolForKey:CPSegmentItemSelectedKey];
1170 enabled = [aCoder decodeBoolForKey:CPSegmentItemEnabledKey];
1171 tag = [aCoder decodeIntForKey:CPSegmentItemTagKey];
1172 width = [aCoder decodeFloatForKey:CPSegmentItemWidthKey];
1174 frame = CGRectMakeZero();
1180 - (void)encodeWithCoder:(
CPCoder)aCoder
1182 [aCoder encodeObject:image forKey:CPSegmentItemImageKey];
1183 [aCoder encodeObject:label forKey:CPSegmentItemLabelKey];
1184 [aCoder encodeObject:menu forKey:CPSegmentItemMenuKey];
1185 [aCoder encodeBool:selected forKey:CPSegmentItemSelectedKey];
1186 [aCoder encodeBool:enabled forKey:CPSegmentItemEnabledKey];
1187 [aCoder encodeInt:tag forKey:CPSegmentItemTagKey];
1188 [aCoder encodeFloat:width forKey:CPSegmentItemWidthKey];
var CPSegmentedControlSegmentStyleKey
CPSegmentSwitchTrackingSelectAny
void setFont:(CPFont aFont)
CPThemeStateControlSizeMini
void setSelected:forSegment:(BOOL isSelected, [forSegment] unsigned aSegment)
An object representation of nil.
var CPSegmentedControlSegmentsKey
void setFrame:(CGRect aFrame)
CGPoint locationInWindow()
id initWithFrame:(CGRect aFrame)
void setControlSize:(CPControlSize aControlSize)
A collection of unique integers.
global CPApp typedef CPSegmentSwitchTracking CPSegmentSwitchTrackingSelectOne
float widthForSegment:(unsigned aSegment)
A mutable key-value pair collection.
void drawSegmentBezel:highlight:(int aSegment, [highlight] BOOL shouldHighlight)
void trackSegment:(CPEvent anEvent)
void tileWithChangedSegment:(CPInteger aSegment)
CGRect bezelFrameForSegment:(unsigned aSegment)
BOOL isSelectedForSegment:(unsigned aSegment)
var CPSegmentItemSelectedKey
An immutable string (collection of characters).
CGPoint convertPoint:fromView:(CGPoint aPoint, [fromView] CPView aView)
if(CPFeatureIsCompatible(CPHTMLCanvasFeature))
BOOL sendAction:to:(SEL anAction, [to] id anObject)
id initWithCoder:(CPCoder aCoder)
void setNeedsDisplay:(BOOL aFlag)
var CPSegmentedControlTrackingModeKey
var CPSegmentedControlSelectedKey
void removeSegmentsAtIndexes:(CPIndexSet indices)
Defines methods for use when archiving & restoring (enc/decoding).
CGRect contentFrameForSegment:(unsigned aSegment)
var CPSegmentedControlNoSelectionPlaceholder
void setSelectedSegment:(unsigned aSegment)
void insertSegments:atIndexes:(CPArray segments, [atIndexes] CPIndexSet indices)
void setFrameSize:(CGSize aSize)
var CPSegmentItemWidthKey
var CPSegmentItemImageKey
id indexSetWithIndexesInRange:(CPRange aRange)
unsigned testSegment:(CGPoint aPoint)
CPThemeStateControlSizeSmall
void enumerateIndexesUsingBlock:(Function/*(int idx, @ref BOOL stop) */aFunction)
CPSegmentSwitchTrackingMomentary
void encodeWithCoder:(CPCoder aCoder)
var CPSegmentItemLabelKey
var CPSegmentItemEnabledKey
CPView layoutEphemeralSubviewNamed:positioned:relativeToEphemeralSubviewNamed:(CPString aViewName, [positioned] CPWindowOrderingMode anOrderingMode, [relativeToEphemeralSubviewNamed] CPString relativeToViewName)
CPCenterVerticalTextAlignment
CGRect rectForEphemeralSubviewNamed:(CPString aViewName)