mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-26 05:27:03 +00:00
403 lines
10 KiB
Plaintext
403 lines
10 KiB
Plaintext
|
|
@import "CPControl.j"
|
|
@import "CPThemedValue.j"
|
|
|
|
#include "CoreGraphics/CGGeometry.h"
|
|
|
|
|
|
@implementation CPSlider : CPControl
|
|
{
|
|
double _minValue;
|
|
double _maxValue;
|
|
double _altIncrementValue;
|
|
}
|
|
|
|
+ (id)themedAttributes
|
|
{
|
|
return [CPDictionary dictionaryWithObjects:[nil, _CGSizeMakeZero(), 0.0, nil, nil]
|
|
forKeys:[@"knob-color", @"knob-size", @"track-width", @"vertical-track-color", @"horizontal-track-color"]];
|
|
}
|
|
|
|
- (id)initWithFrame:(CGRect)aFrame
|
|
{
|
|
self = [super initWithFrame:aFrame];
|
|
|
|
if (self)
|
|
{
|
|
_minValue = 0.0;
|
|
_maxValue = 100.0;
|
|
|
|
[self setObjectValue:50.0];
|
|
|
|
[self setContinuous:YES];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)setMinValue:(float)aMinimumValue
|
|
{
|
|
if (_minValue === aMinimumValue)
|
|
return;
|
|
|
|
_minValue = aMinimumValue;
|
|
|
|
var doubleValue = [self doubleValue];
|
|
|
|
if (doubleValue < _minValue)
|
|
[self setDoubleValue:_minValue];
|
|
}
|
|
|
|
- (float)minValue
|
|
{
|
|
return _minValue;
|
|
}
|
|
|
|
- (void)setMaxValue:(float)aMaximumValue
|
|
{
|
|
if (_maxValue === aMaximumValue)
|
|
return;
|
|
|
|
_maxValue = aMaximumValue;
|
|
|
|
var doubleValue = [self doubleValue];
|
|
|
|
if (doubleValue > _maxValue)
|
|
[self setDoubleValue:_maxValue];
|
|
}
|
|
|
|
- (float)maxValue
|
|
{
|
|
return _maxValue;
|
|
}
|
|
|
|
- (void)setObjectValue:(id)aValue
|
|
{
|
|
[super setObjectValue:MIN(MAX(aValue, _minValue), _maxValue)];
|
|
|
|
[self setNeedsLayout];
|
|
[self setNeedsDisplay:YES];
|
|
}
|
|
|
|
- (CGRect)trackRectForBounds:(CGRect)bounds
|
|
{
|
|
var trackWidth = [self currentValueForThemedAttributeName:@"track-width"];
|
|
|
|
if (trackWidth <= 0)
|
|
return _CGRectMakeZero();
|
|
|
|
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;
|
|
}
|
|
|
|
return bounds;
|
|
}
|
|
|
|
- (CGRect)knobRectForBounds:(CGRect)bounds
|
|
{
|
|
var knobSize = [self currentValueForThemedAttributeName:@"knob-size"];
|
|
|
|
if (knobSize.width <= 0 || knobSize.height <= 0)
|
|
return _CGRectMakeZero();
|
|
|
|
var knobRect = _CGRectMake(0.0, 0.0, knobSize.width, knobSize.height),
|
|
trackRect = [self trackRectForBounds:bounds];
|
|
|
|
// No track, do our best to approximate a place for this thing.
|
|
if (!trackRect || _CGRectIsEmpty(trackRect))
|
|
trackRect = bounds;
|
|
|
|
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;
|
|
}
|
|
|
|
return knobRect;
|
|
}
|
|
|
|
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
|
|
{
|
|
if (aName === "track-view")
|
|
return [self trackRectForBounds:[self bounds]];
|
|
|
|
else if (aName === "knob-view")
|
|
return [self knobRectForBounds:[self bounds]];
|
|
|
|
return [super rectForEphemeralSubviewNamed:aName];
|
|
}
|
|
|
|
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
|
{
|
|
if (aName === "track-view" || aName === "knob-view")
|
|
{
|
|
var view = [[CPView alloc] init];
|
|
|
|
[view setHitTests:NO];
|
|
|
|
return view;
|
|
}
|
|
|
|
return [super createEphemeralSubviewNamed:aName];
|
|
}
|
|
|
|
- (int)isVertical
|
|
{
|
|
var bounds = [self bounds],
|
|
width = CGRectGetWidth(bounds),
|
|
height = CGRectGetHeight(bounds);
|
|
|
|
return width < height ? 1 : (width > height ? 0 : -1);
|
|
}
|
|
|
|
- (void)layoutSubviews
|
|
{
|
|
var trackView = [self layoutEphemeralSubviewNamed:@"track-view"
|
|
positioned:CPWindowBelow
|
|
relativeToEphemeralSubviewNamed:@"knob-view"];
|
|
|
|
if (trackView)
|
|
if ([self isVertical])
|
|
[trackView setBackgroundColor:[self currentValueForThemedAttributeName:@"vertical-track-color"]];
|
|
else
|
|
[trackView setBackgroundColor:[self currentValueForThemedAttributeName:@"horizontal-track-color"]];
|
|
|
|
var knobView = [self layoutEphemeralSubviewNamed:@"knob-view"
|
|
positioned:CPWindowAbove
|
|
relativeToEphemeralSubviewNamed:@"track-view"];
|
|
|
|
if (knobView)
|
|
[knobView setBackgroundColor:[self currentValueForThemedAttributeName:"knob-color"]];
|
|
}
|
|
|
|
- (BOOL)tracksMouseOutsideOfFrame
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
- (float)_valueAtPoint:(CGPoint)aPoint
|
|
{
|
|
var bounds = [self bounds],
|
|
knobRect = [self knobRectForBounds:bounds],
|
|
trackRect = [self trackRectForBounds:bounds];
|
|
|
|
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;
|
|
}
|
|
|
|
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
|
{
|
|
var bounds = [self bounds],
|
|
knobRect = [self knobRectForBounds:_CGRectMakeCopy(bounds)];
|
|
|
|
if (_CGRectContainsPoint(knobRect, aPoint))
|
|
_dragOffset = _CGSizeMake(_CGRectGetMidX(knobRect) - aPoint.x, _CGRectGetMidY(knobRect) - aPoint.y);
|
|
|
|
else
|
|
{
|
|
var trackRect = [self trackRectForBounds:bounds];
|
|
|
|
if (trackRect && _CGRectContainsPoint(trackRect, aPoint))
|
|
{
|
|
_dragOffset = _CGSizeMakeZero();
|
|
|
|
[self setObjectValue:[self _valueAtPoint:aPoint]];
|
|
}
|
|
|
|
else
|
|
return NO;
|
|
}
|
|
|
|
[self setHighlighted:YES];
|
|
|
|
[self setNeedsLayout];
|
|
[self setNeedsDisplay:YES];
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)continueTracking:(CGPoint)lastPoint at:(CGPoint)aPoint
|
|
{
|
|
[self setObjectValue:[self _valueAtPoint:_CGPointMake(aPoint.x + _dragOffset.width, aPoint.y + _dragOffset.height)]];
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
|
|
{
|
|
[self setHighlighted:NO];
|
|
|
|
if ([_target respondsToSelector:@selector(sliderDidFinish:)])
|
|
[_target sliderDidFinish:self];
|
|
|
|
[self setNeedsLayout];
|
|
[self setNeedsDisplay:YES];
|
|
}
|
|
|
|
/*!
|
|
@ignore
|
|
shoudl we have _continuous?
|
|
*/
|
|
- (void)setContinuous:(BOOL)flag
|
|
{
|
|
if (flag)
|
|
_sendActionOn |= CPLeftMouseDraggedMask;
|
|
else
|
|
_sendActionOn &= ~CPLeftMouseDraggedMask;
|
|
}
|
|
|
|
@end
|
|
/*
|
|
@implementation CPSlider (Theming)
|
|
|
|
- (void)viewDidChangeTheme
|
|
{
|
|
[super viewDidChangeTheme];
|
|
|
|
var theme = [self theme];
|
|
|
|
if (!theme)
|
|
return;
|
|
|
|
[_knobColor setTheme:theme];
|
|
[_knobSize setTheme:theme];
|
|
|
|
[_trackWidth setTheme:theme];
|
|
[_horizontalTrackColor setTheme:theme];
|
|
[_verticalTrackColor setTheme:theme];
|
|
|
|
[self setNeedsLayout];
|
|
[self setNeedsDisplay:YES];
|
|
}
|
|
|
|
- (CPDictionary)themedValues
|
|
{
|
|
var values = [super themedValues];
|
|
|
|
[values setObject:_knobSize forKey:@"knob-size"];
|
|
[values setObject:_knobColor forKey:@"knob-color"];
|
|
[values setObject:_trackWidth forKey:@"track-width"];
|
|
|
|
if ([self isVertical])
|
|
[values setObject:_verticalTrackColor forKey:@"vertical-track-color"];
|
|
else
|
|
[values setObject:_horizontalTrackColor forKey:@"horizontal-track-color"];
|
|
|
|
return values;
|
|
}
|
|
|
|
@end
|
|
*/
|
|
var CPSliderMinValueKey = "CPSliderMinValueKey",
|
|
CPSliderMaxValueKey = "CPSliderMaxValueKey",
|
|
CPSliderAltIncrValueKey = "CPSliderAltIncrValueKey",
|
|
|
|
CPSliderKnobColorKey = "CPSliderKnobColorKey",
|
|
CPSliderKnobSizeKey = "CPSliderKnobSizeKey";
|
|
CPSliderTrackWidthKey = "CPSliderTrackWidthKey";
|
|
CPSliderHorizontalTrackColorKey = "CPSliderHorizontalTrackColorKey";
|
|
CPSliderVerticalTrackColorKey = "CPSliderVerticalTrackColorKey";
|
|
|
|
@implementation CPSlider (CPCoding)
|
|
|
|
- (id)initWithCoder:(CPCoder)aCoder
|
|
{
|
|
self = [super initWithCoder:aCoder];
|
|
|
|
if (self)
|
|
{
|
|
_minValue = [aCoder decodeDoubleForKey:CPSliderMinValueKey];
|
|
_maxValue = [aCoder decodeDoubleForKey:CPSliderMaxValueKey];
|
|
_altIncrementValue = [aCoder decodeDoubleForKey:CPSliderAltIncrValueKey];
|
|
|
|
[self setContinuous:YES];
|
|
|
|
[self setNeedsLayout];
|
|
[self setNeedsDisplay:YES];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
{
|
|
[super encodeWithCoder:aCoder];
|
|
/*
|
|
var count = [_subviews count],
|
|
subviews = nil;
|
|
|
|
if (count)
|
|
{
|
|
subviews = _subviews;
|
|
|
|
if (count === 2 && _trackView && _knobView)
|
|
_subviews = [];
|
|
else
|
|
{
|
|
if (_trackView)
|
|
[_subviews removeObjectIdenticalTo:_trackView];
|
|
if (_knobView)
|
|
[_subviews removeObjectIdenticalTo:_knobView];
|
|
}
|
|
}*/
|
|
|
|
[super encodeWithCoder:aCoder];
|
|
|
|
// _subviews = subviews;
|
|
|
|
[aCoder encodeDouble:_minValue forKey:CPSliderMinValueKey];
|
|
[aCoder encodeDouble:_maxValue forKey:CPSliderMaxValueKey];
|
|
[aCoder encodeDouble:_altIncrementValue forKey:CPSliderAltIncrValueKey];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation CPSlider (Deprecated)
|
|
|
|
- (id)value
|
|
{
|
|
CPLog.warn("[CPSlider value] is deprecated, use doubleValue or objectValue instead.");
|
|
|
|
return [self doubleValue];
|
|
}
|
|
|
|
- (void)setValue:(id)aValue
|
|
{
|
|
CPLog.warn("[CPSlider setValue:] is deprecated, use setDoubleValue: or setObjectValue: instead.");
|
|
|
|
[self setObjectValue:aValue];
|
|
}
|
|
|
|
@end
|