canvas.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. #if defined(Hiro_Canvas)
  2. @implementation CocoaCanvas : NSImageView
  3. -(id) initWith:(hiro::mCanvas&)canvasReference {
  4. if(self = [super initWithFrame:NSMakeRect(0, 0, 0, 0)]) {
  5. canvas = &canvasReference;
  6. [self setEditable:NO]; //disable image drag-and-drop functionality
  7. NSTrackingArea* area = [[[NSTrackingArea alloc] initWithRect:[self frame]
  8. options:NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect
  9. owner:self userInfo:nil
  10. ] autorelease];
  11. [self addTrackingArea:area];
  12. }
  13. return self;
  14. }
  15. -(void) resetCursorRects {
  16. if(auto mouseCursor = NSMakeCursor(canvas->mouseCursor())) {
  17. [self addCursorRect:self.bounds cursor:mouseCursor];
  18. }
  19. }
  20. -(NSDragOperation) draggingEntered:(id<NSDraggingInfo>)sender {
  21. return DropPathsOperation(sender);
  22. }
  23. -(BOOL) performDragOperation:(id<NSDraggingInfo>)sender {
  24. auto paths = DropPaths(sender);
  25. if(!paths) return NO;
  26. canvas->doDrop(paths);
  27. return YES;
  28. }
  29. -(void) mouseButton:(NSEvent*)event down:(BOOL)isDown {
  30. if(isDown) {
  31. switch([event buttonNumber]) {
  32. case 0: return canvas->doMousePress(hiro::Mouse::Button::Left);
  33. case 1: return canvas->doMousePress(hiro::Mouse::Button::Right);
  34. case 2: return canvas->doMousePress(hiro::Mouse::Button::Middle);
  35. }
  36. } else {
  37. switch([event buttonNumber]) {
  38. case 0: return canvas->doMouseRelease(hiro::Mouse::Button::Left);
  39. case 1: return canvas->doMouseRelease(hiro::Mouse::Button::Right);
  40. case 2: return canvas->doMouseRelease(hiro::Mouse::Button::Middle);
  41. }
  42. }
  43. }
  44. -(void) mouseEntered:(NSEvent*)event {
  45. canvas->doMouseEnter();
  46. }
  47. -(void) mouseExited:(NSEvent*)event {
  48. canvas->doMouseLeave();
  49. }
  50. -(void) mouseMove:(NSEvent*)event {
  51. if([event window] == nil) return;
  52. NSPoint location = [self convertPoint:[event locationInWindow] fromView:nil];
  53. canvas->doMouseMove({(int)location.x, (int)([self frame].size.height - 1 - location.y)});
  54. }
  55. -(void) mouseDown:(NSEvent*)event {
  56. [self mouseButton:event down:YES];
  57. }
  58. -(void) mouseUp:(NSEvent*)event {
  59. [self mouseButton:event down:NO];
  60. }
  61. -(void) mouseDragged:(NSEvent*)event {
  62. [self mouseMove:event];
  63. }
  64. -(void) rightMouseDown:(NSEvent*)event {
  65. [self mouseButton:event down:YES];
  66. }
  67. -(void) rightMouseUp:(NSEvent*)event {
  68. [self mouseButton:event down:NO];
  69. }
  70. -(void) rightMouseDragged:(NSEvent*)event {
  71. [self mouseMove:event];
  72. }
  73. -(void) otherMouseDown:(NSEvent*)event {
  74. [self mouseButton:event down:YES];
  75. }
  76. -(void) otherMouseUp:(NSEvent*)event {
  77. [self mouseButton:event down:NO];
  78. }
  79. -(void) otherMouseDragged:(NSEvent*)event {
  80. [self mouseMove:event];
  81. }
  82. @end
  83. namespace hiro {
  84. auto pCanvas::construct() -> void {
  85. @autoreleasepool {
  86. cocoaView = cocoaCanvas = [[CocoaCanvas alloc] initWith:self()];
  87. pWidget::construct();
  88. }
  89. }
  90. auto pCanvas::destruct() -> void {
  91. @autoreleasepool {
  92. [cocoaView removeFromSuperview];
  93. [cocoaView release];
  94. }
  95. }
  96. auto pCanvas::minimumSize() const -> Size {
  97. if(auto& icon = state().icon) return {(int)icon.width(), (int)icon.height()};
  98. return {0, 0};
  99. }
  100. auto pCanvas::setAlignment(Alignment) -> void {
  101. update();
  102. }
  103. auto pCanvas::setColor(Color color) -> void {
  104. update();
  105. }
  106. auto pCanvas::setDroppable(bool droppable) -> void {
  107. @autoreleasepool {
  108. if(droppable) {
  109. [cocoaCanvas registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
  110. } else {
  111. [cocoaCanvas unregisterDraggedTypes];
  112. }
  113. }
  114. }
  115. auto pCanvas::setFocusable(bool focusable) -> void {
  116. //TODO
  117. }
  118. auto pCanvas::setGeometry(Geometry geometry) -> void {
  119. pWidget::setGeometry(geometry);
  120. update();
  121. }
  122. auto pCanvas::setGradient(Gradient gradient) -> void {
  123. update();
  124. }
  125. auto pCanvas::setIcon(const image& icon) -> void {
  126. update();
  127. }
  128. auto pCanvas::update() -> void {
  129. _rasterize();
  130. @autoreleasepool {
  131. [cocoaView setNeedsDisplay:YES];
  132. }
  133. }
  134. //todo: support cases where the icon size does not match the canvas size (alignment)
  135. auto pCanvas::_rasterize() -> void {
  136. @autoreleasepool {
  137. int width = 0;
  138. int height = 0;
  139. if(auto& icon = state().icon) {
  140. width = icon.width();
  141. height = icon.height();
  142. } else {
  143. width = pSizable::state().geometry.width();
  144. height = pSizable::state().geometry.height();
  145. }
  146. if(width <= 0 || height <= 0) return;
  147. if(width != surfaceWidth || height != surfaceHeight) {
  148. [cocoaView setImage:nil];
  149. surface = nullptr;
  150. bitmap = nullptr;
  151. }
  152. surfaceWidth = width;
  153. surfaceHeight = height;
  154. if(!surface) {
  155. surface = [[[NSImage alloc] initWithSize:NSMakeSize(width, height)] autorelease];
  156. bitmap = [[[NSBitmapImageRep alloc]
  157. initWithBitmapDataPlanes:nil
  158. pixelsWide:width pixelsHigh:height
  159. bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES
  160. isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace
  161. bitmapFormat:NSAlphaNonpremultipliedBitmapFormat
  162. bytesPerRow:(width * 4) bitsPerPixel:32
  163. ] autorelease];
  164. [surface addRepresentation:bitmap];
  165. [cocoaView setImage:surface];
  166. }
  167. auto target = (uint32_t*)[bitmap bitmapData];
  168. if(auto icon = state().icon) {
  169. icon.transform(0, 32, 255u << 24, 255u << 0, 255u << 8, 255u << 16); //Cocoa uses ABGR format
  170. memory::copy(target, icon.data(), icon.size());
  171. } else if(auto& gradient = state().gradient) {
  172. auto& colors = gradient.state.colors;
  173. image fill;
  174. fill.allocate(width, height);
  175. fill.gradient(colors[0].value(), colors[1].value(), colors[2].value(), colors[3].value());
  176. memory::copy(target, fill.data(), fill.size());
  177. } else {
  178. uint32_t color = state().color.value();
  179. for(auto n : range(width * height)) target[n] = color;
  180. }
  181. }
  182. }
  183. }
  184. #endif