diff --git a/AppKit/CPSlider.j b/AppKit/CPSlider.j index efa3d9209..09a116037 100644 --- a/AppKit/CPSlider.j +++ b/AppKit/CPSlider.j @@ -24,28 +24,35 @@ #include "CoreGraphics/CGGeometry.h" +/*! SLIDER STATES */ + +CPLinearSlider = 0; +CPCircularSlider = 1; @implementation CPSlider : CPControl { double _minValue; double _maxValue; double _altIncrementValue; + int _sliderType; } + (id)themedAttributes { - return [CPDictionary dictionaryWithObjects:[nil, _CGSizeMakeZero(), 0.0, nil, nil] - forKeys:[@"knob-color", @"knob-size", @"track-width", @"vertical-track-color", @"horizontal-track-color"]]; + return [CPDictionary dictionaryWithObjects:[nil, _CGSizeMakeZero(), 0.0, nil] + forKeys:[@"knob-color", @"knob-size", @"track-width", @"track-color"]]; } - (id)initWithFrame:(CGRect)aFrame { self = [super initWithFrame:aFrame]; - + if (self) { _minValue = 0.0; _maxValue = 100.0; + + _sliderType = CPLinearSlider; [self setObjectValue:50.0]; @@ -99,6 +106,26 @@ [self setNeedsDisplay:YES]; } +- (void)setSliderType:(CPSliderType)sliderType +{ + if (sliderType === _sliderType) + return; + + _sliderType = sliderType; + + if (_sliderType === CPCircularSlider) + _controlState |= CPControlStateCircular; + else + _controlState &= ~CPControlStateCircular; + + [self setNeedsLayout]; +} + +- (CPSliderType)sliderType +{ + return _sliderType; +} + - (CGRect)trackRectForBounds:(CGRect)bounds { var trackWidth = [self currentValueForThemedAttributeName:@"track-width"]; @@ -106,15 +133,30 @@ if (trackWidth <= 0) return _CGRectMakeZero(); - if ([self isVertical]) + if ([self sliderType] === CPLinearSlider) { - bounds.origin.x = (_CGRectGetWidth(bounds) - trackWidth) / 2.0; - bounds.size.width = trackWidth; + if ([self isVertical]) + { + bounds.origin.x = (_CGRectGetWidth(bounds) - trackWidth) / 2.0; + bounds.size.width = trackWidth; + } + else + { + bounds.origin.y = (_CGRectGetHeight(bounds) - trackWidth) / 2.0; + bounds.size.height = trackWidth; + } } else { - bounds.origin.y = (_CGRectGetHeight(bounds) - trackWidth) / 2.0; - bounds.size.height = trackWidth; + var originalBounds = CGRectCreateCopy(bounds); + + bounds.size.width = MIN(bounds.size.width, bounds.size.height); + bounds.size.height = bounds.size.width; + + if (bounds.size.width < originalBounds.size.width) + bounds.origin.x += (originalBounds.size.width - bounds.size.width) / 2.0; + else + bounds.origin.y += (originalBounds.size.height - bounds.size.height) / 2.0; } return bounds; @@ -134,17 +176,28 @@ if (!trackRect || _CGRectIsEmpty(trackRect)) trackRect = bounds; - if ([self isVertical]) + if ([self sliderType] === CPLinearSlider) { - knobRect.origin.x = _CGRectGetMidX(trackRect) - knobSize.width / 2.0; - knobRect.origin.y = (([self doubleValue] - _minValue) / (_maxValue - _minValue)) * (_CGRectGetHeight(trackRect) - knobSize.height); + if ([self isVertical]) + { + knobRect.origin.x = _CGRectGetMidX(trackRect) - knobSize.width / 2.0; + knobRect.origin.y = (([self doubleValue] - _minValue) / (_maxValue - _minValue)) * (_CGRectGetHeight(trackRect) - knobSize.height); + } + else + { + knobRect.origin.x = (([self doubleValue] - _minValue) / (_maxValue - _minValue)) * (_CGRectGetWidth(trackRect) - knobSize.width); + knobRect.origin.y = _CGRectGetMidY(trackRect) - knobSize.height / 2.0; + } } else { - knobRect.origin.x = (([self doubleValue] - _minValue) / (_maxValue - _minValue)) * (_CGRectGetWidth(trackRect) - knobSize.width); - knobRect.origin.y = _CGRectGetMidY(trackRect) - knobSize.height / 2.0; + var angle = 3*PI_2 - ([self doubleValue] - _minValue) / (_maxValue - _minValue) * PI2, + radius = CGRectGetWidth(trackRect) / 2.0 - 6.0; + + knobRect.origin.x = radius * COS(angle) + CGRectGetMidX(trackRect) - 3.0; + knobRect.origin.y = radius * SIN(angle) + CGRectGetMidY(trackRect) - 2.0; } - + return knobRect; } @@ -200,9 +253,9 @@ if (trackView) if ([self isVertical]) - [trackView setBackgroundColor:[self currentValueForThemedAttributeName:@"vertical-track-color"]]; + [trackView setBackgroundColor:[self currentValueForThemedAttributeName:@"track-color"]]; else - [trackView setBackgroundColor:[self currentValueForThemedAttributeName:@"horizontal-track-color"]]; + [trackView setBackgroundColor:[self currentValueForThemedAttributeName:@"track-color"]]; var knobView = [self layoutEphemeralSubviewNamed:@"knob-view" positioned:CPWindowAbove @@ -223,27 +276,44 @@ knobRect = [self knobRectForBounds:bounds], trackRect = [self trackRectForBounds:bounds]; - if ([self isVertical]) + if ([self sliderType] === CPLinearSlider) { - var knobHeight = _CGRectGetHeight(knobRect); - - trackRect.origin.y += knobHeight / 2; - trackRect.size.height -= knobHeight; - - var minValue = [self minValue]; - - return MAX(0.0, MIN(1.0, (aPoint.y - _CGRectGetMinY(trackRect)) / _CGRectGetHeight(trackRect))) * ([self maxValue] - minValue) + minValue; + if ([self isVertical]) + { + var knobHeight = _CGRectGetHeight(knobRect); + + trackRect.origin.y += knobHeight / 2; + trackRect.size.height -= knobHeight; + + var minValue = [self minValue]; + + return MAX(0.0, MIN(1.0, (aPoint.y - _CGRectGetMinY(trackRect)) / _CGRectGetHeight(trackRect))) * ([self maxValue] - minValue) + minValue; + } + else + { + var knobWidth = _CGRectGetWidth(knobRect); + + trackRect.origin.x += knobWidth / 2; + trackRect.size.width -= knobWidth; + + var minValue = [self minValue]; + + return MAX(0.0, MIN(1.0, (aPoint.x - _CGRectGetMinX(trackRect)) / _CGRectGetWidth(trackRect))) * ([self maxValue] - minValue) + minValue; + } } else + { + var knobWidth = _CGRectGetWidth(knobRect); - var knobWidth = _CGRectGetWidth(knobRect); - - trackRect.origin.x += knobWidth / 2; - trackRect.size.width -= knobWidth; - - var minValue = [self minValue]; - - return MAX(0.0, MIN(1.0, (aPoint.x - _CGRectGetMinX(trackRect)) / _CGRectGetWidth(trackRect))) * ([self maxValue] - minValue) + minValue; + trackRect.origin.x += knobWidth / 2; + trackRect.size.width -= knobWidth; + + var minValue = [self minValue], + dx = aPoint.x - _CGRectGetMidX(trackRect), + dy = aPoint.y - _CGRectGetMidY(trackRect); + + return MAX(0.0, MIN(1.0, (3*PI_2 - ATAN2(dy, dx))%PI2 / PI2)) * ([self maxValue] - minValue) + minValue; + } } - (BOOL)startTrackingAt:(CGPoint)aPoint @@ -311,7 +381,8 @@ var CPSliderMinValueKey = "CPSliderMinValueKey", CPSliderMaxValueKey = "CPSliderMaxValueKey", - CPSliderAltIncrValueKey = "CPSliderAltIncrValueKey"; + CPSliderAltIncrValueKey = "CPSliderAltIncrValueKey", + CPSliderTypeKey = "CPSliderTypeKey"; @implementation CPSlider (CPCoding) @@ -326,6 +397,7 @@ var CPSliderMinValueKey = "CPSliderMinValueKey", { _altIncrementValue = [aCoder decodeDoubleForKey:CPSliderAltIncrValueKey]; + [self setSliderType:[aCoder decodeIntForkey:CPSliderTypeKey]]; [self setContinuous:YES]; [self setNeedsLayout]; @@ -342,6 +414,7 @@ var CPSliderMinValueKey = "CPSliderMinValueKey", [aCoder encodeDouble:_minValue forKey:CPSliderMinValueKey]; [aCoder encodeDouble:_maxValue forKey:CPSliderMaxValueKey]; [aCoder encodeDouble:_altIncrementValue forKey:CPSliderAltIncrValueKey]; + [aCoder encodeInt:_sliderType forKey:CPSliderTypeKey]; } @end diff --git a/AppKit/CPThemedAttribute.j b/AppKit/CPThemedAttribute.j index a572aeb1a..e25590412 100644 --- a/AppKit/CPThemedAttribute.j +++ b/AppKit/CPThemedAttribute.j @@ -12,6 +12,8 @@ CPControlStateBezeled = 1 << 5, CPControlStateVertical = 1 << 6, CPControlStateEditable = 1 << 7, CPControlStateEditing = 1 << 8; +CPControlStateHorizontal = 1 << 9; +CPControlStateCircular = 1 << 10; var BIT_COUNT = [ 0 /*00000*/, 1 /*00001*/, 1 /*00010*/, 2 /*00011*/, 1 /*00100*/, 2 /*00101*/, 2 /*00110*/, 3 /*00111*/, 1 /*01000*/, 2 /*01001*/, 2 /*01010*/, 3 /*01011*/, 2 /*01100*/, 3 /*01101*/, 3 /*01110*/, 4 /*01111*/, diff --git a/AppKit/Themes/Aristo/Resources/CircularSliderBezel.png b/AppKit/Themes/Aristo/Resources/CircularSliderBezel.png new file mode 100644 index 000000000..fca36e39e Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/CircularSliderBezel.png differ diff --git a/AppKit/Themes/Aristo/Resources/CircularSliderKnob.png b/AppKit/Themes/Aristo/Resources/CircularSliderKnob.png new file mode 100644 index 000000000..5c5164fe6 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/CircularSliderKnob.png differ diff --git a/AppKit/Themes/Aristo/ThemeDescriptors.j b/AppKit/Themes/Aristo/ThemeDescriptors.j index 990e018da..648c13180 100644 --- a/AppKit/Themes/Aristo/ThemeDescriptors.j +++ b/AppKit/Themes/Aristo/ThemeDescriptors.j @@ -7,7 +7,7 @@ */ @import - +@import @implementation AristoThemeDescriptor : CPObject { @@ -315,7 +315,7 @@ isVertical:NO]]; [slider setValue:5.0 forThemedAttributeName:@"track-width"]; - [slider setValue:trackColor forThemedAttributeName:@"horizontal-track-color"]; + [slider setValue:trackColor forThemedAttributeName:@"track-color"]; var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob.png" size:CGSizeMake(23.0, 24.0)]]; knobHighlightedColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob-highlighted.png" size:CGSizeMake(23.0, 24.0)]]; @@ -339,7 +339,7 @@ isVertical:YES]]; [slider setValue:5.0 forThemedAttributeName:@"track-width"]; - [slider setValue:trackColor forThemedAttributeName:@"vertical-track-color"]; + [slider setValue:trackColor forThemedAttributeName:@"track-color"]; var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob.png" size:CGSizeMake(23.0, 24.0)]]; knobHighlightedColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"knob-highlighted.png" size:CGSizeMake(23.0, 24.0)]]; @@ -350,6 +350,26 @@ return slider; } + ++ (CPSlider)themedCircularSlider +{ + var slider = [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 32.0, 32.0)], + trackColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"circularSliderBezel.png" size:CGSizeMake(32.0, 32.0)]]; + + [slider setSliderType:CPCircularSlider]; + [slider setValue:trackColor forThemedAttributeName:@"track-color" inControlState:CPControlStateCircular]; + [slider setValue:trackColor forThemedAttributeName:@"track-color" inControlState:CPControlStateCircular|CPControlStateVertical]; + + var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"circularSliderKnob.png" size:CGSizeMake(5.0, 5.0)]], + knobHighlightedColor = knobColor; + + [slider setValue:CGSizeMake(5.0, 5.0) forThemedAttributeName:@"knob-size" inControlState:CPControlStateCircular]; + [slider setValue:knobColor forThemedAttributeName:@"knob-color" inControlState:CPControlStateCircular]; + [slider setValue:knobHighlightedColor forThemedAttributeName:@"knob-color" inControlState:CPControlStateCircular|CPControlStateHighlighted]; + + return slider; +} + /* var buttonBar = [[CPButtonBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 30.0)], color = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices: