26 #include "../Foundation/Foundation.h" 32 @
typedef CPScrollerPart
44 @
typedef CPUsableScrollerParts
49 var
PARTS_ARRANGEMENT = [CPScrollerKnobSlot, CPScrollerDecrementLine, CPScrollerIncrementLine, CPScrollerKnob],
53 var _CACHED_THEME_SCROLLER = nil;
78 CPUsableScrollerParts _usableParts;
82 float _knobProportion;
84 CPScrollerPart _hitPart;
86 CPScrollerPart _trackingPart;
87 float _trackingFloatValue;
88 CGPoint _trackingStartPoint;
100 #pragma mark Class methods 110 @"scroller-width": 7.0,
115 @"decrement-line-size": CGSizeMakeZero(),
116 @"increment-line-size": CGSizeMakeZero(),
117 @"track-inset": CGInsetMakeZero(),
118 @"knob-inset": CGInsetMakeZero(),
119 @"minimum-knob-length": 21.0,
120 @"track-border-overlay": 9.0
124 + (float)scrollerWidth
132 + (float)scrollerWidthInStyle:(
int)aStyle
134 if (!_CACHED_THEME_SCROLLER)
135 _CACHED_THEME_SCROLLER = [[
self alloc] init];
138 return [_CACHED_THEME_SCROLLER valueForThemeAttribute:@"scroller-width" inState:CPThemeStateScrollViewLegacy];
140 return [_CACHED_THEME_SCROLLER currentValueForThemeAttribute:@"scroller-width"];
146 + (float)scrollerOverlay
148 if (!_CACHED_THEME_SCROLLER)
149 _CACHED_THEME_SCROLLER = [[
self alloc] init];
151 return [_CACHED_THEME_SCROLLER currentValueForThemeAttribute:@"track-border-overlay"];
158 + (float)scrollerWidthForControlSize:(CPControlSize)aControlSize
165 #pragma mark Initialization 167 - (id)initWithFrame:(CGRect)aFrame
169 if (
self = [super initWithFrame:aFrame])
177 _allowFadingOut = YES;
181 var paramAnimFadeOut = @{
187 [_animationScroller setViewAnimations:[paramAnimFadeOut]];
188 [_animationScroller setDelegate:self];
193 [
self _setIsVertical:CGRectGetHeight(aFrame) >= CGRectGetWidth(aFrame)];
201 #pragma mark Getters / Setters 215 - (void)setStyle:(
id)aStyle
217 if (_style != nil && _style === aStyle)
224 _allowFadingOut = NO;
230 _allowFadingOut = YES;
237 - (void)setObjectValue:(
id)aValue
245 - (float)knobProportion
247 return _knobProportion;
254 - (void)setKnobProportion:(
float)aProportion
256 if (!_IS_NUMERIC(aProportion))
259 _knobProportion = MIN(1.0, MAX(0.0001, aProportion));
267 #pragma mark Privates 270 - (void)_adjustScrollerSize
272 var
frame = [
self frame],
273 scrollerWidth = [
self currentValueForThemeAttribute:@"scroller-width"];
275 if ([
self isVertical] && CGRectGetWidth(
frame) !== scrollerWidth)
276 frame.size.width = scrollerWidth;
278 if (![
self isVertical] && CGRectGetHeight(
frame) !== scrollerWidth)
279 frame.size.height = scrollerWidth;
281 [
self setFrame:frame];
285 - (void)_performFadeOut:(
CPTimer)aTimer
293 #pragma mark Utilities 295 - (CGRect)rectForPart:(CPScrollerPart)aPart
298 return CGRectMakeZero();
300 return _partRects[aPart];
308 - (CPScrollerPart)testPart:(CGPoint)aPoint
318 if (CGRectContainsPoint([
self rectForPart:
CPScrollerKnob], aPoint))
342 - (void)checkSpaceForParts
347 if (_knobProportion === 1.0)
351 _partRects[CPScrollerDecrementPage] = CGRectMakeZero();
352 _partRects[CPScrollerKnob] = CGRectMakeZero();
353 _partRects[CPScrollerIncrementPage] = CGRectMakeZero();
354 _partRects[CPScrollerDecrementLine] = CGRectMakeZero();
355 _partRects[CPScrollerIncrementLine] = CGRectMakeZero();
358 _partRects[CPScrollerKnobSlot] = CGRectMakeCopy(
bounds);
369 height = CGRectGetHeight(
bounds);
371 if ([
self isVertical])
375 effectiveDecrementLineHeight = decrementLineSize.height + trackInset.top,
376 effectiveIncrementLineHeight = incrementLineSize.height + trackInset.bottom,
377 slotSize = height - effectiveDecrementLineHeight - effectiveIncrementLineHeight,
379 knobVerticalInset = knobInset.top + knobInset.bottom,
380 knobWidth =
width - knobInset.left - knobInset.right,
381 knobHeight = MAX(minimumKnobLength, ((slotSize - knobVerticalInset) * _knobProportion)),
382 knobLocation = effectiveDecrementLineHeight + (slotSize - knobHeight - knobVerticalInset) * [
self floatValue] + knobInset.top;
384 _partRects[
CPScrollerDecrementPage] = CGRectMake(0.0, effectiveDecrementLineHeight,
width, knobLocation - effectiveDecrementLineHeight);
385 _partRects[CPScrollerKnob] = CGRectMake(knobInset.left, knobLocation, knobWidth, knobHeight);
386 _partRects[CPScrollerIncrementPage] = CGRectMake(0.0, knobLocation + knobHeight,
width, height - (knobLocation + knobHeight) - effectiveIncrementLineHeight);
387 _partRects[CPScrollerKnobSlot] = CGRectMake(trackInset.left, effectiveDecrementLineHeight,
width - trackInset.left - trackInset.right, slotSize);
388 _partRects[CPScrollerDecrementLine] = CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
389 _partRects[CPScrollerIncrementLine] = CGRectMake(0.0, height - incrementLineSize.height, incrementLineSize.width, incrementLineSize.height);
391 if (height < knobHeight + decrementLineSize.height + incrementLineSize.height + trackInset.top + trackInset.bottom)
392 _partRects[CPScrollerKnob] = CGRectMakeZero();
394 if (height < decrementLineSize.height + incrementLineSize.height - 2)
396 _partRects[CPScrollerIncrementLine] = CGRectMakeZero();
397 _partRects[CPScrollerDecrementLine] = CGRectMakeZero();
398 _partRects[CPScrollerKnobSlot] = CGRectMake(trackInset.left, 0,
width - trackInset.left - trackInset.right, height);
405 effectiveDecrementLineWidth = decrementLineSize.width + trackInset.left,
406 effectiveIncrementLineWidth = incrementLineSize.width + trackInset.right,
407 slotSize =
width - effectiveDecrementLineWidth - effectiveIncrementLineWidth,
409 knobHorizontalInset = knobInset.left + knobInset.right,
410 knobWidth = MAX(minimumKnobLength, ((slotSize - knobHorizontalInset) * _knobProportion)),
411 knobHeight = height - knobInset.top - knobInset.bottom,
412 knobLocation = effectiveDecrementLineWidth + (slotSize - knobWidth - knobHorizontalInset) * [
self floatValue] + knobInset.left;
414 _partRects[
CPScrollerDecrementPage] = CGRectMake(effectiveDecrementLineWidth, 0.0, knobLocation - effectiveDecrementLineWidth, height);
415 _partRects[CPScrollerKnob] = CGRectMake(knobLocation, knobInset.top, knobWidth, knobHeight);
416 _partRects[CPScrollerIncrementPage] = CGRectMake(knobLocation + knobWidth, 0.0,
width - (knobLocation + knobWidth) - effectiveIncrementLineWidth, height);
417 _partRects[CPScrollerKnobSlot] = CGRectMake(effectiveDecrementLineWidth, trackInset.top, slotSize, height - trackInset.top - trackInset.bottom);
418 _partRects[CPScrollerDecrementLine] = CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
419 _partRects[CPScrollerIncrementLine] = CGRectMake(
width - incrementLineSize.width, 0.0, incrementLineSize.width, incrementLineSize.height);
421 if (
width < knobWidth + decrementLineSize.width + incrementLineSize.width + trackInset.left + trackInset.right)
422 _partRects[CPScrollerKnob] = CGRectMakeZero();
424 if (
width < decrementLineSize.width + incrementLineSize.width - 2)
426 _partRects[CPScrollerIncrementLine] = CGRectMakeZero();
427 _partRects[CPScrollerDecrementLine] = CGRectMakeZero();
428 _partRects[CPScrollerKnobSlot] = CGRectMake(0.0, 0.0,
width, slotSize);
437 - (CPUsableScrollerParts)usableParts
447 if (_isMouseOver && _knobProportion != 1.0)
451 [_timerFadeOut invalidate];
461 [_animationScroller startAnimation];
473 - (void)drawArrow:(CPScrollerArrow)anArrow highlight:(BOOL)shouldHighlight
491 - (
CPView)createViewForPart:(CPScrollerPart)aPart
493 var view = [[
CPView alloc] initWithFrame:CGRectMakeZero()];
495 [view setHitTests:NO];
500 - (CGRect)rectForEphemeralSubviewNamed:(
CPString)aName
502 return _partRects[aName];
507 var view = [[
CPView alloc] initWithFrame:CGRectMakeZero()];
509 [view setHitTests:NO];
514 - (void)layoutSubviews
516 [
self _adjustScrollerSize];
523 for (; index < count; ++index)
552 - (CPScrollerPart)hitPart
563 var type = [anEvent
type];
581 knobSlotRect = [
self rectForPart:CPScrollerKnobSlot],
582 remainder = ![
self isVertical] ? (CGRectGetWidth(knobSlotRect) - CGRectGetWidth(knobRect)) : (CGRectGetHeight(knobSlotRect) - CGRectGetHeight(knobRect));
589 delta = ![
self isVertical] ? location.x - _trackingStartPoint.x : location.y - _trackingStartPoint.y;
591 [
self setFloatValue:_trackingFloatValue + delta / remainder];
595 [CPApp setTarget:self selector:@selector(trackKnob:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
607 var type = [anEvent
type];
621 _trackingPart = [
self hitPart];
636 knobWidth = ![
self isVertical] ? CGRectGetWidth(knobRect) : CGRectGetHeight(knobRect),
637 knobSlotRect = [
self rectForPart:CPScrollerKnobSlot],
638 remainder = (![
self isVertical] ? CGRectGetWidth(knobSlotRect) : CGRectGetHeight(knobSlotRect)) - knobWidth;
640 [
self setFloatValue:((![
self isVertical] ? _trackingStartPoint.x - CGRectGetMinX(knobSlotRect) : _trackingStartPoint.y - CGRectGetMinY(knobSlotRect)) - knobWidth / 2.0) / remainder];
667 _trackingPart = hitPart;
674 else if (type ==
CPPeriodic && CGRectContainsPoint([
self rectForPart:_trackingPart], _trackingStartPoint))
675 [
self sendAction:[
self action] to:[
self target]];
677 [CPApp setTarget:self selector:@selector(trackScrollButtons:) forNextEventMatchingMask:CPPeriodicMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
681 - (void)_setIsVertical:(BOOL)isVertical
683 _isVertical = isVertical;
686 [
self setThemeState:CPThemeStateVertical];
688 [
self unsetThemeState:CPThemeStateVertical];
691 - (void)setFrameSize:(CGSize)aSize
701 #pragma mark Overrides 703 - (id)currentValueForThemeAttribute:(
CPString)anAttributeName
705 var themeState = _themeState;
710 return [
self valueForThemeAttribute:anAttributeName inState:themeState];
715 if (![
self isEnabled])
738 [_timerFadeOut invalidate];
740 if (![
self isEnabled])
743 _allowFadingOut = NO;
746 if ([
self alphaValue] > 0 && _knobProportion != 1.0)
754 if ([
self isHidden] || ![
self isEnabled] || !_isMouseOver)
761 [_timerFadeOut invalidate];
769 - (float)scrollerWidth
772 return [
self valueForThemeAttribute:@"scroller-width" inState:CPThemeStateScrollViewLegacy];
774 return [
self currentValueForThemeAttribute:@"scroller-width"];
778 #pragma mark Delegates 795 if (
self = [super initWithCoder:aCoder])
797 _knobProportion = 1.0;
800 _knobProportion = [aCoder decodeFloatForKey:CPScrollerKnobProportionKey];
806 _allowFadingOut = YES;
809 var paramAnimFadeOut = @{
815 [_animationScroller setViewAnimations:[paramAnimFadeOut]];
816 [_animationScroller setDelegate:self];
819 [
self setStyle:[aCoder decodeIntForKey:CPScrollerStyleKey]];
821 [
self _setIsVertical:[aCoder decodeBoolForKey:CPScrollerIsVerticalKey]];
831 [aCoder encodeInt:_isVertical forKey:CPScrollerIsVerticalKey];
832 [aCoder encodeFloat:_knobProportion forKey:CPScrollerKnobProportionKey];
833 [aCoder encodeInt:_style forKey:CPScrollerStyleKey];
845 - (void)setFloatValue:(
float)aValue knobProportion:(
float)aProportion
866 - (BOOL)allowFadingOut
868 return _allowFadingOut;
Used to implement exception handling (creating & raising).
BOOL setThemeState:(ThemeState aState)
An object representation of nil.
CPViewAnimationFadeOutEffect
void mouseExited:(CPEvent anEvent)
CGPoint locationInWindow()
void raise:reason:(CPString aName, [reason] CPString aReason)
A mutable key-value pair collection.
An immutable string (collection of characters).
CGPoint convertPoint:fromView:(CGPoint aPoint, [fromView] CPView aView)
void stopPeriodicEvents()
id initWithDuration:animationCurve:(float aDuration, [animationCurve] CPAnimationCurve anAnimationCurve)
BOOL sendAction:to:(SEL anAction, [to] id anObject)
void setFloatValue:(float aValue)
void setNeedsDisplay:(BOOL aFlag)
void setObjectValue:(id anObject)
CPTimer scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:(CPTimeInterval seconds, [target] id aTarget, [selector] SEL aSelector, [userInfo] id userInfo, [repeats] BOOL shouldRepeat)
void setAlphaValue:(float anAlphaValue)
void highlight:(BOOL shouldHighlight)
A timer object that can send a message after the given time interval.
Defines methods for use when archiving & restoring (enc/decoding).
void mouseEntered:(CPEvent anEvent)
BOOL unsetThemeState:(ThemeState aState)
void setFrameSize:(CGSize aSize)
void startPeriodicEventsAfterDelay:withPeriod:(CPTimeInterval aDelay, [withPeriod] CPTimeInterval aPeriod)
void encodeWithCoder:(CPCoder aCoder)
CPView layoutEphemeralSubviewNamed:positioned:relativeToEphemeralSubviewNamed:(CPString aViewName, [positioned] CPWindowOrderingMode anOrderingMode, [relativeToEphemeralSubviewNamed] CPString relativeToViewName)