bsnes/hiro/cocoa/widget/canvas.cpp

225 lines
5.7 KiB
C++
Executable File

#if defined(Hiro_Canvas)
@implementation CocoaCanvas : NSImageView
-(id) initWith:(hiro::mCanvas&)canvasReference {
if(self = [super initWithFrame:NSMakeRect(0, 0, 0, 0)]) {
canvas = &canvasReference;
[self setEditable:NO]; //disable image drag-and-drop functionality
NSTrackingArea* area = [[[NSTrackingArea alloc] initWithRect:[self frame]
options:NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect
owner:self userInfo:nil
] autorelease];
[self addTrackingArea:area];
}
return self;
}
-(void) resetCursorRects {
if(auto mouseCursor = NSMakeCursor(canvas->mouseCursor())) {
[self addCursorRect:self.bounds cursor:mouseCursor];
}
}
-(NSDragOperation) draggingEntered:(id<NSDraggingInfo>)sender {
return DropPathsOperation(sender);
}
-(BOOL) performDragOperation:(id<NSDraggingInfo>)sender {
auto paths = DropPaths(sender);
if(!paths) return NO;
canvas->doDrop(paths);
return YES;
}
-(void) mouseButton:(NSEvent*)event down:(BOOL)isDown {
if(isDown) {
switch([event buttonNumber]) {
case 0: return canvas->doMousePress(hiro::Mouse::Button::Left);
case 1: return canvas->doMousePress(hiro::Mouse::Button::Right);
case 2: return canvas->doMousePress(hiro::Mouse::Button::Middle);
}
} else {
switch([event buttonNumber]) {
case 0: return canvas->doMouseRelease(hiro::Mouse::Button::Left);
case 1: return canvas->doMouseRelease(hiro::Mouse::Button::Right);
case 2: return canvas->doMouseRelease(hiro::Mouse::Button::Middle);
}
}
}
-(void) mouseEntered:(NSEvent*)event {
canvas->doMouseEnter();
}
-(void) mouseExited:(NSEvent*)event {
canvas->doMouseLeave();
}
-(void) mouseMove:(NSEvent*)event {
if([event window] == nil) return;
NSPoint location = [self convertPoint:[event locationInWindow] fromView:nil];
canvas->doMouseMove({(int)location.x, (int)([self frame].size.height - 1 - location.y)});
}
-(void) mouseDown:(NSEvent*)event {
[self mouseButton:event down:YES];
}
-(void) mouseUp:(NSEvent*)event {
[self mouseButton:event down:NO];
}
-(void) mouseDragged:(NSEvent*)event {
[self mouseMove:event];
}
-(void) rightMouseDown:(NSEvent*)event {
[self mouseButton:event down:YES];
}
-(void) rightMouseUp:(NSEvent*)event {
[self mouseButton:event down:NO];
}
-(void) rightMouseDragged:(NSEvent*)event {
[self mouseMove:event];
}
-(void) otherMouseDown:(NSEvent*)event {
[self mouseButton:event down:YES];
}
-(void) otherMouseUp:(NSEvent*)event {
[self mouseButton:event down:NO];
}
-(void) otherMouseDragged:(NSEvent*)event {
[self mouseMove:event];
}
@end
namespace hiro {
auto pCanvas::construct() -> void {
@autoreleasepool {
cocoaView = cocoaCanvas = [[CocoaCanvas alloc] initWith:self()];
pWidget::construct();
}
}
auto pCanvas::destruct() -> void {
@autoreleasepool {
[cocoaView removeFromSuperview];
[cocoaView release];
}
}
auto pCanvas::minimumSize() const -> Size {
if(auto& icon = state().icon) return {(int)icon.width(), (int)icon.height()};
return {0, 0};
}
auto pCanvas::setAlignment(Alignment) -> void {
update();
}
auto pCanvas::setColor(Color color) -> void {
update();
}
auto pCanvas::setDroppable(bool droppable) -> void {
@autoreleasepool {
if(droppable) {
[cocoaCanvas registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
} else {
[cocoaCanvas unregisterDraggedTypes];
}
}
}
auto pCanvas::setFocusable(bool focusable) -> void {
//TODO
}
auto pCanvas::setGeometry(Geometry geometry) -> void {
pWidget::setGeometry(geometry);
update();
}
auto pCanvas::setGradient(Gradient gradient) -> void {
update();
}
auto pCanvas::setIcon(const image& icon) -> void {
update();
}
auto pCanvas::update() -> void {
_rasterize();
@autoreleasepool {
[cocoaView setNeedsDisplay:YES];
}
}
//todo: support cases where the icon size does not match the canvas size (alignment)
auto pCanvas::_rasterize() -> void {
@autoreleasepool {
int width = 0;
int height = 0;
if(auto& icon = state().icon) {
width = icon.width();
height = icon.height();
} else {
width = pSizable::state().geometry.width();
height = pSizable::state().geometry.height();
}
if(width <= 0 || height <= 0) return;
if(width != surfaceWidth || height != surfaceHeight) {
[cocoaView setImage:nil];
surface = nullptr;
bitmap = nullptr;
}
surfaceWidth = width;
surfaceHeight = height;
if(!surface) {
surface = [[[NSImage alloc] initWithSize:NSMakeSize(width, height)] autorelease];
bitmap = [[[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:nil
pixelsWide:width pixelsHigh:height
bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES
isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace
bitmapFormat:NSAlphaNonpremultipliedBitmapFormat
bytesPerRow:(width * 4) bitsPerPixel:32
] autorelease];
[surface addRepresentation:bitmap];
[cocoaView setImage:surface];
}
auto target = (uint32_t*)[bitmap bitmapData];
if(auto icon = state().icon) {
icon.transform(0, 32, 255u << 24, 255u << 0, 255u << 8, 255u << 16); //Cocoa uses ABGR format
memory::copy(target, icon.data(), icon.size());
} else if(auto& gradient = state().gradient) {
auto& colors = gradient.state.colors;
image fill;
fill.allocate(width, height);
fill.gradient(colors[0].value(), colors[1].value(), colors[2].value(), colors[3].value());
memory::copy(target, fill.data(), fill.size());
} else {
uint32_t color = state().color.value();
for(auto n : range(width * height)) target[n] = color;
}
}
}
}
#endif