26 #include "../Foundation/Foundation.h"
73 CPControlSize _controlSize;
74 CPUsableScrollerParts _usableParts;
78 float _knobProportion;
80 CPScrollerPart _hitPart;
82 CPScrollerPart _trackingPart;
83 float _trackingFloatValue;
84 CGPoint _trackingStartPoint;
96 #pragma mark Class methods
103 + (id)themeAttributes
106 @"scroller-width": 7.0,
111 @"decrement-line-size":_CGSizeMakeZero(),
112 @"increment-line-size":_CGSizeMakeZero(),
113 @"track-inset":_CGInsetMakeZero(),
114 @"knob-inset": _CGInsetMakeZero(),
115 @"minimum-knob-length":21.0,
116 @"track-border-overlay": 9.0
120 + (float)scrollerWidth
128 + (float)scrollerWidthInStyle:(
int)aStyle
130 var scroller = [[
self alloc] init];
133 return [scroller valueForThemeAttribute:@"scroller-width" inState:CPThemeStateScrollViewLegacy];
134 return [scroller currentValueForThemeAttribute:@"scroller-width"];
140 + (float)scrollerOverlay
142 return [[[
self alloc] init] currentValueForThemeAttribute:@"track-border-overlay"];
149 + (float)scrollerWidthForControlSize:(CPControlSize)aControlSize
156 #pragma mark Initialization
158 - (id)initWithFrame:(CGRect)aFrame
160 if (
self = [super initWithFrame:aFrame])
169 _allowFadingOut = YES;
173 forKeys:[CPViewAnimationTargetKey, CPViewAnimationEffectKey]];
176 [_animationScroller setViewAnimations:[paramAnimFadeOut]];
177 [_animationScroller setDelegate:self];
179 [
self _calculateIsVertical];
187 #pragma mark Getters / Setters
201 - (void)setStyle:(
id)aStyle
203 if (_style != nil && _style === aStyle)
215 _allowFadingOut = YES;
219 [
self _adjustScrollerSize];
222 - (void)setObjectValue:(
id)aValue
230 - (CPControlSize)controlSize
239 - (void)setControlSize:(CPControlSize)aControlSize
241 if (_controlSize == aControlSize)
244 _controlSize = aControlSize;
253 - (float)knobProportion
255 return _knobProportion;
262 - (void)setKnobProportion:(
float)aProportion
264 if (!_IS_NUMERIC(aProportion))
267 _knobProportion = MIN(1.0, MAX(0.0001, aProportion));
275 #pragma mark Privates
278 - (void)_adjustScrollerSize
280 var
frame = [
self frame],
281 scrollerWidth = [
self currentValueForThemeAttribute:@"scroller-width"];
283 if ([
self isVertical] && CGRectGetWidth(
frame) !== scrollerWidth)
284 frame.size.width = scrollerWidth;
286 if (![
self isVertical] && CGRectGetHeight(
frame) !== scrollerWidth)
287 frame.size.height = scrollerWidth;
289 [
self setFrame:frame];
293 - (void)_performFadeOut:(
CPTimer)aTimer
301 #pragma mark Utilities
303 - (CGRect)rectForPart:(CPScrollerPart)aPart
306 return _CGRectMakeZero();
308 return _partRects[aPart];
316 - (CPScrollerPart)testPart:(CGPoint)aPoint
326 if (CGRectContainsPoint([
self rectForPart:
CPScrollerKnob], aPoint))
350 - (void)checkSpaceForParts
352 var bounds = [
self bounds];
355 if (_knobProportion === 1.0)
359 _partRects[CPScrollerDecrementPage] = CGRectMakeZero();
360 _partRects[CPScrollerKnob] = CGRectMakeZero();
361 _partRects[CPScrollerIncrementPage] = CGRectMakeZero();
362 _partRects[CPScrollerDecrementLine] = CGRectMakeZero();
363 _partRects[CPScrollerIncrementLine] = CGRectMakeZero();
366 _partRects[CPScrollerKnobSlot] = CGRectMakeCopy(bounds);
376 width = _CGRectGetWidth(bounds),
377 height = _CGRectGetHeight(bounds);
379 if ([
self isVertical])
383 effectiveDecrementLineHeight = decrementLineSize.height + trackInset.top,
384 effectiveIncrementLineHeight = incrementLineSize.height + trackInset.bottom,
385 slotSize = height - effectiveDecrementLineHeight - effectiveIncrementLineHeight,
387 knobWidth =
width - knobInset.left - knobInset.right,
388 knobHeight = MAX(minimumKnobLength, (slotSize * _knobProportion)),
389 knobLocation = effectiveDecrementLineHeight + (slotSize - knobHeight) * [
self floatValue];
391 _partRects[CPScrollerDecrementPage] = _CGRectMake(0.0, effectiveDecrementLineHeight,
width, knobLocation - effectiveDecrementLineHeight);
392 _partRects[CPScrollerKnob] = _CGRectMake(knobInset.left, knobLocation, knobWidth, knobHeight);
393 _partRects[CPScrollerIncrementPage] = _CGRectMake(0.0, knobLocation + knobHeight,
width, height - (knobLocation + knobHeight) - effectiveIncrementLineHeight);
394 _partRects[CPScrollerKnobSlot] = _CGRectMake(trackInset.left, effectiveDecrementLineHeight,
width - trackInset.left - trackInset.right, slotSize);
395 _partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
396 _partRects[CPScrollerIncrementLine] = _CGRectMake(0.0, height - incrementLineSize.height, incrementLineSize.width, incrementLineSize.height);
398 if (height < knobHeight + decrementLineSize.height + incrementLineSize.height + trackInset.top + trackInset.bottom)
399 _partRects[CPScrollerKnob] = _CGRectMakeZero();
401 if (height < decrementLineSize.height + incrementLineSize.height - 2)
403 _partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
404 _partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
405 _partRects[CPScrollerKnobSlot] = _CGRectMake(trackInset.left, 0,
width - trackInset.left - trackInset.right, height);
412 effectiveDecrementLineWidth = decrementLineSize.width + trackInset.left,
413 effectiveIncrementLineWidth = incrementLineSize.width + trackInset.right,
414 slotSize =
width - effectiveDecrementLineWidth - effectiveIncrementLineWidth,
416 knobWidth = MAX(minimumKnobLength, (slotSize * _knobProportion)),
417 knobHeight = height - knobInset.top - knobInset.bottom,
418 knobLocation = effectiveDecrementLineWidth + (slotSize - knobWidth) * [
self floatValue];
420 _partRects[CPScrollerDecrementPage] = _CGRectMake(effectiveDecrementLineWidth, 0.0, knobLocation - effectiveDecrementLineWidth, height);
421 _partRects[CPScrollerKnob] = _CGRectMake(knobLocation, knobInset.top, knobWidth, knobHeight);
422 _partRects[CPScrollerIncrementPage] = _CGRectMake(knobLocation + knobWidth, 0.0,
width - (knobLocation + knobWidth) - effectiveIncrementLineWidth, height);
423 _partRects[CPScrollerKnobSlot] = _CGRectMake(effectiveDecrementLineWidth, trackInset.top, slotSize, height - trackInset.top - trackInset.bottom);
424 _partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
425 _partRects[CPScrollerIncrementLine] = _CGRectMake(
width - incrementLineSize.width, 0.0, incrementLineSize.width, incrementLineSize.height);
427 if (
width < knobWidth + decrementLineSize.width + incrementLineSize.width + trackInset.left + trackInset.right)
428 _partRects[CPScrollerKnob] = _CGRectMakeZero();
430 if (
width < decrementLineSize.width + incrementLineSize.width - 2)
432 _partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
433 _partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
434 _partRects[CPScrollerKnobSlot] = _CGRectMake(0.0, 0.0,
width, slotSize);
443 - (CPUsableScrollerParts)usableParts
453 if (_isMouseOver && _knobProportion != 1.0)
457 [_timerFadeOut invalidate];
470 [_animationScroller startAnimation];
482 - (void)drawArrow:(CPScrollerArrow)anArrow highlight:(BOOL)shouldHighlight
500 - (
CPView)createViewForPart:(CPScrollerPart)aPart
502 var view = [[
CPView alloc] initWithFrame:_CGRectMakeZero()];
504 [view setHitTests:NO];
509 - (CGRect)rectForEphemeralSubviewNamed:(
CPString)aName
511 return _partRects[aName];
516 var view = [[
CPView alloc] initWithFrame:_CGRectMakeZero()];
518 [view setHitTests:NO];
523 - (void)layoutSubviews
531 for (; index < count; ++index)
560 - (CPScrollerPart)hitPart
571 var type = [anEvent
type];
589 knobSlotRect = [
self rectForPart:CPScrollerKnobSlot],
590 remainder = ![
self isVertical] ? (_CGRectGetWidth(knobSlotRect) - _CGRectGetWidth(knobRect)) : (_CGRectGetHeight(knobSlotRect) - _CGRectGetHeight(knobRect));
597 delta = ![
self isVertical] ? location.x - _trackingStartPoint.x : location.y - _trackingStartPoint.y;
599 [
self setFloatValue:_trackingFloatValue + delta / remainder];
603 [CPApp setTarget:self selector:@selector(trackKnob:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
615 var type = [anEvent
type];
629 _trackingPart = [
self hitPart];
644 knobWidth = ![
self isVertical] ? _CGRectGetWidth(knobRect) : _CGRectGetHeight(knobRect),
645 knobSlotRect = [
self rectForPart:CPScrollerKnobSlot],
646 remainder = (![
self isVertical] ? _CGRectGetWidth(knobSlotRect) : _CGRectGetHeight(knobSlotRect)) - knobWidth;
648 [
self setFloatValue:((![
self isVertical] ? _trackingStartPoint.x - _CGRectGetMinX(knobSlotRect) : _trackingStartPoint.y - _CGRectGetMinY(knobSlotRect)) - knobWidth / 2.0) / remainder];
675 _trackingPart = hitPart;
682 else if (type ==
CPPeriodic && CGRectContainsPoint([
self rectForPart:_trackingPart], _trackingStartPoint))
683 [
self sendAction:[
self action] to:[
self target]];
685 [CPApp setTarget:self selector:@selector(trackScrollButtons:) forNextEventMatchingMask:CPPeriodicMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
689 - (void)_calculateIsVertical
692 var bounds = [
self bounds],
693 width = _CGRectGetWidth(bounds),
694 height = _CGRectGetHeight(bounds);
696 _isVertical = width < height ? 1 : (width > height ? 0 : -1);
698 if (_isVertical === 1)
699 [
self setThemeState:CPThemeStateVertical];
700 else if (_isVertical === 0)
701 [
self unsetThemeState:CPThemeStateVertical];
704 - (void)setFrameSize:(CGSize)aSize
714 #pragma mark Overrides
716 - (id)currentValueForThemeAttribute:(
CPString)anAttributeName
718 var themeState = _themeState;
728 if (![
self isEnabled])
749 [_timerFadeOut invalidate];
751 if (![
self isEnabled])
754 _allowFadingOut = NO;
757 if ([
self alphaValue] > 0 && _knobProportion != 1.0)
765 if ([
self isHidden] || ![
self isEnabled] || !_isMouseOver)
768 _allowFadingOut = YES;
772 [_timerFadeOut invalidate];
779 #pragma mark Delegates
796 if (
self = [super initWithCoder:aCoder])
801 _controlSize = [aCoder decodeIntForKey:CPScrollerControlSizeKey];
803 _knobProportion = 1.0;
806 _knobProportion = [aCoder decodeFloatForKey:CPScrollerKnobProportionKey];
812 _allowFadingOut = YES;
816 forKeys:[CPViewAnimationTargetKey, CPViewAnimationEffectKey]];
819 [_animationScroller setViewAnimations:[paramAnimFadeOut]];
820 [_animationScroller setDelegate:self];
823 [
self _calculateIsVertical];
825 [
self setStyle:[aCoder decodeIntForKey:CPScrollerStyleKey]];
835 [aCoder encodeInt:_controlSize forKey:CPScrollerControlSizeKey];
836 [aCoder encodeFloat:_knobProportion forKey:CPScrollerKnobProportionKey];
837 [aCoder encodeInt:_style forKey:CPScrollerStyleKey];
849 - (void)setFloatValue:(
float)aValue knobProportion:(
float)aProportion
870 - (BOOL)allowFadingOut
872 return _allowFadingOut;