refactor: remove vector from pieces view (#4587)
This commit is contained in:
parent
e4c5981545
commit
74c8248237
|
@ -2,10 +2,7 @@
|
||||||
// It may be used under the MIT (SPDX: MIT) license.
|
// It may be used under the MIT (SPDX: MIT) license.
|
||||||
// License text can be found in the licenses/ folder.
|
// License text can be found in the licenses/ folder.
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <libtransmission/transmission.h>
|
#include <libtransmission/transmission.h>
|
||||||
#include <libtransmission/utils.h>
|
|
||||||
|
|
||||||
#import "PiecesView.h"
|
#import "PiecesView.h"
|
||||||
#import "Torrent.h"
|
#import "Torrent.h"
|
||||||
|
@ -13,31 +10,96 @@
|
||||||
#import "NSApplicationAdditions.h"
|
#import "NSApplicationAdditions.h"
|
||||||
|
|
||||||
static NSInteger const kMaxAcross = 18;
|
static NSInteger const kMaxAcross = 18;
|
||||||
|
static NSInteger const kMaxCells = kMaxAcross * kMaxAcross;
|
||||||
|
|
||||||
static CGFloat const kBetweenPadding = 1.0;
|
static CGFloat const kBetweenPadding = 1.0;
|
||||||
|
|
||||||
static int8_t const kHighPeers = 10;
|
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,
|
int8_t available[kMaxCells];
|
||||||
PIECE_SOME,
|
float complete[kMaxCells];
|
||||||
PIECE_HIGH_PEERS,
|
} PieceInfo;
|
||||||
PIECE_FINISHED,
|
|
||||||
PIECE_FLASHING
|
|
||||||
};
|
|
||||||
|
|
||||||
@interface PiecesView ()
|
@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
|
@end
|
||||||
|
|
||||||
@implementation PiecesView
|
@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
|
- (void)drawRect:(NSRect)dirtyRect
|
||||||
{
|
{
|
||||||
|
@ -63,29 +125,17 @@ enum
|
||||||
|
|
||||||
- (void)setTorrent:(Torrent*)torrent
|
- (void)setTorrent:(Torrent*)torrent
|
||||||
{
|
{
|
||||||
[self clearView];
|
|
||||||
|
|
||||||
_torrent = (torrent && !torrent.magnet) ? torrent : nil;
|
_torrent = (torrent && !torrent.magnet) ? torrent : nil;
|
||||||
if (_torrent)
|
self.image = [[NSImage alloc] initWithSize:self.bounds.size];
|
||||||
{
|
|
||||||
//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 clearView];
|
||||||
[self setNeedsDisplay];
|
[self setNeedsDisplay];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)clearView
|
- (void)clearView
|
||||||
{
|
{
|
||||||
self.fPieces.clear();
|
fRenderedHashString = nil;
|
||||||
|
memset(&fPieceInfo, 0, sizeof(PieceInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateView
|
- (void)updateView
|
||||||
|
@ -95,116 +145,63 @@ enum
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSInteger numPieces = self.fNumPieces;
|
// get the previous state
|
||||||
|
PieceInfo const oldInfo = fPieceInfo;
|
||||||
//determine if first time
|
BOOL const first = ![self.torrent.hashString isEqualToString:fRenderedHashString];
|
||||||
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 current state
|
||||||
BOOL const showAvailability = [NSUserDefaults.standardUserDefaults boolForKey:@"PiecesViewShowAvailability"];
|
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);
|
NSInteger const row = index / across;
|
||||||
[self.torrent getAvailability:std::data(pieces) size:std::size(pieces)];
|
NSInteger const col = index % across;
|
||||||
}
|
|
||||||
else
|
cellBounds[index] = [NSValue valueWithRect:NSMakeRect(
|
||||||
{
|
col * (cellWidth + kBetweenPadding) + kBetweenPadding + extraBorder,
|
||||||
piecesPercent.resize(numPieces);
|
fullWidth - (row + 1) * (cellWidth + kBetweenPadding) - extraBorder,
|
||||||
[self.torrent getAmountFinished:std::data(piecesPercent) size:std::size(piecesPercent)];
|
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];
|
// build an image with the cells
|
||||||
NSMutableArray<NSColor*>* fillColors = [NSMutableArray arrayWithCapacity:numPieces];
|
if (numCells > 0)
|
||||||
|
|
||||||
NSColor* defaultColor = NSApp.darkMode ? NSColor.blackColor : NSColor.whiteColor;
|
|
||||||
|
|
||||||
NSInteger usedCount = 0;
|
|
||||||
|
|
||||||
for (NSInteger index = 0; index < numPieces; index++)
|
|
||||||
{
|
{
|
||||||
NSColor* pieceColor = nil;
|
self.image = [NSImage imageWithSize:self.bounds.size flipped:NO drawingHandler:^BOOL(NSRect /*dstRect*/) {
|
||||||
|
NSRect cFillRects[numCells];
|
||||||
if (showAvailability ? pieces[index] == -1 : piecesPercent[index] == 1.0)
|
for (NSInteger i = 0; i < numCells; ++i)
|
||||||
{
|
{
|
||||||
if (first || self.fPieces[index] != PIECE_FINISHED)
|
cFillRects[i] = cellBounds[i].rectValue;
|
||||||
{
|
|
||||||
if (!first && self.fPieces[index] != PIECE_FLASHING)
|
|
||||||
{
|
|
||||||
pieceColor = NSColor.systemOrangeColor;
|
|
||||||
self.fPieces[index] = PIECE_FLASHING;
|
|
||||||
}
|
}
|
||||||
else
|
NSColor* cFillColors[numCells];
|
||||||
|
for (NSInteger i = 0; i < numCells; ++i)
|
||||||
{
|
{
|
||||||
pieceColor = NSColor.systemBlueColor;
|
cFillColors[i] = cellColors[i];
|
||||||
self.fPieces[index] = PIECE_FINISHED;
|
|
||||||
}
|
}
|
||||||
}
|
NSRectFillListWithColors(cFillRects, cFillColors, numCells);
|
||||||
}
|
|
||||||
else if (showAvailability ? pieces[index] == 0 : piecesPercent[index] == 0.0)
|
|
||||||
{
|
|
||||||
if (first || self.fPieces[index] != PIECE_NONE)
|
|
||||||
{
|
|
||||||
pieceColor = defaultColor;
|
|
||||||
self.fPieces[index] = PIECE_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
return YES;
|
return YES;
|
||||||
}];
|
}];
|
||||||
[self setNeedsDisplay];
|
[self setNeedsDisplay];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// save the current state so we can compare it later
|
||||||
|
fPieceInfo = info;
|
||||||
|
fRenderedHashString = self.torrent.hashString;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)acceptsFirstMouse:(NSEvent*)event
|
- (BOOL)acceptsFirstMouse:(NSEvent*)event
|
||||||
|
|
Loading…
Reference in New Issue