BsMacOSWindow.mm 21 KB

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