//
// PSView.m
// Perforce
//
// Created by Adam Czubernat on 06.06.2013.
// Copyright (c) 2013 Perforce Software, Inc. All rights reserved.
//
#import "PSView.h"
#import <objc/message.h>
@interface PSView () {
NSDictionary *targets;
NSDictionary *selectors;
}
- (void)drawImage:(NSRect)dirtyRect;
@end
@implementation PSView
@synthesize backgroundColor, drawingBlock, image, contentMode;
- (id)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
image = [coder decodeObjectForKey:@"image"];
backgroundColor = [coder decodeObjectForKey:@"backgroundColor"];
drawingBlock = [coder decodeObjectForKey:@"drawingBlock"];
contentMode = [[coder decodeObjectForKey:@"contentMode"] intValue];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];
[coder encodeObject:image forKey:@"image"];
[coder encodeObject:backgroundColor forKey:@"backgroundColor"];
[coder encodeObject:drawingBlock forKey:@"drawingBlock"];
[coder encodeObject:@(contentMode) forKey:@"contentMode"];
}
- (void)drawRect:(NSRect)dirtyRect {
// Draw background
if (backgroundColor) {
[backgroundColor setFill];
NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver);
}
// Draw image
if (image)
[self drawImage:dirtyRect];
// Custom drawing block
if (drawingBlock)
drawingBlock(self, dirtyRect);
}
- (void)mouseDown:(NSEvent *)theEvent {
PSView *strongSelf = self;
[super mouseDown:theEvent];
if ([theEvent clickCount] == 1) {
[strongSelf sendActionsForEvents:PSViewEventClick];
} else if ([theEvent clickCount] > 1) {
[strongSelf sendActionsForEvents:PSViewEventDoubleClick];
}
}
// Fix to NSScrollview bottom
- (NSRect)adjustScroll:(NSRect)newVisible {
CGRect scrollRect = [self enclosingScrollView].frame;
if (scrollRect.size.height > self.frame.size.height) {
newVisible.origin.y = -scrollRect.size.height + self.frame.size.height;
return newVisible;
}
return [super adjustScroll:newVisible];
}
#pragma mark - Private
- (void)drawImage:(NSRect)dirtyRect {
NSRect drawRect = self.bounds;
NSRect imageRect = NSZeroRect;
PSViewContentMode mode = contentMode;
// Get best size
CGSize size = (image.representations.count ?
[image bestRepresentationForRect:drawRect context:NULL hints:nil].size :
image.size);
// Resizable image filling by default
if (image.isResizable && mode == PSViewContentModeAspectFit)
mode = PSViewContentModeFill;
if (mode == PSViewContentModeAspectFit || mode == PSViewContentModeAspectFill) {
BOOL max = mode == PSViewContentModeAspectFill;
CGFloat factor = (max ? fmaxf : fminf)(drawRect.size.width / size.width,
drawRect.size.height / size.height);
size = (CGSize) { size.width * factor, size.height * factor };
drawRect = (CGRect) {
truncf((drawRect.size.width - size.width) * 0.5f),
truncf((drawRect.size.height - size.height) * 0.5f),
size,
};
} else if (mode == PSViewContentModeCenter) {
imageRect = (CGRect) {
truncf((drawRect.size.width - size.width) * 0.5f),
truncf((drawRect.size.height - size.height) * 0.5f),
size,
};
imageRect = CGRectIntersection(imageRect, drawRect);
} else if (mode == PSViewContentModeFillHorizontal) {
drawRect = (CGRect) {
0.0f, truncf((drawRect.size.height - size.height) * 0.5f),
drawRect.size.width, size.height,
};
} else if (mode == PSViewContentModeFillVertical) {
drawRect = (CGRect) {
truncf((drawRect.size.width - size.width) * 0.5f), 0.0f,
size.width, drawRect.size.height,
};
}
[image
drawInRect:drawRect
fromRect:imageRect
operation:NSCompositeSourceOver
fraction:1.0f
respectFlipped:YES
hints:nil];
}
#pragma mark - Public
- (void)setBackgroundColor:(NSColor *)aBackgroundColor {
backgroundColor = aBackgroundColor;
[self setNeedsDisplay:YES];
}
- (void)setImage:(NSImage *)anImage {
image = anImage;
[self setNeedsDisplay:YES];
}
- (void)setDrawingBlock:(void (^)(NSView *, NSRect))aDrawingBlock {
drawingBlock = [aDrawingBlock copy];
[self setNeedsDisplay:YES];
}
#pragma mark - Target/Observer pattern
- (void)addTarget:(id)target action:(SEL)action forEvents:(PSViewEvent)events {
if (!targets) {
NSMutableArray *(^weakArray)(void) = ^{ // Create non-retaining array
CFArrayCallBacks callbacks = { 0, NULL, NULL, CFCopyDescription, CFEqual };
return CFBridgingRelease(CFArrayCreateMutable(NULL, 0, &callbacks));
};
targets = @{
@(PSViewEventClick) : weakArray(),
@(PSViewEventDoubleClick) : weakArray(),
};
selectors = @{
@(PSViewEventClick) : [NSMutableArray array],
@(PSViewEventDoubleClick) : [NSMutableArray array],
};
}
for (NSNumber *eventNumber in targets) {
if (!(events & eventNumber.intValue))
continue;
NSMutableArray *eventTargets = [targets objectForKey:eventNumber];
NSMutableArray *eventSelectors = [selectors objectForKey:eventNumber];
[eventTargets addObject:target];
[eventSelectors addObject:NSStringFromSelector(action)];
}
}
- (void)sendActionsForEvents:(PSViewEvent)events {
for (NSNumber *eventNumber in targets) {
if (!(events & eventNumber.intValue))
continue;
NSMutableArray *eventTargets = [targets objectForKey:eventNumber];
NSMutableArray *eventSelectors = [selectors objectForKey:eventNumber];
[eventTargets enumerateObjectsUsingBlock:^(id target, NSUInteger idx, BOOL *stop) {
SEL selector = NSSelectorFromString([eventSelectors objectAtIndex:idx]);
objc_msgSend(target, selector);
}];
}
}
@end
# |
Change |
User |
Description |
Committed |
|
#1
|
10691 |
DWishR |
Populate //guest/DWishR/piper/.... |
|
|
//guest/perforce_software/piper/Perforce/Classes/Views/PSView.m |
#1
|
8919 |
Matt Attaway |
Initial add of Piper, a lightweight Perforce client for artists and designers. |
|
|