79 CPButtonDefaultHeight = 25.0;
80 CPButtonImageOffset = 3.0;
93 BOOL _allowsMixedState;
98 CPInteger _showsStateBy;
99 CPInteger _highlightsBy;
100 BOOL _imageDimsWhenDisabled;
103 CPBezelStyle _bezelStyle;
104 CPControlSize _controlSize;
107 unsigned _keyEquivalentModifierMask;
111 float _periodicDelay;
112 float _periodicInterval;
117 + (Class)_binderClassForBinding:(
CPString)aBinding
119 if (aBinding === CPTargetBinding || [aBinding hasPrefix:CPArgumentBinding])
122 return [
super _binderClassForBinding:aBinding];
132 var button = [[
self alloc] init];
134 [button setTheme:aTheme];
135 [button setTitle:aTitle];
150 @"image-offset": 0.0,
151 @"bezel-inset": CGInsetMakeZero(),
152 @"content-inset": CGInsetMakeZero(),
162 - (id)initWithFrame:(CGRect)aFrame
169 [
self setValue:CPCenterTextAlignment forThemeAttribute:@"alignment"];
170 [
self setValue:CPCenterVerticalTextAlignment forThemeAttribute:@"vertical-alignment"];
171 [
self setValue:CPImageLeft forThemeAttribute:@"image-position"];
172 [
self setValue:CPImageScaleNone forThemeAttribute:@"image-scaling"];
187 _keyEquivalent =
@"";
188 _keyEquivalentModifierMask = 0;
191 _periodicInterval = 0.05;
192 _periodicDelay = 0.5;
194 [
self setButtonType:CPMomentaryPushInButton];
202 - (BOOL)allowsMixedState
204 return _allowsMixedState;
211 - (void)setAllowsMixedState:(BOOL)aFlag
215 if (_allowsMixedState === aFlag)
218 _allowsMixedState = aFlag;
220 if (!_allowsMixedState && [
self state] ===
CPMixedState)
228 - (void)setObjectValue:(
id)anObjectValue
230 if (!anObjectValue || anObjectValue ===
@"" || ([anObjectValue intValue] === 0))
232 else if (![anObjectValue isKindOfClass:[
CPNumber class]])
237 if ([
self allowsMixedState])
244 switch ([
self objectValue])
247 [
self unsetThemeState:CPThemeStateSelected];
248 [
self setThemeState:CPButtonStateMixed];
250 [
self setThemeState:CPThemeStateHighlighted];
252 [
self unsetThemeState:CPThemeStateHighlighted];
256 [
self unsetThemeState:CPButtonStateMixed];
257 [
self setThemeState:CPThemeStateSelected];
259 [
self setThemeState:CPThemeStateHighlighted];
261 [
self unsetThemeState:CPThemeStateHighlighted];
265 [
self unsetThemeState:CPThemeStateSelected | CPButtonStateMixed | CPThemeStateHighlighted];
276 - (CPInteger)nextState
278 if ([
self allowsMixedState])
280 var value = [
self state];
282 return value - ((value === -1) ? -2 : 1);
285 return 1 - [
self state];
295 if ([
self infoForBinding:CPValueBinding])
306 - (void)setState:(CPInteger)aState
326 if (_title === aTitle)
331 [
self setNeedsLayout];
332 [
self setNeedsDisplay:YES];
347 if (_alternateTitle === aTitle)
350 _alternateTitle = aTitle;
352 [
self setNeedsLayout];
353 [
self setNeedsDisplay:YES];
358 return _alternateTitle;
363 [
self setValue:anImage forThemeAttribute:@"image"];
368 return [
self valueForThemeAttribute:@"image" inState:CPThemeStateNormal];
377 [
self setValue:anImage forThemeAttribute:@"image" inState:CPThemeStateHighlighted];
385 return [
self valueForThemeAttribute:@"image" inState:CPThemeStateHighlighted];
388 - (void)setImageOffset:(
float)theImageOffset
390 [
self setValue:theImageOffset forThemeAttribute:@"image-offset"];
395 return [
self valueForThemeAttribute:@"image-offset"];
398 - (void)setShowsStateBy:(CPInteger)aMask
403 if (_showsStateBy === aMask)
406 _showsStateBy = aMask;
409 [
self setThemeState:CPThemeStateHighlighted];
411 [
self unsetThemeState:CPThemeStateHighlighted];
413 [
self setNeedsDisplay:YES];
414 [
self setNeedsLayout];
417 - (CPInteger)showsStateBy
419 return _showsStateBy;
422 - (void)setHighlightsBy:(CPInteger)aMask
424 if (_highlightsBy === aMask)
427 _highlightsBy = aMask;
429 if ([
self hasThemeState:CPThemeStateHighlighted])
431 [
self setNeedsDisplay:YES];
432 [
self setNeedsLayout];
436 - (CPInteger)highlightsBy
438 return _highlightsBy;
441 - (void)setButtonType:(CPButtonType)aButtonType
446 [
self setHighlightsBy:CPChangeGrayCellMask | CPChangeBackgroundCellMask];
451 [
self setHighlightsBy:CPPushInCellMask | CPChangeGrayCellMask | CPChangeBackgroundCellMask];
461 [
self setHighlightsBy:CPPushInCellMask | CPChangeGrayCellMask | CPChangeBackgroundCellMask];
462 [
self setShowsStateBy:CPChangeBackgroundCellMask | CPChangeGrayCellMask];
466 [
self setHighlightsBy:CPChangeGrayCellMask | CPChangeBackgroundCellMask];
467 [
self setShowsStateBy:CPChangeGrayCellMask | CPChangeBackgroundCellMask];
477 reason:"The CPSwitchButton type is not supported in Cappuccino, use the CPCheckBox class instead."];
481 reason:"The CPRadioButton type is not supported in Cappuccino, use the CPRadio class instead."];
485 reason:"Unknown button type."];
491 - (void)setImageDimsWhenDisabled:(BOOL)imageShouldDimWhenDisabled
493 imageShouldDimWhenDisabled = !!imageShouldDimWhenDisabled;
495 if (_imageDimsWhenDisabled === imageShouldDimWhenDisabled)
498 _imageDimsWhenDisabled = imageShouldDimWhenDisabled;
500 if ([
self hasThemeState:CPThemeStateDisabled])
502 [
self setNeedsDisplay:YES];
503 [
self setNeedsLayout];
507 - (BOOL)imageDimsWhenDisabled
509 return _imageDimsWhenDisabled;
512 - (void)setPeriodicDelay:(
float)aDelay interval:(
float)anInterval
514 _periodicDelay = aDelay;
515 _periodicInterval = anInterval;
520 if ([
self isContinuous])
524 if (!_continuousTimer)
536 if (_target && _action && [_target respondsToSelector:_action])
537 [_target performSelector:_action withObject:self];
540 - (BOOL)startTrackingAt:(CGPoint)aPoint
561 return startedTracking;
564 - (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
568 if (mouseIsUp && CGRectContainsPoint([
self bounds], aPoint))
578 [
self setNeedsLayout];
579 [
self setNeedsDisplay:YES];
583 - (void)invalidateTimers
585 if (_continuousTimer)
587 [_continuousTimer invalidate];
588 _continuousTimer = nil;
591 if (_continuousDelayTimer)
593 [_continuousDelayTimer invalidate];
594 _continuousDelayTimer = nil;
598 - (CGRect)contentRectForBounds:(CGRect)bounds
600 var contentInset = [
self currentValueForThemeAttribute:@"content-inset"];
602 return CGRectInsetByInset(bounds, contentInset);
605 - (CGRect)bezelRectForBounds:(CGRect)bounds
608 if (![
self isBordered])
611 var bezelInset = [
self currentValueForThemeAttribute:@"bezel-inset"];
613 return CGRectInsetByInset(bounds, bezelInset);
616 - (CGSize)_minimumFrameSize
618 var size = CGSizeMakeZero(),
619 contentView = [
self ephemeralSubviewNamed:@"content-view"];
623 [contentView sizeToFit];
624 size = [contentView frameSize];
627 size = [([
self title] || " ") sizeWithFont:[
self currentValueForThemeAttribute:@"font"]];
629 var contentInset = [
self currentValueForThemeAttribute:@"content-inset"],
630 minSize = [
self currentValueForThemeAttribute:@"min-size"],
631 maxSize = [
self currentValueForThemeAttribute:@"max-size"];
633 size.width = MAX(size.width + contentInset.left + contentInset.right, minSize.width);
634 size.height = MAX(size.height + contentInset.top + contentInset.bottom, minSize.height);
636 if (maxSize.width >= 0.0)
637 size.width = MIN(size.width, maxSize.width);
639 if (maxSize.height >= 0.0)
640 size.height = MIN(size.height, maxSize.height);
652 [
self setFrameSize:[
self _minimumFrameSize]];
654 if ([
self ephemeralSubviewNamed:
@"content-view"])
658 - (CGRect)rectForEphemeralSubviewNamed:(
CPString)aName
660 if (aName ===
"bezel-view")
663 else if (aName ===
"content-view")
666 return [
super rectForEphemeralSubviewNamed:aName];
671 if (aName ===
"bezel-view")
673 var view = [[
CPView alloc] initWithFrame:CGRectMakeZero()];
675 [view setHitTests:NO];
680 return [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
683 - (void)layoutSubviews
685 var bezelView = [
self layoutEphemeralSubviewNamed:@"bezel-view"
686 positioned:CPWindowBelow
687 relativeToEphemeralSubviewNamed:@"content-view"];
689 [bezelView setBackgroundColor:[
self currentValueForThemeAttribute:@"bezel-color"]];
691 var contentView = [
self layoutEphemeralSubviewNamed:@"content-view"
692 positioned:CPWindowAbove
693 relativeToEphemeralSubviewNamed:@"bezel-view"];
704 if (_showsStateBy & CPContentsCellMask)
706 title = ([
self state] ==
CPOffState && _alternateTitle) ? _alternateTitle : _title;
715 else if (_showsStateBy & CPContentsCellMask)
717 title = ([
self state] !=
CPOffState && _alternateTitle) ? _alternateTitle : _title;
723 image = [
self image];
730 title = ([
self state] !=
CPOffState && _alternateTitle) ? _alternateTitle : _title;
736 image = [
self image];
740 [contentView setText:title];
741 [contentView setImage:image];
742 [contentView setImageOffset:[
self currentValueForThemeAttribute:@"image-offset"]];
744 [contentView setFont:[
self currentValueForThemeAttribute:@"font"]];
745 [contentView setTextColor:[
self currentValueForThemeAttribute:@"text-color"]];
746 [contentView setAlignment:[
self currentValueForThemeAttribute:@"alignment"]];
747 [contentView setVerticalAlignment:[
self currentValueForThemeAttribute:@"vertical-alignment"]];
748 [contentView setLineBreakMode:[
self currentValueForThemeAttribute:@"line-break-mode"]];
749 [contentView setTextShadowColor:[
self currentValueForThemeAttribute:@"text-shadow-color"]];
750 [contentView setTextShadowOffset:[
self currentValueForThemeAttribute:@"text-shadow-offset"]];
751 [contentView setImagePosition:[
self currentValueForThemeAttribute:@"image-position"]];
752 [contentView setImageScaling:[
self currentValueForThemeAttribute:@"image-scaling"]];
753 [contentView setDimsImage:[
self hasThemeState:CPThemeStateDisabled] && _imageDimsWhenDisabled];
757 - (void)setBordered:(BOOL)shouldBeBordered
759 if (shouldBeBordered)
760 [
self setThemeState:CPThemeStateBordered];
762 [
self unsetThemeState:CPThemeStateBordered];
767 return [
self hasThemeState:CPThemeStateBordered];
778 _keyEquivalent = aString ||
@"";
783 [
self setThemeState:CPThemeStateDefault];
785 [
self unsetThemeState:CPThemeStateDefault];
790 var selfWindow = [
self window];
792 if (selfWindow === aWindow || aWindow === nil)
795 if ([selfWindow defaultButton] ===
self)
796 [selfWindow setDefaultButton:nil];
799 [aWindow setDefaultButton:self];
807 return _keyEquivalent;
813 - (void)setKeyEquivalentModifierMask:(
unsigned)aMask
815 _keyEquivalentModifierMask = aMask;
821 - (unsigned)keyEquivalentModifierMask
823 return _keyEquivalentModifierMask;
833 if ([[
self window] defaultButton] ===
self)
836 if (![anEvent _triggersKeyEquivalent:[
self keyEquivalent] withModifierMask:[
self keyEquivalentModifierMask]])
849 - (void)performClick:(
id)sender
853 if (![
self isEnabled])
858 var shouldHighlight = NO;
865 shouldHighlight = YES;
889 - (void)setBezelStyle:(
unsigned)aBezelStyle
891 if (aBezelStyle === _bezelStyle)
894 var currentState = [CPButtonBezelStyleStateMap objectForKey:_bezelStyle],
895 newState = [CPButtonBezelStyleStateMap objectForKey:aBezelStyle];
898 [
self unsetThemeState:currentState];
901 [
self setThemeState:newState];
903 _bezelStyle = aBezelStyle;
906 - (unsigned)bezelStyle
943 _title = [aCoder decodeObjectForKey:CPButtonTitleKey];
944 _alternateTitle = [aCoder decodeObjectForKey:CPButtonAlternateTitleKey];
945 _allowsMixedState = [aCoder decodeBoolForKey:CPButtonAllowsMixedStateKey];
950 _highlightsBy = [aCoder decodeIntForKey:CPButtonHighlightsByKey];
951 _showsStateBy = [aCoder decodeIntForKey:CPButtonShowsStateByKey];
966 [
self setKeyEquivalent:CFData.decodeBase64ToUtf16String([aCoder decodeObjectForKey:CPButtonKeyEquivalentKey])];
969 _periodicDelay = [aCoder decodeObjectForKey:CPButtonPeriodicDelayKey];
972 _periodicInterval = [aCoder decodeObjectForKey:CPButtonPeriodicIntervalKey];
974 _keyEquivalentModifierMask = [aCoder decodeIntForKey:CPButtonKeyEquivalentMaskKey];
976 [
self setNeedsLayout];
977 [
self setNeedsDisplay:YES];
992 [aCoder encodeObject:_title forKey:CPButtonTitleKey];
993 [aCoder encodeObject:_alternateTitle forKey:CPButtonAlternateTitleKey];
995 [aCoder encodeBool:_allowsMixedState forKey:CPButtonAllowsMixedStateKey];
997 [aCoder encodeInt:_highlightsBy forKey:CPButtonHighlightsByKey];
998 [aCoder encodeInt:_showsStateBy forKey:CPButtonShowsStateByKey];
1001 [aCoder encodeInt:[
self imagePosition] forKey:CPButtonImagePositionKey];
1004 [aCoder encodeObject:CFData.encodeBase64Utf16String(_keyEquivalent) forKey:CPButtonKeyEquivalentKey];
1006 [aCoder encodeInt:_keyEquivalentModifierMask forKey:CPButtonKeyEquivalentMaskKey];
1008 [aCoder encodeObject:_periodicDelay forKey:CPButtonPeriodicDelayKey];
1009 [aCoder encodeObject:_periodicInterval forKey:CPButtonPeriodicIntervalKey];