mirror of
https://github.com/transmission/transmission
synced 2024-12-23 08:13:27 +00:00
refactor: remove vector from pieces view (#4587)
This commit is contained in:
parent
e4c5981545
commit
74c8248237
1 changed files with 127 additions and 130 deletions
|
@ -2,10 +2,7 @@
|
|||
// It may be used under the MIT (SPDX: MIT) license.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <libtransmission/transmission.h>
|
||||
#include <libtransmission/utils.h>
|
||||
|
||||
#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<int8_t> 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<NSInteger>(ceil(sqrt(_fNumPieces)));
|
||||
|
||||
CGFloat const width = self.bounds.size.width;
|
||||
_fWidth = static_cast<NSInteger>((width - (_fAcross + 1) * kBetweenPadding) / _fAcross);
|
||||
_fExtraBorder = static_cast<NSInteger>((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<int8_t>{};
|
||||
auto piecesPercent = std::vector<float>{};
|
||||
// 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<NSValue*>* cellBounds = [NSMutableArray arrayWithCapacity:numCells];
|
||||
NSMutableArray<NSColor*>* 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<NSValue*>* fillRects = [NSMutableArray arrayWithCapacity:numPieces];
|
||||
NSMutableArray<NSColor*>* 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
|
||||
|
|
Loading…
Reference in a new issue