From 74c8248237b101d93bfb988dd4364aad07d4d485 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Jan 2023 09:10:23 -0600 Subject: [PATCH] refactor: remove vector from pieces view (#4587) --- macosx/PiecesView.mm | 257 +++++++++++++++++++++---------------------- 1 file changed, 127 insertions(+), 130 deletions(-) diff --git a/macosx/PiecesView.mm b/macosx/PiecesView.mm index 28b84a4da..955c11142 100644 --- a/macosx/PiecesView.mm +++ b/macosx/PiecesView.mm @@ -2,10 +2,7 @@ // It may be used under the MIT (SPDX: MIT) license. // License text can be found in the licenses/ folder. -#include - #include -#include #import "PiecesView.h" #import "Torrent.h" @@ -13,31 +10,96 @@ #import "NSApplicationAdditions.h" static NSInteger const kMaxAcross = 18; +static NSInteger const kMaxCells = kMaxAcross * kMaxAcross; + static CGFloat const kBetweenPadding = 1.0; static int8_t const kHighPeers = 10; -enum +static NSColor* const DoneColor = NSColor.systemBlueColor; +static NSColor* const BlinkColor = NSColor.systemOrangeColor; +static NSColor* const HighColor = NSColor.systemGreenColor; // high availability + +typedef struct PieceInfo { - PIECE_NONE, - PIECE_SOME, - PIECE_HIGH_PEERS, - PIECE_FINISHED, - PIECE_FLASHING -}; + int8_t available[kMaxCells]; + float complete[kMaxCells]; +} PieceInfo; @interface PiecesView () - -@property(nonatomic) std::vector fPieces; - -@property(nonatomic) NSInteger fNumPieces; -@property(nonatomic) NSInteger fAcross; -@property(nonatomic) NSInteger fWidth; -@property(nonatomic) NSInteger fExtraBorder; - @end @implementation PiecesView +{ + PieceInfo fPieceInfo; + NSString* fRenderedHashString; +} + +- (NSColor*)backgroundColor +{ + return NSApp.darkMode ? NSColor.blackColor : NSColor.whiteColor; +} + +- (BOOL)isCompletenessDone:(float)val +{ + return val >= 1.0F; +} + +- (BOOL)isCompletenessNone:(float)val +{ + return val <= 0.0F; +} + +- (NSColor*)completenessColor:(float)oldVal newVal:(float)newVal noBlink:(BOOL)noBlink +{ + if ([self isCompletenessDone:newVal]) + { + return noBlink || [self isCompletenessDone:oldVal] ? DoneColor : BlinkColor; + } + + if ([self isCompletenessNone:newVal]) + { + return noBlink || [self isCompletenessNone:oldVal] ? [self backgroundColor] : BlinkColor; + } + + return [[self backgroundColor] blendedColorWithFraction:newVal ofColor:DoneColor]; +} + +- (BOOL)isAvailabilityDone:(uint8_t)val +{ + return val == (uint8_t)-1; +} + +- (BOOL)isAvailabilityNone:(uint8_t)val +{ + return val == 0; +} + +- (BOOL)isAvailabilityHigh:(uint8_t)val +{ + return val >= kHighPeers; +} + +- (NSColor*)availabilityColor:(int8_t)oldVal newVal:(int8_t)newVal noBlink:(bool)noBlink +{ + if ([self isAvailabilityDone:newVal]) + { + return noBlink || [self isAvailabilityDone:oldVal] ? DoneColor : BlinkColor; + } + + if ([self isAvailabilityNone:newVal]) + { + return noBlink || [self isAvailabilityNone:oldVal] ? [self backgroundColor] : BlinkColor; + } + + if ([self isAvailabilityHigh:newVal]) + { + return noBlink || [self isAvailabilityHigh:oldVal] ? HighColor : BlinkColor; + } + + CGFloat percent = CGFloat(newVal) / kHighPeers; + return [[self backgroundColor] blendedColorWithFraction:percent ofColor:HighColor]; +} - (void)drawRect:(NSRect)dirtyRect { @@ -63,29 +125,17 @@ enum - (void)setTorrent:(Torrent*)torrent { - [self clearView]; - _torrent = (torrent && !torrent.magnet) ? torrent : nil; - if (_torrent) - { - //determine relevant values - _fNumPieces = MIN(_torrent.pieceCount, kMaxAcross * kMaxAcross); - _fAcross = static_cast(ceil(sqrt(_fNumPieces))); - - CGFloat const width = self.bounds.size.width; - _fWidth = static_cast((width - (_fAcross + 1) * kBetweenPadding) / _fAcross); - _fExtraBorder = static_cast((width - ((_fWidth + kBetweenPadding) * _fAcross + kBetweenPadding)) / 2); - } - - NSImage* back = [[NSImage alloc] initWithSize:self.bounds.size]; - self.image = back; + self.image = [[NSImage alloc] initWithSize:self.bounds.size]; + [self clearView]; [self setNeedsDisplay]; } - (void)clearView { - self.fPieces.clear(); + fRenderedHashString = nil; + memset(&fPieceInfo, 0, sizeof(PieceInfo)); } - (void)updateView @@ -95,116 +145,63 @@ enum return; } - NSInteger numPieces = self.fNumPieces; - - //determine if first time - BOOL const first = std::empty(self.fPieces); - if (first) - { - _fPieces.resize(numPieces); - } - - auto pieces = std::vector{}; - auto piecesPercent = std::vector{}; + // get the previous state + PieceInfo const oldInfo = fPieceInfo; + BOOL const first = ![self.torrent.hashString isEqualToString:fRenderedHashString]; + // get the current state BOOL const showAvailability = [NSUserDefaults.standardUserDefaults boolForKey:@"PiecesViewShowAvailability"]; - if (showAvailability) + NSInteger const numCells = MIN(_torrent.pieceCount, kMaxCells); + PieceInfo info; + [self.torrent getAvailability:info.available size:numCells]; + [self.torrent getAmountFinished:info.complete size:numCells]; + + // compute bounds and color of each cell + NSInteger const across = (NSInteger)ceil(sqrt(numCells)); + CGFloat const fullWidth = self.bounds.size.width; + NSInteger const cellWidth = (NSInteger)((fullWidth - (across + 1) * kBetweenPadding) / across); + NSInteger const extraBorder = (NSInteger)((fullWidth - ((cellWidth + kBetweenPadding) * across + kBetweenPadding)) / 2); + NSMutableArray* cellBounds = [NSMutableArray arrayWithCapacity:numCells]; + NSMutableArray* cellColors = [NSMutableArray arrayWithCapacity:numCells]; + for (NSInteger index = 0; index < numCells; index++) { - pieces.resize(numPieces); - [self.torrent getAvailability:std::data(pieces) size:std::size(pieces)]; - } - else - { - piecesPercent.resize(numPieces); - [self.torrent getAmountFinished:std::data(piecesPercent) size:std::size(piecesPercent)]; + NSInteger const row = index / across; + NSInteger const col = index % across; + + cellBounds[index] = [NSValue valueWithRect:NSMakeRect( + col * (cellWidth + kBetweenPadding) + kBetweenPadding + extraBorder, + fullWidth - (row + 1) * (cellWidth + kBetweenPadding) - extraBorder, + cellWidth, + cellWidth)]; + + cellColors[index] = showAvailability ? + [self availabilityColor:oldInfo.available[index] newVal:info.available[index] noBlink:first] : + [self completenessColor:oldInfo.complete[index] newVal:info.complete[index] noBlink:first]; } - NSMutableArray* fillRects = [NSMutableArray arrayWithCapacity:numPieces]; - NSMutableArray* fillColors = [NSMutableArray arrayWithCapacity:numPieces]; - - NSColor* defaultColor = NSApp.darkMode ? NSColor.blackColor : NSColor.whiteColor; - - NSInteger usedCount = 0; - - for (NSInteger index = 0; index < numPieces; index++) + // build an image with the cells + if (numCells > 0) { - NSColor* pieceColor = nil; - - if (showAvailability ? pieces[index] == -1 : piecesPercent[index] == 1.0) - { - if (first || self.fPieces[index] != PIECE_FINISHED) + self.image = [NSImage imageWithSize:self.bounds.size flipped:NO drawingHandler:^BOOL(NSRect /*dstRect*/) { + NSRect cFillRects[numCells]; + for (NSInteger i = 0; i < numCells; ++i) { - if (!first && self.fPieces[index] != PIECE_FLASHING) - { - pieceColor = NSColor.systemOrangeColor; - self.fPieces[index] = PIECE_FLASHING; - } - else - { - pieceColor = NSColor.systemBlueColor; - self.fPieces[index] = PIECE_FINISHED; - } + cFillRects[i] = cellBounds[i].rectValue; } - } - else if (showAvailability ? pieces[index] == 0 : piecesPercent[index] == 0.0) - { - if (first || self.fPieces[index] != PIECE_NONE) + NSColor* cFillColors[numCells]; + for (NSInteger i = 0; i < numCells; ++i) { - pieceColor = defaultColor; - self.fPieces[index] = PIECE_NONE; + cFillColors[i] = cellColors[i]; } - } - else if (showAvailability && pieces[index] >= kHighPeers) - { - if (first || self.fPieces[index] != PIECE_HIGH_PEERS) - { - pieceColor = NSColor.systemGreenColor; - self.fPieces[index] = PIECE_HIGH_PEERS; - } - } - else - { - //always redraw "mixed" - CGFloat percent = showAvailability ? (CGFloat)pieces[index] / kHighPeers : piecesPercent[index]; - NSColor* fullColor = showAvailability ? NSColor.systemGreenColor : NSColor.systemBlueColor; - pieceColor = [defaultColor blendedColorWithFraction:percent ofColor:fullColor]; - self.fPieces[index] = PIECE_SOME; - } - - if (pieceColor) - { - NSInteger const across = index % self.fAcross; - NSInteger const down = index / self.fAcross; - fillRects[usedCount] = [NSValue - valueWithRect:NSMakeRect( - across * (self.fWidth + kBetweenPadding) + kBetweenPadding + self.fExtraBorder, - self.bounds.size.width - (down + 1) * (self.fWidth + kBetweenPadding) - self.fExtraBorder, - self.fWidth, - self.fWidth)]; - fillColors[usedCount] = pieceColor; - - usedCount++; - } - } - - if (usedCount > 0) - { - self.image = [NSImage imageWithSize:self.bounds.size flipped:NO drawingHandler:^BOOL(NSRect dstRect) { - NSRect cFillRects[usedCount]; - for (NSInteger i = 0; i < usedCount; ++i) - { - cFillRects[i] = fillRects[i].rectValue; - } - NSColor* cFillColors[usedCount]; - for (NSInteger i = 0; i < usedCount; ++i) - { - cFillColors[i] = fillColors[i]; - } - NSRectFillListWithColors(cFillRects, cFillColors, usedCount); + NSRectFillListWithColors(cFillRects, cFillColors, numCells); return YES; }]; [self setNeedsDisplay]; } + + // save the current state so we can compare it later + fPieceInfo = info; + fRenderedHashString = self.torrent.hashString; } - (BOOL)acceptsFirstMouse:(NSEvent*)event