![]() |
API 0.9.5
|
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