BsMacOSWindow.mm 22 KB

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