API 0.9.5
AppKit/CPScroller.j
Go to the documentation of this file.
00001 /*
00002  * CPScroller.j
00003  * AppKit
00004  *
00005  * Created by Francisco Tolmasky.
00006  * Copyright 2008, 280 North, Inc.
00007  *
00008  * Modified to match Lion style by Antoine Mercadal 2011
00009  * <antoine.mercadal@archipelproject.org>
00010  *
00011  * This library is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU Lesser General Public
00013  * License as published by the Free Software Foundation; either
00014  * version 2.1 of the License, or (at your option) any later version.
00015  *
00016  * This library is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00019  * Lesser General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU Lesser General Public
00022  * License along with this library; if not, write to the Free Software
00023  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00024  */
00025 
00026 
00027 // CPScroller Constants
00028 CPScrollerNoPart            = 0;
00029 CPScrollerDecrementPage     = 1;
00030 CPScrollerKnob              = 2;
00031 CPScrollerIncrementPage     = 3;
00032 CPScrollerDecrementLine     = 4;
00033 CPScrollerIncrementLine     = 5;
00034 CPScrollerKnobSlot          = 6;
00035 
00036 CPScrollerIncrementArrow    = 0;
00037 CPScrollerDecrementArrow    = 1;
00038 
00039 CPNoScrollerParts           = 0;
00040 CPOnlyScrollerArrows        = 1;
00041 CPAllScrollerParts          = 2;
00042 
00048 var PARTS_ARRANGEMENT   = [CPScrollerKnobSlot, CPScrollerDecrementLine, CPScrollerIncrementLine, CPScrollerKnob],
00049     NAMES_FOR_PARTS     = {},
00050     PARTS_FOR_NAMES     = {};
00051 
00052 NAMES_FOR_PARTS[CPScrollerDecrementLine]    = @"decrement-line";
00053 NAMES_FOR_PARTS[CPScrollerIncrementLine]    = @"increment-line";
00054 NAMES_FOR_PARTS[CPScrollerKnobSlot]         = @"knob-slot";
00055 NAMES_FOR_PARTS[CPScrollerKnob]             = @"knob";
00056 
00057 
00058 CPScrollerStyleLegacy           = 0;
00059 CPScrollerStyleOverlay          = 1;
00060 
00061 CPScrollerKnobStyleDefault      = 0;
00062 CPScrollerKnobStyleDark         = 1;
00063 CPScrollerKnobStyleLight        = 2;
00064 
00065 CPThemeStateScrollViewLegacy    = CPThemeState("scroller-style-legacy");
00066 CPThemeStateScrollerKnobLight   = CPThemeState("scroller-knob-light");
00067 CPThemeStateScrollerKnobDark    = CPThemeState("scroller-knob-dark");
00068 
00069 @implementation CPScroller : CPControl
00070 {
00071     CPControlSize           _controlSize;
00072     CPUsableScrollerParts   _usableParts;
00073     CPArray                 _partRects;
00074 
00075     BOOL                    _isVertical;
00076     float                   _knobProportion;
00077 
00078     CPScrollerPart          _hitPart;
00079 
00080     CPScrollerPart          _trackingPart;
00081     float                   _trackingFloatValue;
00082     CGPoint                 _trackingStartPoint;
00083 
00084     CPViewAnimation         _animationScroller;
00085 
00086     BOOL                    _allowFadingOut;
00087     int                     _style;
00088     CPTimer                 _timerFadeOut;
00089     BOOL                    _isMouseOver;
00090 }
00091 
00092 
00093 #pragma mark -
00094 #pragma mark Class methods
00095 
00096 + (CPString)defaultThemeClass
00097 {
00098     return "scroller";
00099 }
00100 
00101 + (id)themeAttributes
00102 {
00103     return [CPDictionary dictionaryWithJSObject:{
00104         @"scroller-width": 7.0,
00105         @"knob-slot-color": [CPNull null],
00106         @"decrement-line-color": [CPNull null],
00107         @"increment-line-color": [CPNull null],
00108         @"knob-color": [CPNull null],
00109         @"decrement-line-size":_CGSizeMakeZero(),
00110         @"increment-line-size":_CGSizeMakeZero(),
00111         @"track-inset":_CGInsetMakeZero(),
00112         @"knob-inset": _CGInsetMakeZero(),
00113         @"minimum-knob-length":21.0,
00114         @"track-border-overlay": 9.0
00115     }];
00116 }
00117 
00118 + (float)scrollerWidth
00119 {
00120     return [self scrollerWidthInStyle:CPScrollerStyleLegacy];
00121 }
00122 
00126 + (float)scrollerWidthInStyle:(int)aStyle
00127 {
00128     var scroller = [[self alloc] init];
00129 
00130     if (aStyle == CPScrollerStyleLegacy)
00131         return [scroller valueForThemeAttribute:@"scroller-width" inState:CPThemeStateScrollViewLegacy];
00132     return [scroller currentValueForThemeAttribute:@"scroller-width"];
00133 }
00134 
00138 + (float)scrollerOverlay
00139 {
00140     return [[[self alloc] init] currentValueForThemeAttribute:@"track-border-overlay"];
00141 }
00142 
00147 + (float)scrollerWidthForControlSize:(CPControlSize)aControlSize
00148 {
00149     return [self scrollerWidth];
00150 }
00151 
00152 
00153 #pragma mark -
00154 #pragma mark Initialization
00155 
00156 - (id)initWithFrame:(CGRect)aFrame
00157 {
00158     if (self = [super initWithFrame:aFrame])
00159     {
00160         _controlSize = CPRegularControlSize;
00161         _partRects = [];
00162 
00163         [self setFloatValue:0.0];
00164         [self setKnobProportion:1.0];
00165 
00166         _hitPart = CPScrollerNoPart;
00167         _allowFadingOut = YES;
00168         _isMouseOver = NO;
00169         _style = CPScrollerStyleOverlay;
00170         var paramAnimFadeOut   = [CPDictionary dictionaryWithObjects:[self, CPViewAnimationFadeOutEffect]
00171                                                           forKeys:[CPViewAnimationTargetKey, CPViewAnimationEffectKey]];
00172 
00173         _animationScroller = [[CPViewAnimation alloc] initWithDuration:0.2 animationCurve:CPAnimationEaseInOut];
00174         [_animationScroller setViewAnimations:[paramAnimFadeOut]];
00175         [_animationScroller setDelegate:self];
00176         [self setAlphaValue:0.0];
00177         [self _calculateIsVertical];
00178     }
00179 
00180     return self;
00181 }
00182 
00183 
00184 #pragma mark -
00185 #pragma mark Getters / Setters
00186 
00190 - (void)style
00191 {
00192     return _style;
00193 }
00194 
00199 - (void)setStyle:(id)aStyle
00200 {
00201     if (_style != nil && _style === aStyle)
00202         return;
00203 
00204     _style = aStyle;
00205 
00206     if (_style === CPScrollerStyleLegacy)
00207     {
00208         [self fadeIn];
00209         [self setThemeState:CPThemeStateScrollViewLegacy];
00210     }
00211     else
00212     {
00213         _allowFadingOut = YES;
00214         [self unsetThemeState:CPThemeStateScrollViewLegacy];
00215     }
00216 
00217     [self _adjustScrollerSize];
00218 }
00219 
00220 - (void)setObjectValue:(id)aValue
00221 {
00222     [super setObjectValue:MIN(1.0, MAX(0.0, +aValue))];
00223 }
00224 
00228 - (CPControlSize)controlSize
00229 {
00230     return _controlSize;
00231 }
00232 
00237 - (void)setControlSize:(CPControlSize)aControlSize
00238 {
00239     if (_controlSize == aControlSize)
00240         return;
00241 
00242     _controlSize = aControlSize;
00243 
00244     [self setNeedsLayout];
00245     [self setNeedsDisplay:YES];
00246 }
00247 
00251 - (float)knobProportion
00252 {
00253     return _knobProportion;
00254 }
00255 
00260 - (void)setKnobProportion:(float)aProportion
00261 {
00262     _knobProportion = MIN(1.0, MAX(0.0001, aProportion));
00263 
00264     [self setNeedsDisplay:YES];
00265     [self setNeedsLayout];
00266 }
00267 
00268 
00269 #pragma mark -
00270 #pragma mark Privates
00271 
00273 - (void)_adjustScrollerSize
00274 {
00275     var frame = [self frame],
00276         scrollerWidth = [self currentValueForThemeAttribute:@"scroller-width"];
00277 
00278     if ([self isVertical] && CGRectGetWidth(frame) !== scrollerWidth)
00279         frame.size.width = scrollerWidth;
00280 
00281     if (![self isVertical] && CGRectGetHeight(frame) !== scrollerWidth)
00282         frame.size.height = scrollerWidth;
00283 
00284     [self setFrame:frame];
00285 }
00286 
00288 - (void)_performFadeOut:(CPTimer)aTimer
00289 {
00290     [self fadeOut];
00291     _timerFadeOut = nil;
00292 }
00293 
00294 
00295 #pragma mark -
00296 #pragma mark Utilities
00297 
00298 - (CGRect)rectForPart:(CPScrollerPart)aPart
00299 {
00300     if (aPart == CPScrollerNoPart)
00301         return _CGRectMakeZero();
00302 
00303     return _partRects[aPart];
00304 }
00305 
00311 - (CPScrollerPart)testPart:(CGPoint)aPoint
00312 {
00313     aPoint = [self convertPoint:aPoint fromView:nil];
00314 
00315     // The ordering of these tests is important.  We check the knob and
00316     // page rects first since they may overlap with the arrows.
00317 
00318     if (![self hasThemeState:CPThemeStateSelected])
00319         return CPScrollerNoPart;
00320 
00321     if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint))
00322         return CPScrollerKnob;
00323 
00324     if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementPage], aPoint))
00325         return CPScrollerDecrementPage;
00326 
00327     if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementPage], aPoint))
00328         return CPScrollerIncrementPage;
00329 
00330     if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementLine], aPoint))
00331         return CPScrollerDecrementLine;
00332 
00333     if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementLine], aPoint))
00334         return CPScrollerIncrementLine;
00335 
00336     if (CGRectContainsPoint([self rectForPart:CPScrollerKnobSlot], aPoint))
00337         return CPScrollerKnobSlot;
00338 
00339     return CPScrollerNoPart;
00340 }
00341 
00345 - (void)checkSpaceForParts
00346 {
00347     var bounds = [self bounds];
00348 
00349     // Assume we won't be needing the arrows.
00350     if (_knobProportion === 1.0)
00351     {
00352         _usableParts = CPNoScrollerParts;
00353 
00354         _partRects[CPScrollerDecrementPage] = CGRectMakeZero();
00355         _partRects[CPScrollerKnob]          = CGRectMakeZero();
00356         _partRects[CPScrollerIncrementPage] = CGRectMakeZero();
00357         _partRects[CPScrollerDecrementLine] = CGRectMakeZero();
00358         _partRects[CPScrollerIncrementLine] = CGRectMakeZero();
00359 
00360         // In this case, the slot is the entirety of the scroller.
00361         _partRects[CPScrollerKnobSlot] = CGRectMakeCopy(bounds);
00362 
00363         return;
00364     }
00365 
00366     // At this point we know we're going to need arrows.
00367     _usableParts = CPAllScrollerParts;
00368 
00369     var knobInset = [self currentValueForThemeAttribute:@"knob-inset"],
00370         trackInset = [self currentValueForThemeAttribute:@"track-inset"],
00371         width = _CGRectGetWidth(bounds),
00372         height = _CGRectGetHeight(bounds);
00373 
00374     if ([self isVertical])
00375     {
00376         var decrementLineSize = [self currentValueForThemeAttribute:"decrement-line-size"],
00377             incrementLineSize = [self currentValueForThemeAttribute:"increment-line-size"],
00378             effectiveDecrementLineHeight = decrementLineSize.height + trackInset.top,
00379             effectiveIncrementLineHeight = incrementLineSize.height + trackInset.bottom,
00380             slotSize = height - effectiveDecrementLineHeight - effectiveIncrementLineHeight,
00381             minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
00382             knobWidth = width - knobInset.left - knobInset.right,
00383             knobHeight = MAX(minimumKnobLength, (slotSize * _knobProportion)),
00384             knobLocation = effectiveDecrementLineHeight + (slotSize - knobHeight) * [self floatValue];
00385 
00386         _partRects[CPScrollerDecrementPage] = _CGRectMake(0.0, effectiveDecrementLineHeight, width, knobLocation - effectiveDecrementLineHeight);
00387         _partRects[CPScrollerKnob]          = _CGRectMake(knobInset.left, knobLocation, knobWidth, knobHeight);
00388         _partRects[CPScrollerIncrementPage] = _CGRectMake(0.0, knobLocation + knobHeight, width, height - (knobLocation + knobHeight) - effectiveIncrementLineHeight);
00389         _partRects[CPScrollerKnobSlot]      = _CGRectMake(trackInset.left, effectiveDecrementLineHeight, width - trackInset.left - trackInset.right, slotSize);
00390         _partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
00391         _partRects[CPScrollerIncrementLine] = _CGRectMake(0.0, height - incrementLineSize.height, incrementLineSize.width, incrementLineSize.height);
00392 
00393         if (height < knobHeight + decrementLineSize.height + incrementLineSize.height + trackInset.top + trackInset.bottom)
00394             _partRects[CPScrollerKnob] = _CGRectMakeZero();
00395 
00396         if (height < decrementLineSize.height + incrementLineSize.height - 2)
00397         {
00398             _partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
00399             _partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
00400             _partRects[CPScrollerKnobSlot]      = _CGRectMake(trackInset.left, 0,  width - trackInset.left - trackInset.right, height);
00401         }
00402     }
00403     else
00404     {
00405         var decrementLineSize = [self currentValueForThemeAttribute:"decrement-line-size"],
00406             incrementLineSize = [self currentValueForThemeAttribute:"increment-line-size"],
00407             effectiveDecrementLineWidth = decrementLineSize.width + trackInset.left,
00408             effectiveIncrementLineWidth = incrementLineSize.width + trackInset.right,
00409             slotSize = width - effectiveDecrementLineWidth - effectiveIncrementLineWidth,
00410             minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
00411             knobWidth = MAX(minimumKnobLength, (slotSize * _knobProportion)),
00412             knobHeight = height - knobInset.top - knobInset.bottom,
00413             knobLocation = effectiveDecrementLineWidth + (slotSize - knobWidth) * [self floatValue];
00414 
00415         _partRects[CPScrollerDecrementPage] = _CGRectMake(effectiveDecrementLineWidth, 0.0, knobLocation - effectiveDecrementLineWidth, height);
00416         _partRects[CPScrollerKnob]          = _CGRectMake(knobLocation, knobInset.top, knobWidth, knobHeight);
00417         _partRects[CPScrollerIncrementPage] = _CGRectMake(knobLocation + knobWidth, 0.0, width - (knobLocation + knobWidth) - effectiveIncrementLineWidth, height);
00418         _partRects[CPScrollerKnobSlot]      = _CGRectMake(effectiveDecrementLineWidth, trackInset.top, slotSize, height - trackInset.top - trackInset.bottom);
00419         _partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
00420         _partRects[CPScrollerIncrementLine] = _CGRectMake(width - incrementLineSize.width, 0.0, incrementLineSize.width, incrementLineSize.height);
00421 
00422         if (width < knobWidth + decrementLineSize.width + incrementLineSize.width + trackInset.left + trackInset.right)
00423             _partRects[CPScrollerKnob] = _CGRectMakeZero();
00424 
00425         if (width < decrementLineSize.width + incrementLineSize.width - 2)
00426         {
00427             _partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
00428             _partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
00429             _partRects[CPScrollerKnobSlot]      = _CGRectMake(0.0, 0.0,  width, slotSize);
00430         }
00431     }
00432 }
00433 
00438 - (CPUsableScrollerParts)usableParts
00439 {
00440     return _usableParts;
00441 }
00442 
00446 - (void)fadeIn
00447 {
00448     if (_isMouseOver && _knobProportion != 1.0)
00449         [self setThemeState:CPThemeStateSelected];
00450 
00451     if (_timerFadeOut)
00452         [_timerFadeOut invalidate];
00453 
00454     [self setAlphaValue:1.0];
00455 }
00456 
00460 - (void)fadeOut
00461 {
00462     if ([self hasThemeState:CPThemeStateScrollViewLegacy])
00463         return;
00464 
00465     [_animationScroller startAnimation];
00466 }
00467 
00468 
00469 #pragma mark -
00470 #pragma mark  Drawing
00471 
00477 - (void)drawArrow:(CPScrollerArrow)anArrow highlight:(BOOL)shouldHighlight
00478 {
00479 }
00480 
00484 - (void)drawKnob
00485 {
00486 }
00487 
00491 - (void)drawKnobSlot
00492 {
00493 }
00494 
00495 - (CPView)createViewForPart:(CPScrollerPart)aPart
00496 {
00497     var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
00498 
00499     [view setHitTests:NO];
00500 
00501     return view;
00502 }
00503 
00504 - (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
00505 {
00506     return _partRects[aName];
00507 }
00508 
00509 - (CPView)createEphemeralSubviewNamed:(CPString)aName
00510 {
00511     var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
00512 
00513     [view setHitTests:NO];
00514 
00515     return view;
00516 }
00517 
00518 - (void)layoutSubviews
00519 {
00520     [self checkSpaceForParts];
00521 
00522     var index = 0,
00523         count = PARTS_ARRANGEMENT.length;
00524 
00525     for (; index < count; ++index)
00526     {
00527         var part = PARTS_ARRANGEMENT[index];
00528 
00529         if (index === 0)
00530             view = [self layoutEphemeralSubviewNamed:part positioned:CPWindowBelow relativeToEphemeralSubviewNamed:PARTS_ARRANGEMENT[index + 1]];
00531         else
00532             view = [self layoutEphemeralSubviewNamed:part positioned:CPWindowAbove relativeToEphemeralSubviewNamed:PARTS_ARRANGEMENT[index - 1]];
00533 
00534         if (view)
00535             [view setBackgroundColor:[self currentValueForThemeAttribute:NAMES_FOR_PARTS[part] + "-color"]];
00536     }
00537 }
00538 
00542 - (void)drawParts
00543 {
00544     [self drawKnobSlot];
00545     [self drawKnob];
00546     [self drawArrow:CPScrollerDecrementArrow highlight:NO];
00547     [self drawArrow:CPScrollerIncrementArrow highlight:NO];
00548 }
00549 
00550 // Event Handling
00554 - (CPScrollerPart)hitPart
00555 {
00556     return _hitPart;
00557 }
00558 
00563 - (void)trackKnob:(CPEvent)anEvent
00564 {
00565     var type = [anEvent type];
00566 
00567     if (type === CPLeftMouseUp)
00568     {
00569         _hitPart = CPScrollerNoPart;
00570 
00571         return;
00572     }
00573 
00574     if (type === CPLeftMouseDown)
00575     {
00576         _trackingFloatValue = [self floatValue];
00577         _trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
00578     }
00579 
00580     else if (type === CPLeftMouseDragged)
00581     {
00582         var knobRect = [self rectForPart:CPScrollerKnob],
00583             knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
00584             remainder = ![self isVertical] ? (_CGRectGetWidth(knobSlotRect) - _CGRectGetWidth(knobRect)) : (_CGRectGetHeight(knobSlotRect) - _CGRectGetHeight(knobRect));
00585 
00586         if (remainder <= 0)
00587             [self setFloatValue:0.0];
00588         else
00589         {
00590             var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
00591                 delta = ![self isVertical] ? location.x - _trackingStartPoint.x : location.y - _trackingStartPoint.y;
00592 
00593             [self setFloatValue:_trackingFloatValue + delta / remainder];
00594         }
00595     }
00596 
00597     [CPApp setTarget:self selector:@selector(trackKnob:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
00598 
00599     if (type === CPLeftMouseDragged)
00600         [self sendAction:[self action] to:[self target]];
00601 }
00602 
00607 - (void)trackScrollButtons:(CPEvent)anEvent
00608 {
00609     var type = [anEvent type];
00610 
00611     if (type === CPLeftMouseUp)
00612     {
00613         [self highlight:NO];
00614         [CPEvent stopPeriodicEvents];
00615 
00616         _hitPart = CPScrollerNoPart;
00617 
00618         return;
00619     }
00620 
00621     if (type === CPLeftMouseDown)
00622     {
00623         _trackingPart = [self hitPart];
00624 
00625         _trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
00626 
00627         if ([anEvent modifierFlags] & CPAlternateKeyMask)
00628         {
00629             if (_trackingPart == CPScrollerDecrementLine)
00630                 _hitPart = CPScrollerDecrementPage;
00631 
00632             else if (_trackingPart == CPScrollerIncrementLine)
00633                 _hitPart = CPScrollerIncrementPage;
00634 
00635             else if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
00636             {
00637                 var knobRect = [self rectForPart:CPScrollerKnob],
00638                     knobWidth = ![self isVertical] ? _CGRectGetWidth(knobRect) : _CGRectGetHeight(knobRect),
00639                     knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
00640                     remainder = (![self isVertical] ? _CGRectGetWidth(knobSlotRect) : _CGRectGetHeight(knobSlotRect)) - knobWidth;
00641 
00642                 [self setFloatValue:((![self isVertical] ? _trackingStartPoint.x - _CGRectGetMinX(knobSlotRect) : _trackingStartPoint.y - _CGRectGetMinY(knobSlotRect)) - knobWidth / 2.0) / remainder];
00643 
00644                 _hitPart = CPScrollerKnob;
00645 
00646                 [self sendAction:[self action] to:[self target]];
00647 
00648                 // Now just track the knob.
00649                 return [self trackKnob:anEvent];
00650             }
00651         }
00652 
00653         [self highlight:YES];
00654         [self sendAction:[self action] to:[self target]];
00655 
00656         [CPEvent startPeriodicEventsAfterDelay:0.5 withPeriod:0.04];
00657     }
00658 
00659     else if (type === CPLeftMouseDragged)
00660     {
00661         _trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
00662 
00663         if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
00664         {
00665             var hitPart = [self testPart:[anEvent locationInWindow]];
00666 
00667             if (hitPart == CPScrollerDecrementPage || hitPart == CPScrollerIncrementPage)
00668             {
00669                 _trackingPart = hitPart;
00670                 _hitPart = hitPart;
00671             }
00672         }
00673 
00674         [self highlight:CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint)];
00675     }
00676     else if (type == CPPeriodic && CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint))
00677         [self sendAction:[self action] to:[self target]];
00678 
00679     [CPApp setTarget:self selector:@selector(trackScrollButtons:) forNextEventMatchingMask:CPPeriodicMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
00680 
00681 }
00682 
00683 - (void)_calculateIsVertical
00684 {
00685     // Recalculate isVertical.
00686     var bounds = [self bounds],
00687         width = _CGRectGetWidth(bounds),
00688         height = _CGRectGetHeight(bounds);
00689 
00690     _isVertical = width < height ? 1 : (width > height ? 0 : -1);
00691 
00692     if (_isVertical === 1)
00693         [self setThemeState:CPThemeStateVertical];
00694     else if (_isVertical === 0)
00695         [self unsetThemeState:CPThemeStateVertical];
00696 }
00697 
00698 - (void)setFrameSize:(CGSize)aSize
00699 {
00700     [super setFrameSize:aSize];
00701 
00702     [self checkSpaceForParts];
00703     [self setNeedsLayout];
00704 }
00705 
00706 
00707 #pragma mark -
00708 #pragma mark Overrides
00709 
00710 - (id)currentValueForThemeAttribute:(CPString)anAttributeName
00711 {
00712     var themeState = _themeState;
00713 
00714     if (NAMES_FOR_PARTS[_hitPart] + "-color" !== anAttributeName)
00715         themeState &= ~CPThemeStateHighlighted;
00716 
00717     return [self valueForThemeAttribute:anAttributeName inState:themeState];
00718 }
00719 
00720 - (void)mouseDown:(CPEvent)anEvent
00721 {
00722     if (![self isEnabled])
00723         return;
00724 
00725     _hitPart = [self testPart:[anEvent locationInWindow]];
00726 
00727     switch (_hitPart)
00728     {
00729         case CPScrollerKnob:            return [self trackKnob:anEvent];
00730 
00731         case CPScrollerDecrementLine:
00732         case CPScrollerIncrementLine:
00733         case CPScrollerDecrementPage:
00734         case CPScrollerIncrementPage:   return [self trackScrollButtons:anEvent];
00735     }
00736 }
00737 
00738 - (void)mouseEntered:(CPEvent)anEvent
00739 {
00740     [super mouseEntered:anEvent];
00741 
00742     if (_timerFadeOut)
00743         [_timerFadeOut invalidate];
00744 
00745     if (![self isEnabled])
00746         return;
00747 
00748     _allowFadingOut = NO;
00749     _isMouseOver = YES;
00750 
00751     if ([self alphaValue] > 0 && _knobProportion != 1.0)
00752         [self setThemeState:CPThemeStateSelected];
00753 }
00754 
00755 - (void)mouseExited:(CPEvent)anEvent
00756 {
00757     [super mouseExited:anEvent];
00758 
00759     if ([self isHidden] || ![self isEnabled] || !_isMouseOver)
00760         return;
00761 
00762     _allowFadingOut = YES;
00763     _isMouseOver = NO;
00764 
00765     if (_timerFadeOut)
00766         [_timerFadeOut invalidate];
00767     _timerFadeOut = [CPTimer scheduledTimerWithTimeInterval:1.2 target:self selector:@selector(_performFadeOut:) userInfo:nil repeats:NO];
00768 }
00769 
00770 
00771 #pragma mark -
00772 #pragma mark Delegates
00773 
00774 - (void)animationDidEnd:(CPAnimation)animation
00775 {
00776     [self unsetThemeState:CPThemeStateSelected];
00777 }
00778 
00779 @end
00780 
00781 var CPScrollerControlSizeKey = @"CPScrollerControlSize",
00782     CPScrollerKnobProportionKey = @"CPScrollerKnobProportion",
00783     CPScrollerStyleKey = @"CPScrollerStyleKey";
00784 
00785 @implementation CPScroller (CPCoding)
00786 
00787 - (id)initWithCoder:(CPCoder)aCoder
00788 {
00789     if (self = [super initWithCoder:aCoder])
00790     {
00791         _controlSize = CPRegularControlSize;
00792         if ([aCoder containsValueForKey:CPScrollerControlSizeKey])
00793             _controlSize = [aCoder decodeIntForKey:CPScrollerControlSizeKey];
00794 
00795         _knobProportion = 1.0;
00796         if ([aCoder containsValueForKey:CPScrollerKnobProportionKey])
00797             _knobProportion = [aCoder decodeFloatForKey:CPScrollerKnobProportionKey];
00798 
00799         _partRects = [];
00800 
00801         _hitPart = CPScrollerNoPart;
00802 
00803         _allowFadingOut = YES;
00804         _isMouseOver = NO;
00805         var paramAnimFadeOut   = [CPDictionary dictionaryWithObjects:[self, CPViewAnimationFadeOutEffect]
00806                                                           forKeys:[CPViewAnimationTargetKey, CPViewAnimationEffectKey]];
00807         _animationScroller = [[CPViewAnimation alloc] initWithDuration:0.2 animationCurve:CPAnimationEaseInOut];
00808         [_animationScroller setViewAnimations:[paramAnimFadeOut]];
00809         [_animationScroller setDelegate:self];
00810         [self setAlphaValue:0.0];
00811 
00812         [self _calculateIsVertical];
00813 
00814         [self setStyle:[aCoder decodeIntForKey:CPScrollerStyleKey]];
00815     }
00816 
00817     return self;
00818 }
00819 
00820 - (void)encodeWithCoder:(CPCoder)aCoder
00821 {
00822     [super encodeWithCoder:aCoder];
00823 
00824     [aCoder encodeInt:_controlSize forKey:CPScrollerControlSizeKey];
00825     [aCoder encodeFloat:_knobProportion forKey:CPScrollerKnobProportionKey];
00826     [aCoder encodeInt:_style forKey:CPScrollerStyleKey];
00827 }
00828 
00829 @end
00830 
00831 @implementation CPScroller (Deprecated)
00832 
00838 - (void)setFloatValue:(float)aValue knobProportion:(float)aProportion
00839 {
00840     [self setFloatValue:aValue];
00841     [self setKnobProportion:aProportion];
00842 }
00843 
00844 @end
00845 
00846 @implementation CPScroller (CPSynthesizedAccessors)
00847 
00851 - (BOOL)isVertical
00852 {
00853     return _isVertical;
00854 }
00855 
00859 - (BOOL)allowFadingOut
00860 {
00861     return _allowFadingOut;
00862 }
00863 
00864 @end
 All Classes Files Functions Variables Defines