Revision: 28656 http://sourceforge.net/p/bibdesk/svn/28656 Author: hofman Date: 2024-01-23 15:28:04 +0000 (Tue, 23 Jan 2024) Log Message: ----------- Add progress indicators as subviews. Avoids redrawing the icons. Let the progress indicators manage the timer. Animate changing the progress.
Modified Paths: -------------- trunk/bibdesk_vendorsrc/amaxwell/FileView/FVFileView.h trunk/bibdesk_vendorsrc/amaxwell/FileView/FVFileView.m trunk/bibdesk_vendorsrc/amaxwell/FileView/FVProgressIndicator.h trunk/bibdesk_vendorsrc/amaxwell/FileView/FVProgressIndicator.m Modified: trunk/bibdesk_vendorsrc/amaxwell/FileView/FVFileView.h =================================================================== --- trunk/bibdesk_vendorsrc/amaxwell/FileView/FVFileView.h 2024-01-23 09:38:09 UTC (rev 28655) +++ trunk/bibdesk_vendorsrc/amaxwell/FileView/FVFileView.h 2024-01-23 15:28:04 UTC (rev 28656) @@ -258,7 +258,6 @@ NSDictionary *_contentBinding; NSMutableArray *_downloads; NSMutableDictionary *_progressIndicators; - CFRunLoopTimerRef _progressTimer; NSMutableArray *_accessibilityIcons; NSMutableSet *_modificationSet; NSLock *_modificationLock; Modified: trunk/bibdesk_vendorsrc/amaxwell/FileView/FVFileView.m =================================================================== --- trunk/bibdesk_vendorsrc/amaxwell/FileView/FVFileView.m 2024-01-23 09:38:09 UTC (rev 28655) +++ trunk/bibdesk_vendorsrc/amaxwell/FileView/FVFileView.m 2024-01-23 15:28:04 UTC (rev 28656) @@ -226,7 +226,6 @@ - (void)_showArrowsForIconAtIndex:(NSUInteger)anIndex; - (void)_hideArrows; - (void)_cancelDownloads; -- (void)_invalidateProgressTimer; - (void)_downloadURLAtIndex:(NSUInteger)anIndex replace:(BOOL)replace; - (void)_handleFinderLabelChanged:(NSNotification *)note; - (void)_updateBinding:(NSString *)binding; @@ -419,9 +418,6 @@ // a dictionary of progress indicators _progressIndicators = nil; - // timer to update the view when a download's length is indeterminate - _progressTimer = NULL; - // set of _FVFileKey instances _modificationSet = [NSMutableSet new]; _modificationLock = [NSLock new]; @@ -453,7 +449,6 @@ - (void)dealloc { - [self _invalidateProgressTimer]; CFRunLoopTimerInvalidate(_zombieTimer); CFRelease(_zombieTimer); // this variable is accessed in super's dealloc, so set it to NULL @@ -1657,6 +1652,31 @@ [self _resetTrackingRectsAndToolTips]; } +- (NSRect)_rectOfProgressIndicatorForIconAtIndex:(NSUInteger)anIndex; +{ + NSUInteger r, c; + NSRect frame = NSZeroRect; + if ([self _getGridRow:&r column:&c ofIndex:anIndex]) { + frame = [self _rectOfIconInRow:r column:c]; + NSPoint center = NSMakePoint(NSMidX(frame), NSMidY(frame)); + + CGFloat size = NSHeight(frame) / 2; + frame.size.height = size; + frame.size.width = size; + frame.origin.x = center.x - NSWidth(frame) / 2; + frame.origin.y = center.y - NSHeight(frame) / 2; + } + return frame; +} + +- (void)_updateProgressIndicatorFrames { + if ([_progressIndicators count] > 0) { + [_progressIndicators enumerateKeysAndObjectsUsingBlock:^(NSURL *url, FVProgressIndicator *progressIndicator, BOOL *stop){ + [progressIndicator setFrame:[self _rectOfProgressIndicatorForIconAtIndex:[progressIndicator indexInView]]]; + }]; + } +} + - (void)_resetViewLayout; { // When the tracking rects are active while the view is layed out, mouseEntered may be called even if the mouse does not move. So we remove them before doing the layout. We will reinstall them at the end of this method. @@ -1665,6 +1685,9 @@ // Problem exposed in BibDesk: select all, scroll halfway down in file pane, then change selection to a single row. FVFileView content didn't update correctly, even though reloadIcons was called. Logging drawRect: indicated that the wrong region was being updated, but calling _recalculateGridSize here fixed it. [self _recalculateGridSize]; + // We may have to change the frames of the progress indicators. + [self _updateProgressIndicatorFrames]; + // grid may have changed, so do a full redisplay [self setNeedsDisplay:YES]; @@ -1849,8 +1872,15 @@ } for (NSURL *url in [_progressIndicators allKeys]) { - if ([_orderedURLs containsObject:url] == NO) - [self removeProgressIndicatorForURL:url atIndex:[[_progressIndicators objectForKey:url] indexInView]]; + FVProgressIndicator *progressIndicator = [_progressIndicators objectForKey:url]; + NSUInteger anIndex = [progressIndicator indexInView]; + if ([_orderedURLs containsObject:url] == NO) { + [self removeProgressIndicatorForURL:url atIndex:anIndex]; + } else if (anIndex >= [self numberOfIcons] || [[self URLAtIndex:anIndex] isEqual:url] == NO) { + anIndex = [_orderedURLs indexOfObject:url]; + [progressIndicator setIndexInView:anIndex]; + [progressIndicator setFrame:[self _rectOfProgressIndicatorForIconAtIndex:anIndex]]; + } } [self _resetViewLayout]; @@ -2482,23 +2512,6 @@ CGColorRelease(shadowColor); } -- (NSRect)_rectOfProgressIndicatorForIconAtIndex:(NSUInteger)anIndex; -{ - NSUInteger r, c; - NSRect frame = NSZeroRect; - if ([self _getGridRow:&r column:&c ofIndex:anIndex]) { - frame = [self _rectOfIconInRow:r column:c]; - NSPoint center = NSMakePoint(NSMidX(frame), NSMidY(frame)); - - CGFloat size = NSHeight(frame) / 2; - frame.size.height = size; - frame.size.width = size; - frame.origin.x = center.x - NSWidth(frame) / 2; - frame.origin.y = center.y - NSHeight(frame) / 2; - } - return frame; -} - - (void)_fillBackgroundColorOrGradientInRect:(NSRect)rect forBounds:(NSRect)bounds { // any solid color background should override the gradient code @@ -2604,15 +2617,6 @@ else if (NSIsEmptyRect(_rubberBandRect) == NO) { [self _drawRubberbandRect]; } - - if ([_progressIndicators count]) { - [_progressIndicators enumerateKeysAndObjectsUsingBlock:^(NSURL *url, FVProgressIndicator *progressIndicator, BOOL *stop){ - NSUInteger anIndex = [progressIndicator indexInView]; - // we only draw a if there's an active download for this URL/index pair - if (anIndex < [self numberOfIcons] && [[self URLAtIndex:anIndex] isEqual:url]) - [progressIndicator drawWithFrame:[self _rectOfProgressIndicatorForIconAtIndex:anIndex]]; - }]; - } } #if DEBUG_GRID [[NSColor grayColor] set]; @@ -4095,27 +4099,6 @@ #pragma mark Download support -- (void)_invalidateProgressTimer -{ - if (_progressTimer) { - CFRunLoopTimerInvalidate(_progressTimer); - CFRelease(_progressTimer); - _progressTimer = NULL; - } -} - -- (void)_progressTimerFired:(CFRunLoopTimerRef)timer -{ - NSUInteger r, c; - for (FVProgressIndicator *progressIndicator in [_progressIndicators allValues]) { - if ([progressIndicator style] == FVProgressIndicatorIndeterminate) { - [progressIndicator animate]; - if ([self _getGridRow:&r column:&c ofIndex:[progressIndicator indexInView]]) - [self _setNeedsDisplayForIconInRow:r column:c]; - } - } -} - - (void)downloadUpdated:(FVDownload *)download { [self updateProgressIndicator:[download currentProgress] forURL:[download downloadURL] atIndex:[download indexInView]]; @@ -4221,7 +4204,6 @@ - (void)updateProgressIndicator:(double)progress forURL:(NSURL *)aURL atIndex:(NSUInteger)anIndex { FVProgressIndicator *progressIndicator = [_progressIndicators objectForKey:aURL]; - NSUInteger r, c; if (progressIndicator == nil) { progressIndicator = [[FVProgressIndicator alloc] init]; [progressIndicator setIndexInView:anIndex]; @@ -4228,41 +4210,29 @@ if (_progressIndicators == nil) _progressIndicators = [[NSMutableDictionary alloc] init]; [_progressIndicators setObject:progressIndicator forKey:aURL]; + [progressIndicator setFrame:[self _rectOfProgressIndicatorForIconAtIndex:anIndex]]; + [self addSubview:progressIndicator]; } else if (anIndex != [progressIndicator indexInView]) { - NSUInteger oldIndex = [progressIndicator indexInView]; [progressIndicator setIndexInView:anIndex]; - if (oldIndex < [self numberOfIcons] && [self _getGridRow:&r column:&c ofIndex:oldIndex]) - [self _setNeedsDisplayForIconInRow:r column:c]; + [progressIndicator setFrame:[self _rectOfProgressIndicatorForIconAtIndex:anIndex]]; } if (progress < 0.0) { [progressIndicator setStyle:FVProgressIndicatorIndeterminate]; - if (_progressTimer == NULL) { - // runloop will retain this timer, but we'll retain it too and release in -dealloc - _progressTimer = FVCreateWeakTimerWithTimeInterval(PROGRESS_TIMER_INTERVAL, CFAbsoluteTimeGetCurrent() + PROGRESS_TIMER_INTERVAL, self, @selector(_progressTimerFired:)); - CFRunLoopAddTimer(CFRunLoopGetCurrent(), _progressTimer, kCFRunLoopDefaultMode); - } } else { [progressIndicator setStyle:FVProgressIndicatorDeterminate]; - [progressIndicator setCurrentProgress:progress]; + [[progressIndicator animator] setCurrentProgress:progress]; } - if ([self _getGridRow:&r column:&c ofIndex:anIndex]) - [self _setNeedsDisplayForIconInRow:r column:c]; } - (void)removeProgressIndicatorForURL:(NSURL *)aURL atIndex:(NSUInteger)anIndex { + [[_progressIndicators objectForKey:aURL] removeFromSuperview]; [_progressIndicators removeObjectForKey:aURL]; - if ([_progressIndicators count] == 0 || [[[_progressIndicators allValues] valueForKey:@"style"] containsObject:[NSNumber numberWithInteger:FVProgressIndicatorIndeterminate]] == NO) - [self _invalidateProgressTimer]; - NSUInteger r, c; - if ([self _getGridRow:&r column:&c ofIndex:anIndex]) - [self _setNeedsDisplayForIconInRow:r column:c]; } - (void)resetProgressIndicators { if ([_progressIndicators count] > 0) { + [[_progressIndicators allValues] makeObjectsPerformSelector:@selector(removeFromSuperview)]; [_progressIndicators removeAllObjects]; - [self _invalidateProgressTimer]; - [self setNeedsDisplay:YES]; } for (FVDownload *download in _downloads) { [self updateProgressIndicator:[download currentProgress] forURL:[download downloadURL] atIndex:[download indexInView]]; Modified: trunk/bibdesk_vendorsrc/amaxwell/FileView/FVProgressIndicator.h =================================================================== --- trunk/bibdesk_vendorsrc/amaxwell/FileView/FVProgressIndicator.h 2024-01-23 09:38:09 UTC (rev 28655) +++ trunk/bibdesk_vendorsrc/amaxwell/FileView/FVProgressIndicator.h 2024-01-23 15:28:04 UTC (rev 28656) @@ -51,7 +51,7 @@ FVProgressIndicatorDeterminate = 0 }; -@interface FVProgressIndicator : NSObject +@interface FVProgressIndicator : NSView { @private CGFloat _currentProgress; @@ -58,6 +58,7 @@ NSInteger _currentStep; FVProgressIndicatorStyle _style; NSUInteger _indexInView; + CFRunLoopTimerRef _progressTimer; } @property (nonatomic) CGFloat currentProgress; @@ -67,8 +68,4 @@ @property (nonatomic) NSUInteger indexInView; -- (void)animate; - -- (void)drawWithFrame:(NSRect)aRect; - @end Modified: trunk/bibdesk_vendorsrc/amaxwell/FileView/FVProgressIndicator.m =================================================================== --- trunk/bibdesk_vendorsrc/amaxwell/FileView/FVProgressIndicator.m 2024-01-23 09:38:09 UTC (rev 28655) +++ trunk/bibdesk_vendorsrc/amaxwell/FileView/FVProgressIndicator.m 2024-01-23 15:28:04 UTC (rev 28656) @@ -37,8 +37,15 @@ */ #import "FVProgressIndicator.h" +#import <QuartzCore/QuartzCore.h> +#import "FVUtilities.h" +@interface FVProgressIndicator () +- (void)_startProgressTimer; +- (void)_invalidateProgressTimer; +@end + @implementation FVProgressIndicator @synthesize currentProgress=_currentProgress; @@ -45,17 +52,25 @@ @synthesize style=_style; @synthesize indexInView=_indexInView; -static NSInteger numSteps = 8; ++ (id)defaultAnimationForKey:(NSString *)key { + if ([key isEqualToString:@"currentProgress"]) + return [CABasicAnimation animation]; + return [super defaultAnimationForKey:key]; +} +static NSInteger _numSteps = 8; +static CGFloat _progressTimeInterval = 0.1; + + (void)initialize { FVINITIALIZE(FVProgressIndicator); - if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_15) - numSteps = 12; + if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_15) { + _numSteps = 12; + _progressTimeInterval = 1.0/24.0; + } } -- (id)init -{ +- (id)init { self = [super init]; if (self) { _currentProgress = 0; @@ -65,36 +80,72 @@ return self; } -- (void)animate -{ - if (_style == FVProgressIndicatorIndeterminate) - _currentStep = (_currentStep + 1) % numSteps; +- (void)dealloc { + [self _invalidateProgressTimer]; } -- (void)drawWithFrame:(NSRect)aRect -{ +- (void)setStyle:(FVProgressIndicatorStyle)style { + if (style != _style) { + _style = style; + [self setNeedsDisplay:YES]; + if (_style == FVProgressIndicatorIndeterminate) + [self _startProgressTimer]; + else + [self _invalidateProgressTimer]; + } +} + +- (void)setCurrentProgress:(CGFloat)currentProgress { + _currentProgress = currentProgress; + [self setNeedsDisplay:YES]; +} + +- (void)_progressTimerFired:(CFRunLoopTimerRef)timer { + if (_style == FVProgressIndicatorIndeterminate) { + _currentStep = (_currentStep + 1) % _numSteps; + [self setNeedsDisplay:YES]; + } +} + +- (void)_startProgressTimer { + if (_progressTimer == NULL && [self window]) { + // runloop will retain this timer, but we'll retain it too and release in -dealloc + _progressTimer = FVCreateWeakTimerWithTimeInterval(_progressTimeInterval, _progressTimeInterval * floor(CFAbsoluteTimeGetCurrent() / _progressTimeInterval + 1.0), self, @selector(_progressTimerFired:)); + CFRunLoopAddTimer(CFRunLoopGetCurrent(), _progressTimer, kCFRunLoopDefaultMode); + } +} + +- (void)_invalidateProgressTimer { + if (_progressTimer) { + CFRunLoopTimerInvalidate(_progressTimer); + CFRelease(_progressTimer); + _progressTimer = NULL; + } +} + +- (void)drawRect:(NSRect)dirtyRect { CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; CGContextSaveGState(context); - CGRect progressRect = NSRectToCGRect(aRect); + CGRect progressRect = NSRectToCGRect([self bounds]); CGPoint ctr = CGPointMake(CGRectGetMidX(progressRect), CGRectGetMidY(progressRect)); CGFloat h = CGRectGetHeight(progressRect) / 64.0; if (_style == FVProgressIndicatorIndeterminate) { // indeterminate download length - CGFloat angle = M_PI * 2.0 / numSteps; + CGFloat angle = M_PI * 2.0 / _numSteps; CGContextTranslateCTM(context, ctr.x, ctr.y); CGContextRotateCTM(context, _currentStep * angle); CGContextTranslateCTM(context, -ctr.x, -ctr.y); NSInteger i; - for (i = 0; i < numSteps; i++) { - CGColorRef strokeColor = CGColorCreateGenericGray(0.0, (1.0 - (CGFloat)i / numSteps) * (numSteps - 4.0) / 8.0); + for (i = 0; i < _numSteps; i++) { + CGColorRef strokeColor = CGColorCreateGenericGray(0.0, (1.0 - (CGFloat)i / _numSteps) * (_numSteps - 4.0) / 8.0); CGContextSetStrokeColorWithColor(context, strokeColor); CGColorRelease(strokeColor); - CGContextSetLineWidth(context, (12.0 - numSteps / 2.0) * h); + CGContextSetLineWidth(context, (12.0 - _numSteps / 2.0) * h); CGContextSetLineCap(context, kCGLineCapRound); CGContextBeginPath(context); - CGContextMoveToPoint(context, ctr.x, ctr.y - (14.0 + numSteps / 4.0) * h); - CGContextAddLineToPoint(context, ctr.x, ctr.y - (26.0 + numSteps / 4.0) * h); + CGContextMoveToPoint(context, ctr.x, ctr.y - (14.0 + _numSteps / 4.0) * h); + CGContextAddLineToPoint(context, ctr.x, ctr.y - (26.0 + _numSteps / 4.0) * h); CGContextStrokePath(context); CGContextTranslateCTM(context, ctr.x, ctr.y); CGContextRotateCTM(context, -angle); @@ -139,4 +190,16 @@ CGContextRestoreGState(context); } +- (void)viewWillMoveToWindow:(NSWindow *)newWindow { + [super viewWillMoveToWindow:newWindow]; + if (newWindow == nil) + [self _invalidateProgressTimer]; +} + +- (void)viewDidMoveToWindow { + [super viewDidMoveToWindow]; + if (_style == FVProgressIndicatorIndeterminate) + [self _startProgressTimer]; +} + @end This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. _______________________________________________ Bibdesk-commit mailing list Bibdesk-commit@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/bibdesk-commit