BsMacOSWindow.mm 21 KB

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