BsMacOSWindow.mm 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2017 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #define BS_COCOA_INTERNALS 1
  4. #include "MacOS/BsMacOSWindow.h"
  5. #include "MacOS/BsMacOSPlatform.h"
  6. #include "MacOS/BsMacOSDropTarget.h"
  7. #include "Math/BsRect2I.h"
  8. #include "Input/BsInputFwd.h"
  9. #include "String/BsUnicode.h"
  10. #include "RenderAPI/BsRenderWindow.h"
  11. static bool keyCodeToInputCommand(uint32_t keyCode, bool shift, bs::InputCommandType& inputCommand)
  12. {
  13. switch(keyCode)
  14. {
  15. case 36: // Return
  16. inputCommand = shift ? bs::InputCommandType::Return : bs::InputCommandType::Confirm;
  17. return true;
  18. case 51: // Backspace
  19. inputCommand = bs::InputCommandType::Backspace;
  20. return true;
  21. case 53: // Escape
  22. inputCommand = bs::InputCommandType::Escape;
  23. return true;
  24. case 117: // Delete
  25. inputCommand = bs::InputCommandType::Delete;
  26. return true;
  27. case 123: // Left
  28. inputCommand = shift ? bs::InputCommandType::SelectLeft : bs::InputCommandType::CursorMoveLeft;
  29. return true;
  30. case 124: // Right
  31. inputCommand = shift ? bs::InputCommandType::SelectRight : bs::InputCommandType::CursorMoveRight;
  32. return true;
  33. case 125: // Down
  34. inputCommand = shift ? bs::InputCommandType::SelectDown : bs::InputCommandType::CursorMoveDown;
  35. return true;
  36. case 126: // Up
  37. inputCommand = shift ? bs::InputCommandType::SelectUp : bs::InputCommandType::CursorMoveUp;
  38. return true;
  39. }
  40. return false;
  41. }
  42. /**
  43. * Overrides window so even borderless windows can become key windows (allowing resize events and cursor changes, among
  44. * others.
  45. */
  46. @interface BSWindow : NSWindow
  47. @end
  48. @implementation BSWindow
  49. - (BOOL)canBecomeKeyWindow
  50. {
  51. return YES;
  52. }
  53. @end
  54. /** Implementation of NSView that handles custom cursors, transparent background images and reports the right mouse click. */
  55. @interface BSView : NSView
  56. @property (atomic, strong) NSArray* resizeAreas;
  57. -(void)rightMouseDown:(NSEvent *) event;
  58. -(void)setBackgroundImage:(NSImage*)image;
  59. @end
  60. @implementation BSView
  61. {
  62. NSTrackingArea* mTrackingArea;
  63. NSImageView* mImageView;
  64. }
  65. -(id)init
  66. {
  67. self = [super init];
  68. mTrackingArea = nil;
  69. mImageView = nil;
  70. return self;
  71. }
  72. -(void)resetCursorRects
  73. {
  74. [super resetCursorRects];
  75. [self addCursorRect:[self bounds] cursor:bs::MacOSPlatform::_getCurrentCursor()];
  76. }
  77. -(void)updateTrackingAreas
  78. {
  79. [super updateTrackingAreas];
  80. if(mTrackingArea)
  81. [self removeTrackingArea:mTrackingArea];
  82. NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp;
  83. mTrackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:[self window] userInfo:nil];
  84. [self addTrackingArea:mTrackingArea];
  85. }
  86. -(void)rightMouseDown:(NSEvent*)event
  87. {
  88. // By default the view eats the right mouse event, but we instead forward the event to window's responder for normal
  89. // handling
  90. if([event.window nextResponder])
  91. [[event.window nextResponder] rightMouseDown:event];
  92. }
  93. -(void)setBackgroundImage:(NSImage*)image
  94. { @autoreleasepool {
  95. if(image)
  96. {
  97. NSRect frame = [self frame];
  98. frame.origin = NSMakePoint(0, 0);
  99. mImageView = [[NSImageView alloc] initWithFrame:frame];
  100. [mImageView setImageScaling:NSImageScaleAxesIndependently];
  101. [mImageView setImage:image];
  102. [mImageView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
  103. self.subviews = @[mImageView];
  104. }
  105. else
  106. self.subviews = @[];
  107. }}
  108. @end
  109. /** Types of mouse events reported by BSWindowListener. */
  110. enum class MouseEventType
  111. {
  112. ButtonUp, ButtonDown
  113. };
  114. /** Listens to mouse and keyboard events for a specific window. Reports them to Platform accordingly. */
  115. @interface BSWindowListener : NSResponder
  116. // Properties
  117. @property (atomic, strong) NSArray* dragAreas;
  118. // Mouse
  119. -(void) handleMouseEvent:(NSEvent *) event type:(MouseEventType) type button:(bs::OSMouseButton) button;
  120. -(void) mouseDown:(NSEvent *) event;
  121. -(void) rightMouseDown:(NSEvent *) event;
  122. -(void) otherMouseDown:(NSEvent *) event;
  123. -(void) mouseUp:(NSEvent *) event;
  124. -(void) rightMouseUp:(NSEvent *) event;
  125. -(void) otherMouseUp:(NSEvent *) event;
  126. -(void) mouseMoved:(NSEvent *) event;
  127. -(void) mouseDragged:(NSEvent *) event;
  128. -(void) rightMouseDragged:(NSEvent *) event;
  129. -(void) otherMouseDragged:(NSEvent *) event;
  130. -(void) scrollWheel:(NSEvent *) event;
  131. // Keyboard
  132. -(void) keyDown:(NSEvent *)event;
  133. // Other
  134. -(BSWindowListener*)initWithOwner:(bs::CocoaWindow*) owner;
  135. @end
  136. @implementation BSWindowListener
  137. {
  138. bs::CocoaWindow* mOwner;
  139. }
  140. @synthesize dragAreas;
  141. - (BSWindowListener* )initWithOwner:(bs::CocoaWindow*)owner
  142. {
  143. self = [super init];
  144. mOwner = owner;
  145. dragAreas = nil;
  146. return self;
  147. }
  148. - (void)handleMouseEvent:(NSEvent *)event type:(MouseEventType) type button:(bs::OSMouseButton) button
  149. {
  150. NSPoint screenPos = NSEvent.mouseLocation;
  151. NSUInteger modifierFlags = NSEvent.modifierFlags;
  152. uint32_t pressedButtons = (uint32_t)NSEvent.pressedMouseButtons;
  153. bs::OSPointerButtonStates buttonStates;
  154. buttonStates.ctrl = (modifierFlags & NSEventModifierFlagControl) != 0;
  155. buttonStates.shift = (modifierFlags & NSEventModifierFlagShift) != 0;
  156. buttonStates.mouseButtons[0] = (pressedButtons & (1 << 0)) != 0;
  157. buttonStates.mouseButtons[1] = (pressedButtons & (1 << 1)) != 0;
  158. buttonStates.mouseButtons[2] = (pressedButtons & (1 << 2)) != 0;
  159. NSWindow* window = [event window];
  160. NSScreen* screen = window ? [window screen] : [NSScreen mainScreen];
  161. bs::flipY(screen, screenPos);
  162. bs::Vector2I pos((int32_t)screenPos.x, (int32_t)screenPos.y);
  163. if(type == MouseEventType::ButtonDown)
  164. bs::MacOSPlatform::sendPointerButtonPressedEvent(pos, button, buttonStates);
  165. else // ButtonUp
  166. {
  167. if([event clickCount] == 2 && button == bs::OSMouseButton::Left)
  168. bs::MacOSPlatform::sendPointerDoubleClickEvent(pos, buttonStates);
  169. else
  170. bs::MacOSPlatform::sendPointerButtonReleasedEvent(pos, button, buttonStates);
  171. }
  172. }
  173. - (void)mouseDown:(NSEvent *)event
  174. {
  175. // Check for manual drag
  176. bool isManualDrag = false;
  177. if(dragAreas)
  178. {
  179. NSPoint point = [event locationInWindow];
  180. NSWindow* window = [event window];
  181. NSScreen* screen = nil;
  182. if(window)
  183. {
  184. NSRect windowFrame = [window frame];
  185. point.x += windowFrame.origin.x;
  186. point.y += windowFrame.origin.y;
  187. screen = [window screen];
  188. }
  189. else
  190. screen = NSScreen.mainScreen;
  191. bs::flipY(screen, point);
  192. for (NSUInteger i = 0; i < [dragAreas count]; i++)
  193. {
  194. bs::Rect2I rect;
  195. [dragAreas[i] getValue:&rect];
  196. if(point.x >= rect.x && point.x < (rect.x + rect.width) &&
  197. (point.y >= rect.y && point.y < (rect.y + rect.height)))
  198. {
  199. [window performWindowDragWithEvent:event];
  200. isManualDrag = true;
  201. break;
  202. }
  203. }
  204. }
  205. if(!isManualDrag)
  206. [self handleMouseEvent:event type:MouseEventType::ButtonDown button:bs::OSMouseButton::Left];
  207. }
  208. - (void)rightMouseDown:(NSEvent *)event
  209. {
  210. [self handleMouseEvent:event type:MouseEventType::ButtonDown button:bs::OSMouseButton::Right];
  211. }
  212. - (void)otherMouseDown:(NSEvent *)event
  213. {
  214. [self handleMouseEvent:event type:MouseEventType::ButtonDown button:bs::OSMouseButton::Middle];
  215. }
  216. - (void)mouseUp:(NSEvent *)event
  217. {
  218. [self handleMouseEvent:event type:MouseEventType::ButtonUp button:bs::OSMouseButton::Left];
  219. }
  220. - (void)rightMouseUp:(NSEvent *)event
  221. {
  222. [self handleMouseEvent:event type:MouseEventType::ButtonUp button:bs::OSMouseButton::Right];
  223. }
  224. - (void)otherMouseUp:(NSEvent *)event
  225. {
  226. [self handleMouseEvent:event type:MouseEventType::ButtonUp button:bs::OSMouseButton::Middle];
  227. }
  228. - (void)mouseMoved:(NSEvent *)event
  229. {
  230. uint32_t pressedButtons = (uint32_t)NSEvent.pressedMouseButtons;
  231. NSPoint point = [event locationInWindow];
  232. NSWindow* window = [event window];
  233. NSScreen* screen = nil;
  234. if(window)
  235. {
  236. NSRect windowFrame = [window frame];
  237. point.x += windowFrame.origin.x;
  238. point.y += windowFrame.origin.y;
  239. screen = [window screen];
  240. }
  241. else
  242. screen = NSScreen.mainScreen;
  243. bs::flipY(screen, point);
  244. bs::Vector2I pos;
  245. pos.x = (int32_t)point.x;
  246. pos.y = (int32_t)point.y;
  247. if(bs::MacOSPlatform::_clipCursor(pos))
  248. bs::MacOSPlatform::_setCursorPosition(pos);
  249. NSUInteger modifierFlags = NSEvent.modifierFlags;
  250. bs::OSPointerButtonStates buttonStates;
  251. buttonStates.ctrl = (modifierFlags & NSEventModifierFlagControl) != 0;
  252. buttonStates.shift = (modifierFlags & NSEventModifierFlagShift) != 0;
  253. buttonStates.mouseButtons[0] = (pressedButtons & (1 << 0)) != 0;
  254. buttonStates.mouseButtons[1] = (pressedButtons & (1 << 1)) != 0;
  255. buttonStates.mouseButtons[2] = (pressedButtons & (1 << 2)) != 0;
  256. bs::MacOSPlatform::sendPointerMovedEvent(pos, buttonStates);
  257. }
  258. - (void)mouseDragged:(NSEvent *)event
  259. {
  260. [self mouseMoved:event];
  261. }
  262. - (void)rightMouseDragged:(NSEvent *)event
  263. {
  264. [self mouseMoved:event];
  265. }
  266. - (void)otherMouseDragged:(NSEvent *)event
  267. {
  268. [self mouseMoved:event];
  269. }
  270. - (void)scrollWheel:(NSEvent *)event
  271. {
  272. float y = (float)[event deltaY];
  273. bs::MacOSPlatform::sendMouseWheelScrollEvent((float)y);
  274. }
  275. - (void)keyDown:(NSEvent *)event
  276. {
  277. NSString* string = event.characters;
  278. uint32_t keyCode = event.keyCode;
  279. NSUInteger modifierFlags = NSEvent.modifierFlags;
  280. bool shift = (modifierFlags & NSEventModifierFlagShift) != 0;
  281. bs::InputCommandType ict;
  282. if(keyCodeToInputCommand(keyCode, shift, ict))
  283. bs::MacOSPlatform::sendInputCommandEvent(ict);
  284. else
  285. {
  286. bs::String utf8String = [string UTF8String];
  287. bs::U32String utf32String = bs::UTF8::toUTF32(utf8String);
  288. for(size_t i = 0; utf32String.length(); i++)
  289. bs::MacOSPlatform::sendCharInputEvent(utf32String[i]);
  290. }
  291. }
  292. - (void)mouseEntered:(NSEvent*)event
  293. {
  294. // Do nothing
  295. }
  296. - (void)mouseExited:(NSEvent*)event
  297. {
  298. bs::ct::RenderWindow* renderWindow = (bs::ct::RenderWindow*)mOwner->_getUserData();
  299. if(renderWindow != nullptr)
  300. renderWindow->_notifyMouseLeft();
  301. }
  302. @end
  303. /** Converts a point from coordinates relative to window's frame, into coordinates relative to window's content rectangle. */
  304. NSPoint frameToContentRect(NSWindow* window, NSPoint framePoint)
  305. {
  306. bs::flipYWindow(window, framePoint);
  307. NSRect frameRect = [window frame];
  308. NSRect contentRect = [window contentRectForFrameRect:frameRect];
  309. NSPoint offset;
  310. offset.x = frameRect.origin.x - contentRect.origin.x;
  311. offset.y = (frameRect.origin.y + frameRect.size.height) - (contentRect.origin.y + contentRect.size.height);
  312. framePoint.x -= offset.x;
  313. framePoint.y -= offset.y;
  314. return framePoint;
  315. }
  316. /** Listens to window move, resize, focus change and close events, handles drag and drop operations. */
  317. @interface BSWindowDelegate : NSObject<NSWindowDelegate, NSDraggingDestination>
  318. -(id)initWithWindow:(bs::CocoaWindow*)window;
  319. @end
  320. @implementation BSWindowDelegate
  321. {
  322. bs::CocoaWindow* mWindow;
  323. NSRect mStandardZoomFrame;
  324. bool mIsZooming;
  325. }
  326. - (id)initWithWindow:(bs::CocoaWindow*)window
  327. {
  328. self = [super init];
  329. mWindow = window;
  330. mIsZooming = false;
  331. return self;
  332. }
  333. - (void)windowWillClose:(NSNotification *)notification
  334. {
  335. // If it's a render window we allow the client code to handle the message
  336. bs::ct::RenderWindow* renderWindow = (bs::ct::RenderWindow*)mWindow->_getUserData();
  337. if(renderWindow != nullptr)
  338. renderWindow->_notifyCloseRequested();
  339. else // If not, we just destroy the window
  340. mWindow->_destroy();
  341. }
  342. - (void)windowDidBecomeKey:(NSNotification*)notification
  343. {
  344. bs::ct::RenderWindow* renderWindow = (bs::ct::RenderWindow*)mWindow->_getUserData();
  345. if(renderWindow != nullptr)
  346. renderWindow->_windowFocusReceived();
  347. }
  348. - (void)windowDidResignKey:(NSNotification*)notification
  349. {
  350. bs::ct::RenderWindow* renderWindow = (bs::ct::RenderWindow*)mWindow->_getUserData();
  351. if(renderWindow != nullptr)
  352. renderWindow->_windowFocusLost();
  353. }
  354. - (void)windowDidResize:(NSNotification*)notification
  355. {
  356. if([[notification object] isKindOfClass:[NSWindow class]])
  357. bs::MacOSPlatform::_updateClipBounds([notification object]);
  358. bs::ct::RenderWindow* renderWindow = (bs::ct::RenderWindow*)mWindow->_getUserData();
  359. if(renderWindow != nullptr)
  360. renderWindow->_windowMovedOrResized();
  361. }
  362. - (void)windowDidMove:(NSNotification*)notification
  363. {
  364. bs::ct::RenderWindow* renderWindow = (bs::ct::RenderWindow*)mWindow->_getUserData();
  365. if(renderWindow != nullptr)
  366. renderWindow->_windowMovedOrResized();
  367. }
  368. - (void)windowDidMiniaturize:(NSNotification*)notification
  369. {
  370. bs::ct::RenderWindow* renderWindow = (bs::ct::RenderWindow*)mWindow->_getUserData();
  371. if(renderWindow != nullptr)
  372. renderWindow->_notifyMinimized();
  373. }
  374. - (void)windowDidDeminiaturize:(NSNotification*)notification
  375. {
  376. bs::ct::RenderWindow* renderWindow = (bs::ct::RenderWindow*)mWindow->_getUserData();
  377. if(renderWindow != nullptr)
  378. renderWindow->_notifyRestored();
  379. }
  380. - (BOOL)windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
  381. {
  382. // Maximizing, or restoring
  383. if(mIsZooming)
  384. {
  385. bs::ct::RenderWindow* renderWindow = (bs::ct::RenderWindow*)mWindow->_getUserData();
  386. if(renderWindow != nullptr)
  387. {
  388. if (newFrame.size.width == mStandardZoomFrame.size.width &&
  389. newFrame.size.height == mStandardZoomFrame.size.height)
  390. renderWindow->_notifyMaximized();
  391. else
  392. renderWindow->_notifyRestored();
  393. }
  394. mIsZooming = true;
  395. }
  396. return YES;
  397. }
  398. - (NSRect)windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)newFrame
  399. {
  400. mIsZooming = true;
  401. mStandardZoomFrame = newFrame;
  402. return newFrame;
  403. }
  404. - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
  405. {
  406. bs::CocoaWindow::Pimpl* privateData = mWindow->_getPrivateData();
  407. NSPoint point = [sender draggingLocation];
  408. point = frameToContentRect(privateData->window, point);
  409. bs::Vector2I position((int32_t)point.x, (int32_t)point.y);
  410. if(bs::CocoaDragAndDrop::_notifyDragEntered(mWindow, position))
  411. return NSDragOperationLink;
  412. return NSDragOperationNone;
  413. }
  414. - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
  415. {
  416. bs::CocoaWindow::Pimpl* privateData = mWindow->_getPrivateData();
  417. NSPoint point = [sender draggingLocation];
  418. point = frameToContentRect(privateData->window, point);
  419. bs::Vector2I position((int32_t)point.x, (int32_t)point.y);
  420. if(bs::CocoaDragAndDrop::_notifyDragMoved(mWindow, position))
  421. return NSDragOperationLink;
  422. return NSDragOperationNone;
  423. }
  424. - (void)draggingExited:(nullable id <NSDraggingInfo>)sender
  425. {
  426. bs::CocoaDragAndDrop::_notifyDragLeft(mWindow);
  427. }
  428. - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
  429. {
  430. NSPasteboard* pasteboard = [sender draggingPasteboard];
  431. if([[pasteboard types] containsObject:NSFilenamesPboardType])
  432. {
  433. NSArray* entries = [pasteboard propertyListForType:NSFilenamesPboardType];
  434. bs::Vector<bs::Path> paths;
  435. for(NSString* path in entries)
  436. {
  437. const char* pathChars = [path UTF8String];
  438. paths.push_back(bs::Path(pathChars));
  439. }
  440. bs::CocoaWindow::Pimpl* privateData = mWindow->_getPrivateData();
  441. NSPoint point = [sender draggingLocation];
  442. point = frameToContentRect(privateData->window, point);
  443. bs::Vector2I position((int32_t)point.x, (int32_t)point.y);
  444. if(bs::CocoaDragAndDrop::_notifyDragDropped(mWindow, position, paths))
  445. return YES;
  446. }
  447. return NO;
  448. }
  449. @end
  450. namespace bs
  451. {
  452. CocoaWindow::CocoaWindow(const WINDOW_DESC& desc)
  453. { @autoreleasepool {
  454. m = bs_new<Pimpl>();
  455. NSArray* screens = [NSScreen screens];
  456. NSScreen* screen = nil;
  457. INT32 x = 0;
  458. INT32 y = 0;
  459. for(NSScreen* entry in screens)
  460. {
  461. NSRect screenRect = [entry frame];
  462. if(((desc.x >= screenRect.origin.x && desc.y < (screenRect.origin.x + screenRect.size.width)) || desc.x == -1) &&
  463. ((desc.y >= screenRect.origin.y && desc.y < (screenRect.origin.y + screenRect.size.height)) || desc.y == -1))
  464. {
  465. if(desc.x == -1)
  466. x = (INT32)screenRect.origin.x + std::max(0, (INT32)screenRect.size.width - (INT32)desc.width) / 2;
  467. else
  468. x = desc.x - (INT32)screenRect.origin.x;
  469. if(desc.y == -1)
  470. y = (INT32)screenRect.origin.y + std::max(0, (INT32)screenRect.size.height - (INT32)desc.height) / 2;
  471. else
  472. y = desc.y - (INT32)screenRect.origin.y;
  473. screen = entry;
  474. break;
  475. }
  476. }
  477. if(!desc.showDecorations)
  478. m->style |= NSWindowStyleMaskBorderless;
  479. else
  480. m->style |= NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
  481. if(desc.allowResize)
  482. m->style |= NSWindowStyleMaskResizable;
  483. m->window = [BSWindow alloc];
  484. m->window = [m->window
  485. initWithContentRect:NSMakeRect(x, y, desc.width, desc.height)
  486. styleMask:(NSWindowStyleMask)m->style
  487. backing:NSBackingStoreBuffered
  488. defer:NO
  489. screen:screen];
  490. if(desc.allowResize)
  491. {
  492. bool allowSpaces = NSAppKitVersionNumber > NSAppKitVersionNumber10_6;
  493. if(allowSpaces)
  494. [m->window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
  495. }
  496. NSString* titleString = [NSString stringWithUTF8String:desc.title.c_str()];
  497. [m->window setAcceptsMouseMovedEvents:YES];
  498. [m->window setReleasedWhenClosed:NO];
  499. [m->window setTitle:titleString];
  500. [m->window makeKeyAndOrderFront:nil];
  501. m->responder = [[BSWindowListener alloc] initWithOwner:this];
  502. [m->window setNextResponder:m->responder];
  503. m->delegate = [[BSWindowDelegate alloc] initWithWindow:this];
  504. [m->window setDelegate:m->delegate];
  505. m->view = [[BSView alloc] init];
  506. [m->window setContentView:m->view];
  507. if(desc.background)
  508. {
  509. [m->window setAlphaValue:1.0f];
  510. [m->window setOpaque:NO];
  511. [m->window setBackgroundColor:[NSColor clearColor]];
  512. NSImage* image = MacOSPlatform::createNSImage(*desc.background);
  513. [m->view setBackgroundImage:image];
  514. }
  515. m->isModal = desc.modal;
  516. m->windowNumber = [m->window windowNumber];
  517. MacOSPlatform::registerWindow(this);
  518. }}
  519. CocoaWindow::~CocoaWindow()
  520. {
  521. if(m->window != nil)
  522. _destroy();
  523. bs_delete(m);
  524. }
  525. void CocoaWindow::move(INT32 x, INT32 y)
  526. { @autoreleasepool {
  527. NSPoint point;
  528. point.x = x;
  529. point.y = y;
  530. [m->window setFrameTopLeftPoint:point];
  531. }}
  532. void CocoaWindow::resize(UINT32 width, UINT32 height)
  533. { @autoreleasepool {
  534. NSRect frameRect = m->window.frame;
  535. NSRect contentRect = [m->window contentRectForFrameRect:frameRect];
  536. contentRect.size.width = width;
  537. contentRect.size.height = height;
  538. [m->window setFrame:[m->window frameRectForContentRect:contentRect] display:YES];
  539. }}
  540. void CocoaWindow::_destroy()
  541. {
  542. MacOSPlatform::unregisterWindow(this);
  543. m->window = nil;
  544. }
  545. Rect2I CocoaWindow::getArea() const
  546. { @autoreleasepool {
  547. NSRect frameRect = [m->window frame];
  548. NSRect contentRect = [m->window contentRectForFrameRect:frameRect];
  549. flipY([m->window screen], contentRect);
  550. return Rect2I(
  551. (INT32)contentRect.origin.x,
  552. (INT32)contentRect.origin.y,
  553. (UINT32)contentRect.size.width,
  554. (UINT32)contentRect.size.height
  555. );
  556. }}
  557. void CocoaWindow::hide()
  558. { @autoreleasepool {
  559. [m->window orderOut:nil];
  560. }}
  561. void CocoaWindow::show()
  562. { @autoreleasepool {
  563. [m->window makeKeyAndOrderFront:nil];
  564. }}
  565. void CocoaWindow::maximize()
  566. { @autoreleasepool {
  567. if(![m->window isZoomed])
  568. [m->window zoom:nil];
  569. }}
  570. void CocoaWindow::minimize()
  571. { @autoreleasepool {
  572. [m->window miniaturize:nil];
  573. }}
  574. void CocoaWindow::restore()
  575. { @autoreleasepool {
  576. if([m->window isMiniaturized])
  577. [m->window deminiaturize:nil];
  578. else if([m->window isZoomed])
  579. [m->window zoom:nil];
  580. }}
  581. Vector2I CocoaWindow::windowToScreenPos(const Vector2I& windowPos) const
  582. { @autoreleasepool {
  583. NSRect frameRect = [m->window frame];
  584. NSRect contentRect = [m->window contentRectForFrameRect:frameRect];
  585. flipY([m->window screen], contentRect);
  586. Vector2I screenPos;
  587. screenPos.x = windowPos.x + (INT32)contentRect.origin.x;
  588. screenPos.y = windowPos.y + (INT32)contentRect.origin.y;
  589. return screenPos;
  590. }}
  591. Vector2I CocoaWindow::screenToWindowPos(const Vector2I& screenPos) const
  592. { @autoreleasepool {
  593. NSRect frameRect = [m->window frame];
  594. NSRect contentRect = [m->window contentRectForFrameRect:frameRect];
  595. flipY([m->window screen], contentRect);
  596. Vector2I windowPos;
  597. windowPos.x = screenPos.x - (INT32) contentRect.origin.x;
  598. windowPos.y = screenPos.y - (INT32) contentRect.origin.y;
  599. return windowPos;
  600. }}
  601. void CocoaWindow::setWindowed()
  602. {
  603. if(m->isFullscreen)
  604. {
  605. [m->window setStyleMask:(NSWindowStyleMask)m->style];
  606. [m->window setFrame:m->windowedRect display:NO];
  607. [m->window setLevel:NSNormalWindowLevel];
  608. m->isFullscreen = false;
  609. }
  610. }
  611. void CocoaWindow::setFullscreen()
  612. {
  613. if(!m->isFullscreen)
  614. m->windowedRect = [m->window frame];
  615. NSRect frame = [[m->window screen] frame];
  616. [m->window setStyleMask:NSWindowStyleMaskBorderless];
  617. [m->window setFrame:frame display:NO];
  618. [m->window setLevel:NSMainMenuWindowLevel+1];
  619. [m->window makeKeyAndOrderFront:nil];
  620. m->isFullscreen = true;
  621. }
  622. void CocoaWindow::_setDragZones(const Vector<Rect2I>& rects)
  623. { @autoreleasepool {
  624. NSMutableArray* array = [[NSMutableArray alloc] init];
  625. for(auto& entry : rects)
  626. [array addObject:[NSValue valueWithBytes:&entry objCType:@encode(Rect2I)]];
  627. [m->responder setDragAreas:array];
  628. }}
  629. void CocoaWindow::_setUserData(void* data)
  630. {
  631. m->userData = data;
  632. }
  633. void* CocoaWindow::_getUserData() const
  634. {
  635. return m->userData;
  636. }
  637. void CocoaWindow::_registerForDragAndDrop()
  638. {
  639. if(m->numDropTargets == 0)
  640. [m->window registerForDraggedTypes:@[NSFilenamesPboardType]];
  641. }
  642. void CocoaWindow::_unregisterForDragAndDrop()
  643. {
  644. if(m->numDropTargets == 0)
  645. return;
  646. m->numDropTargets--;
  647. if(m->numDropTargets == 0)
  648. [m->window unregisterDraggedTypes];
  649. }
  650. }