Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2312 lines
87KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For this technical preview, this file is not subject to commercial licensing.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. //==============================================================================
  14. #if defined (MAC_OS_X_VERSION_10_8) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8) \
  15. && USE_COREGRAPHICS_RENDERING && JUCE_COREGRAPHICS_DRAW_ASYNC
  16. static const juce::Identifier disableAsyncLayerBackedViewIdentifier { "disableAsyncLayerBackedView" };
  17. void setComponentAsyncLayerBackedViewDisabled (juce::Component& comp, bool shouldDisableAsyncLayerBackedView)
  18. {
  19. comp.getProperties().set (disableAsyncLayerBackedViewIdentifier, shouldDisableAsyncLayerBackedView);
  20. }
  21. bool getComponentAsyncLayerBackedViewDisabled (juce::Component& comp)
  22. {
  23. return comp.getProperties()[disableAsyncLayerBackedViewIdentifier];
  24. }
  25. #endif
  26. //==============================================================================
  27. namespace juce
  28. {
  29. typedef void (*AppFocusChangeCallback)();
  30. extern AppFocusChangeCallback appFocusChangeCallback;
  31. typedef bool (*CheckEventBlockedByModalComps) (NSEvent*);
  32. extern CheckEventBlockedByModalComps isEventBlockedByModalComps;
  33. }
  34. namespace juce
  35. {
  36. //==============================================================================
  37. static CGFloat getMainScreenHeight() noexcept
  38. {
  39. if ([[NSScreen screens] count] == 0)
  40. return 0.0f;
  41. return [[[NSScreen screens] objectAtIndex: 0] frame].size.height;
  42. }
  43. static void flipScreenRect (NSRect& r) noexcept
  44. {
  45. r.origin.y = getMainScreenHeight() - (r.origin.y + r.size.height);
  46. }
  47. static NSRect flippedScreenRect (NSRect r) noexcept
  48. {
  49. flipScreenRect (r);
  50. return r;
  51. }
  52. //==============================================================================
  53. class NSViewComponentPeer : public ComponentPeer,
  54. private Timer
  55. {
  56. public:
  57. NSViewComponentPeer (Component& comp, const int windowStyleFlags, NSView* viewToAttachTo)
  58. : ComponentPeer (comp, windowStyleFlags),
  59. safeComponent (&comp),
  60. isSharedWindow (viewToAttachTo != nil),
  61. lastRepaintTime (Time::getMillisecondCounter())
  62. {
  63. appFocusChangeCallback = appFocusChanged;
  64. isEventBlockedByModalComps = checkEventBlockedByModalComps;
  65. auto r = makeNSRect (component.getLocalBounds());
  66. view = [createViewInstance() initWithFrame: r];
  67. setOwner (view, this);
  68. [view registerForDraggedTypes: getSupportedDragTypes()];
  69. notificationCenter = [NSNotificationCenter defaultCenter];
  70. [notificationCenter addObserver: view
  71. selector: @selector (frameChanged:)
  72. name: NSViewFrameDidChangeNotification
  73. object: view];
  74. [view setPostsFrameChangedNotifications: YES];
  75. #if defined (MAC_OS_X_VERSION_10_8) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8) \
  76. && USE_COREGRAPHICS_RENDERING && JUCE_COREGRAPHICS_DRAW_ASYNC
  77. if (! getComponentAsyncLayerBackedViewDisabled (component))
  78. {
  79. [view setWantsLayer: YES];
  80. [[view layer] setDrawsAsynchronously: YES];
  81. }
  82. #endif
  83. if (isSharedWindow)
  84. {
  85. window = [viewToAttachTo window];
  86. [viewToAttachTo addSubview: view];
  87. }
  88. else
  89. {
  90. r.origin.x = (CGFloat) component.getX();
  91. r.origin.y = (CGFloat) component.getY();
  92. flipScreenRect (r);
  93. window = [createWindowInstance() initWithContentRect: r
  94. styleMask: getNSWindowStyleMask (windowStyleFlags)
  95. backing: NSBackingStoreBuffered
  96. defer: YES];
  97. setOwner (window, this);
  98. [window orderOut: nil];
  99. [window setDelegate: (id<NSWindowDelegate>) window];
  100. [window setOpaque: component.isOpaque()];
  101. if (! [window isOpaque])
  102. [window setBackgroundColor: [NSColor clearColor]];
  103. #if defined (MAC_OS_X_VERSION_10_9) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9)
  104. [view setAppearance: [NSAppearance appearanceNamed: NSAppearanceNameAqua]];
  105. #endif
  106. [window setHasShadow: ((windowStyleFlags & windowHasDropShadow) != 0)];
  107. if (component.isAlwaysOnTop())
  108. setAlwaysOnTop (true);
  109. [window setContentView: view];
  110. [window setAcceptsMouseMovedEvents: YES];
  111. // We'll both retain and also release this on closing because plugin hosts can unexpectedly
  112. // close the window for us, and also tend to get cause trouble if setReleasedWhenClosed is NO.
  113. [window setReleasedWhenClosed: YES];
  114. [window retain];
  115. [window setExcludedFromWindowsMenu: (windowStyleFlags & windowIsTemporary) != 0];
  116. [window setIgnoresMouseEvents: (windowStyleFlags & windowIgnoresMouseClicks) != 0];
  117. if ((windowStyleFlags & (windowHasMaximiseButton | windowHasTitleBar)) == (windowHasMaximiseButton | windowHasTitleBar))
  118. [window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
  119. if ([window respondsToSelector: @selector (setRestorable:)])
  120. [window setRestorable: NO];
  121. #if defined (MAC_OS_X_VERSION_10_13) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13)
  122. if ([window respondsToSelector: @selector (setTabbingMode:)])
  123. [window setTabbingMode: NSWindowTabbingModeDisallowed];
  124. #endif
  125. [notificationCenter addObserver: view
  126. selector: @selector (frameChanged:)
  127. name: NSWindowDidMoveNotification
  128. object: window];
  129. [notificationCenter addObserver: view
  130. selector: @selector (frameChanged:)
  131. name: NSWindowDidMiniaturizeNotification
  132. object: window];
  133. [notificationCenter addObserver: view
  134. selector: @selector (windowWillMiniaturize:)
  135. name: NSWindowWillMiniaturizeNotification
  136. object: window];
  137. [notificationCenter addObserver: view
  138. selector: @selector (windowDidDeminiaturize:)
  139. name: NSWindowDidDeminiaturizeNotification
  140. object: window];
  141. }
  142. auto alpha = component.getAlpha();
  143. if (alpha < 1.0f)
  144. setAlpha (alpha);
  145. setTitle (component.getName());
  146. getNativeRealtimeModifiers = []
  147. {
  148. if ([NSEvent respondsToSelector: @selector (modifierFlags)])
  149. NSViewComponentPeer::updateModifiers ([NSEvent modifierFlags]);
  150. return ModifierKeys::currentModifiers;
  151. };
  152. }
  153. ~NSViewComponentPeer() override
  154. {
  155. [notificationCenter removeObserver: view];
  156. setOwner (view, nullptr);
  157. if ([view superview] != nil)
  158. {
  159. redirectWillMoveToWindow (nullptr);
  160. [view removeFromSuperview];
  161. }
  162. if (! isSharedWindow)
  163. {
  164. setOwner (window, nullptr);
  165. [window setContentView: nil];
  166. [window close];
  167. [window release];
  168. }
  169. [view release];
  170. }
  171. //==============================================================================
  172. void* getNativeHandle() const override { return view; }
  173. void setVisible (bool shouldBeVisible) override
  174. {
  175. if (isSharedWindow)
  176. {
  177. if (shouldBeVisible)
  178. [view setHidden: false];
  179. else if ([window firstResponder] != view || ([window firstResponder] == view && [window makeFirstResponder: nil]))
  180. [view setHidden: true];
  181. }
  182. else
  183. {
  184. if (shouldBeVisible)
  185. {
  186. ++insideToFrontCall;
  187. [window orderFront: nil];
  188. --insideToFrontCall;
  189. handleBroughtToFront();
  190. }
  191. else
  192. {
  193. [window orderOut: nil];
  194. }
  195. }
  196. }
  197. void setTitle (const String& title) override
  198. {
  199. JUCE_AUTORELEASEPOOL
  200. {
  201. if (! isSharedWindow)
  202. [window setTitle: juceStringToNS (title)];
  203. }
  204. }
  205. bool setDocumentEditedStatus (bool edited) override
  206. {
  207. if (! hasNativeTitleBar())
  208. return false;
  209. [window setDocumentEdited: edited];
  210. return true;
  211. }
  212. void setRepresentedFile (const File& file) override
  213. {
  214. if (! isSharedWindow)
  215. {
  216. [window setRepresentedFilename: juceStringToNS (file != File()
  217. ? file.getFullPathName()
  218. : String())];
  219. windowRepresentsFile = (file != File());
  220. }
  221. }
  222. void setBounds (const Rectangle<int>& newBounds, bool isNowFullScreen) override
  223. {
  224. fullScreen = isNowFullScreen;
  225. auto r = makeNSRect (newBounds);
  226. auto oldViewSize = [view frame].size;
  227. if (isSharedWindow)
  228. {
  229. r.origin.y = [[view superview] frame].size.height - (r.origin.y + r.size.height);
  230. [view setFrame: r];
  231. }
  232. else
  233. {
  234. // Repaint behaviour of setFrame seemed to change in 10.11, and the drawing became synchronous,
  235. // causing performance issues. But sending an async update causes flickering in older versions,
  236. // hence this version check to use the old behaviour on pre 10.11 machines
  237. static bool isPre10_11 = SystemStats::getOperatingSystemType() <= SystemStats::MacOSX_10_10;
  238. [window setFrame: [window frameRectForContentRect: flippedScreenRect (r)]
  239. display: isPre10_11];
  240. }
  241. if (oldViewSize.width != r.size.width || oldViewSize.height != r.size.height)
  242. [view setNeedsDisplay: true];
  243. }
  244. Rectangle<int> getBounds (const bool global) const
  245. {
  246. auto r = [view frame];
  247. NSWindow* viewWindow = [view window];
  248. if (global && viewWindow != nil)
  249. {
  250. r = [[view superview] convertRect: r toView: nil];
  251. r = [viewWindow convertRectToScreen: r];
  252. flipScreenRect (r);
  253. }
  254. else
  255. {
  256. r.origin.y = [[view superview] frame].size.height - r.origin.y - r.size.height;
  257. }
  258. return convertToRectInt (r);
  259. }
  260. Rectangle<int> getBounds() const override
  261. {
  262. return getBounds (! isSharedWindow);
  263. }
  264. Point<float> localToGlobal (Point<float> relativePosition) override
  265. {
  266. return relativePosition + getBounds (true).getPosition().toFloat();
  267. }
  268. using ComponentPeer::localToGlobal;
  269. Point<float> globalToLocal (Point<float> screenPosition) override
  270. {
  271. return screenPosition - getBounds (true).getPosition().toFloat();
  272. }
  273. using ComponentPeer::globalToLocal;
  274. void setAlpha (float newAlpha) override
  275. {
  276. if (isSharedWindow)
  277. [view setAlphaValue: (CGFloat) newAlpha];
  278. else
  279. [window setAlphaValue: (CGFloat) newAlpha];
  280. }
  281. void setMinimised (bool shouldBeMinimised) override
  282. {
  283. if (! isSharedWindow)
  284. {
  285. if (shouldBeMinimised)
  286. [window miniaturize: nil];
  287. else
  288. [window deminiaturize: nil];
  289. }
  290. }
  291. bool isMinimised() const override
  292. {
  293. return [window isMiniaturized];
  294. }
  295. void setFullScreen (bool shouldBeFullScreen) override
  296. {
  297. if (! isSharedWindow)
  298. {
  299. auto r = lastNonFullscreenBounds;
  300. if (isMinimised())
  301. setMinimised (false);
  302. if (fullScreen != shouldBeFullScreen)
  303. {
  304. if (shouldBeFullScreen && hasNativeTitleBar())
  305. {
  306. fullScreen = true;
  307. [window performZoom: nil];
  308. }
  309. else
  310. {
  311. if (shouldBeFullScreen)
  312. r = component.getParentMonitorArea();
  313. // (can't call the component's setBounds method because that'll reset our fullscreen flag)
  314. if (r != component.getBounds() && ! r.isEmpty())
  315. setBounds (ScalingHelpers::scaledScreenPosToUnscaled (component, r), shouldBeFullScreen);
  316. }
  317. }
  318. }
  319. }
  320. bool isFullScreen() const override
  321. {
  322. return fullScreen;
  323. }
  324. bool isKioskMode() const override
  325. {
  326. return isWindowInKioskMode || ComponentPeer::isKioskMode();
  327. }
  328. static bool isWindowAtPoint (NSWindow* w, NSPoint screenPoint)
  329. {
  330. if ([NSWindow respondsToSelector: @selector (windowNumberAtPoint:belowWindowWithWindowNumber:)])
  331. return [NSWindow windowNumberAtPoint: screenPoint belowWindowWithWindowNumber: 0] == [w windowNumber];
  332. return true;
  333. }
  334. bool contains (Point<int> localPos, bool trueIfInAChildWindow) const override
  335. {
  336. NSRect viewFrame = [view frame];
  337. if (! (isPositiveAndBelow (localPos.getX(), viewFrame.size.width)
  338. && isPositiveAndBelow (localPos.getY(), viewFrame.size.height)))
  339. return false;
  340. if (! SystemStats::isRunningInAppExtensionSandbox())
  341. {
  342. if (NSWindow* const viewWindow = [view window])
  343. {
  344. NSRect windowFrame = [viewWindow frame];
  345. NSPoint windowPoint = [view convertPoint: NSMakePoint (localPos.x, viewFrame.size.height - localPos.y) toView: nil];
  346. NSPoint screenPoint = NSMakePoint (windowFrame.origin.x + windowPoint.x,
  347. windowFrame.origin.y + windowPoint.y);
  348. if (! isWindowAtPoint (viewWindow, screenPoint))
  349. return false;
  350. }
  351. }
  352. NSView* v = [view hitTest: NSMakePoint (viewFrame.origin.x + localPos.getX(),
  353. viewFrame.origin.y + viewFrame.size.height - localPos.getY())];
  354. return trueIfInAChildWindow ? (v != nil)
  355. : (v == view);
  356. }
  357. BorderSize<int> getFrameSize() const override
  358. {
  359. BorderSize<int> b;
  360. if (! isSharedWindow)
  361. {
  362. NSRect v = [view convertRect: [view frame] toView: nil];
  363. NSRect w = [window frame];
  364. b.setTop ((int) (w.size.height - (v.origin.y + v.size.height)));
  365. b.setBottom ((int) v.origin.y);
  366. b.setLeft ((int) v.origin.x);
  367. b.setRight ((int) (w.size.width - (v.origin.x + v.size.width)));
  368. }
  369. return b;
  370. }
  371. void updateFullscreenStatus()
  372. {
  373. if (hasNativeTitleBar())
  374. {
  375. isWindowInKioskMode = (([window styleMask] & NSWindowStyleMaskFullScreen) != 0);
  376. auto screen = getFrameSize().subtractedFrom (component.getParentMonitorArea());
  377. fullScreen = component.getScreenBounds().expanded (2, 2).contains (screen);
  378. }
  379. else
  380. {
  381. isWindowInKioskMode = false;
  382. }
  383. }
  384. bool hasNativeTitleBar() const
  385. {
  386. return (getStyleFlags() & windowHasTitleBar) != 0;
  387. }
  388. bool setAlwaysOnTop (bool alwaysOnTop) override
  389. {
  390. if (! isSharedWindow)
  391. {
  392. [window setLevel: alwaysOnTop ? ((getStyleFlags() & windowIsTemporary) != 0 ? NSPopUpMenuWindowLevel
  393. : NSFloatingWindowLevel)
  394. : NSNormalWindowLevel];
  395. isAlwaysOnTop = alwaysOnTop;
  396. }
  397. return true;
  398. }
  399. void toFront (bool makeActiveWindow) override
  400. {
  401. if (isSharedWindow)
  402. [[view superview] addSubview: view
  403. positioned: NSWindowAbove
  404. relativeTo: nil];
  405. if (window != nil && component.isVisible())
  406. {
  407. ++insideToFrontCall;
  408. if (makeActiveWindow)
  409. [window makeKeyAndOrderFront: nil];
  410. else
  411. [window orderFront: nil];
  412. if (insideToFrontCall <= 1)
  413. {
  414. Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
  415. handleBroughtToFront();
  416. }
  417. --insideToFrontCall;
  418. }
  419. }
  420. void toBehind (ComponentPeer* other) override
  421. {
  422. if (auto* otherPeer = dynamic_cast<NSViewComponentPeer*> (other))
  423. {
  424. if (isSharedWindow)
  425. {
  426. [[view superview] addSubview: view
  427. positioned: NSWindowBelow
  428. relativeTo: otherPeer->view];
  429. }
  430. else if (component.isVisible())
  431. {
  432. [window orderWindow: NSWindowBelow
  433. relativeTo: [otherPeer->window windowNumber]];
  434. }
  435. }
  436. else
  437. {
  438. jassertfalse; // wrong type of window?
  439. }
  440. }
  441. void setIcon (const Image& newIcon) override
  442. {
  443. if (! isSharedWindow)
  444. {
  445. // need to set a dummy represented file here to show the file icon (which we then set to the new icon)
  446. if (! windowRepresentsFile)
  447. [window setRepresentedFilename:juceStringToNS (" ")]; // can't just use an empty string for some reason...
  448. [[window standardWindowButton:NSWindowDocumentIconButton] setImage:imageToNSImage (newIcon)];
  449. }
  450. }
  451. StringArray getAvailableRenderingEngines() override
  452. {
  453. StringArray s ("Software Renderer");
  454. #if USE_COREGRAPHICS_RENDERING
  455. s.add ("CoreGraphics Renderer");
  456. #endif
  457. return s;
  458. }
  459. int getCurrentRenderingEngine() const override
  460. {
  461. return usingCoreGraphics ? 1 : 0;
  462. }
  463. void setCurrentRenderingEngine (int index) override
  464. {
  465. #if USE_COREGRAPHICS_RENDERING
  466. if (usingCoreGraphics != (index > 0))
  467. {
  468. usingCoreGraphics = index > 0;
  469. [view setNeedsDisplay: true];
  470. }
  471. #else
  472. ignoreUnused (index);
  473. #endif
  474. }
  475. void redirectMouseDown (NSEvent* ev)
  476. {
  477. if (! Process::isForegroundProcess())
  478. Process::makeForegroundProcess();
  479. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withFlags (getModifierForButtonNumber ([ev buttonNumber]));
  480. sendMouseEvent (ev);
  481. }
  482. void redirectMouseUp (NSEvent* ev)
  483. {
  484. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutFlags (getModifierForButtonNumber ([ev buttonNumber]));
  485. sendMouseEvent (ev);
  486. showArrowCursorIfNeeded();
  487. }
  488. void redirectMouseDrag (NSEvent* ev)
  489. {
  490. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withFlags (getModifierForButtonNumber ([ev buttonNumber]));
  491. sendMouseEvent (ev);
  492. }
  493. void redirectMouseMove (NSEvent* ev)
  494. {
  495. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons();
  496. NSPoint windowPos = [ev locationInWindow];
  497. NSPoint screenPos = [[ev window] convertRectToScreen: NSMakeRect (windowPos.x, windowPos.y, 1.0f, 1.0f)].origin;
  498. if (isWindowAtPoint ([ev window], screenPos))
  499. sendMouseEvent (ev);
  500. else
  501. // moved into another window which overlaps this one, so trigger an exit
  502. handleMouseEvent (MouseInputSource::InputSourceType::mouse, MouseInputSource::offscreenMousePos, ModifierKeys::currentModifiers,
  503. getMousePressure (ev), MouseInputSource::invalidOrientation, getMouseTime (ev));
  504. showArrowCursorIfNeeded();
  505. }
  506. void redirectMouseEnter (NSEvent* ev)
  507. {
  508. Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
  509. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons();
  510. sendMouseEvent (ev);
  511. }
  512. void redirectMouseExit (NSEvent* ev)
  513. {
  514. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons();
  515. sendMouseEvent (ev);
  516. }
  517. static float checkDeviceDeltaReturnValue (float v) noexcept
  518. {
  519. // (deviceDeltaX can fail and return NaN, so need to sanity-check the result)
  520. v *= 0.5f / 256.0f;
  521. return (v > -1000.0f && v < 1000.0f) ? v : 0.0f;
  522. }
  523. void redirectMouseWheel (NSEvent* ev)
  524. {
  525. updateModifiers (ev);
  526. MouseWheelDetails wheel;
  527. wheel.deltaX = 0;
  528. wheel.deltaY = 0;
  529. wheel.isReversed = false;
  530. wheel.isSmooth = false;
  531. wheel.isInertial = false;
  532. @try
  533. {
  534. if ([ev respondsToSelector: @selector (isDirectionInvertedFromDevice)])
  535. wheel.isReversed = [ev isDirectionInvertedFromDevice];
  536. wheel.isInertial = ([ev momentumPhase] != NSEventPhaseNone);
  537. if ([ev respondsToSelector: @selector (hasPreciseScrollingDeltas)])
  538. {
  539. if ([ev hasPreciseScrollingDeltas])
  540. {
  541. const float scale = 0.5f / 256.0f;
  542. wheel.deltaX = scale * (float) [ev scrollingDeltaX];
  543. wheel.deltaY = scale * (float) [ev scrollingDeltaY];
  544. wheel.isSmooth = true;
  545. }
  546. }
  547. else if ([ev respondsToSelector: @selector (deviceDeltaX)])
  548. {
  549. wheel.deltaX = checkDeviceDeltaReturnValue ((float) getMsgSendFPRetFn() (ev, @selector (deviceDeltaX)));
  550. wheel.deltaY = checkDeviceDeltaReturnValue ((float) getMsgSendFPRetFn() (ev, @selector (deviceDeltaY)));
  551. }
  552. }
  553. @catch (...)
  554. {}
  555. if (wheel.deltaX == 0.0f && wheel.deltaY == 0.0f)
  556. {
  557. const float scale = 10.0f / 256.0f;
  558. wheel.deltaX = scale * (float) [ev deltaX];
  559. wheel.deltaY = scale * (float) [ev deltaY];
  560. }
  561. handleMouseWheel (MouseInputSource::InputSourceType::mouse, getMousePos (ev, view), getMouseTime (ev), wheel);
  562. }
  563. void redirectMagnify (NSEvent* ev)
  564. {
  565. const float invScale = 1.0f - (float) [ev magnification];
  566. if (invScale > 0.0f)
  567. handleMagnifyGesture (MouseInputSource::InputSourceType::mouse, getMousePos (ev, view), getMouseTime (ev), 1.0f / invScale);
  568. }
  569. void redirectCopy (NSObject*) { handleKeyPress (KeyPress ('c', ModifierKeys (ModifierKeys::commandModifier), 'c')); }
  570. void redirectPaste (NSObject*) { handleKeyPress (KeyPress ('v', ModifierKeys (ModifierKeys::commandModifier), 'v')); }
  571. void redirectCut (NSObject*) { handleKeyPress (KeyPress ('x', ModifierKeys (ModifierKeys::commandModifier), 'x')); }
  572. void redirectWillMoveToWindow (NSWindow* newWindow)
  573. {
  574. if (isSharedWindow && [view window] == window && newWindow == nullptr)
  575. {
  576. if (auto* comp = safeComponent.get())
  577. comp->setVisible (false);
  578. }
  579. }
  580. void sendMouseEvent (NSEvent* ev)
  581. {
  582. updateModifiers (ev);
  583. handleMouseEvent (MouseInputSource::InputSourceType::mouse, getMousePos (ev, view), ModifierKeys::currentModifiers,
  584. getMousePressure (ev), MouseInputSource::invalidOrientation, getMouseTime (ev));
  585. }
  586. bool handleKeyEvent (NSEvent* ev, bool isKeyDown)
  587. {
  588. auto unicode = nsStringToJuce ([ev characters]);
  589. auto keyCode = getKeyCodeFromEvent (ev);
  590. #if JUCE_DEBUG_KEYCODES
  591. DBG ("unicode: " + unicode + " " + String::toHexString ((int) unicode[0]));
  592. auto unmodified = nsStringToJuce ([ev charactersIgnoringModifiers]);
  593. DBG ("unmodified: " + unmodified + " " + String::toHexString ((int) unmodified[0]));
  594. #endif
  595. if (keyCode != 0 || unicode.isNotEmpty())
  596. {
  597. if (isKeyDown)
  598. {
  599. bool used = false;
  600. for (auto u = unicode.getCharPointer(); ! u.isEmpty();)
  601. {
  602. auto textCharacter = u.getAndAdvance();
  603. switch (keyCode)
  604. {
  605. case NSLeftArrowFunctionKey:
  606. case NSRightArrowFunctionKey:
  607. case NSUpArrowFunctionKey:
  608. case NSDownArrowFunctionKey:
  609. case NSPageUpFunctionKey:
  610. case NSPageDownFunctionKey:
  611. case NSEndFunctionKey:
  612. case NSHomeFunctionKey:
  613. case NSDeleteFunctionKey:
  614. textCharacter = 0;
  615. break; // (these all seem to generate unwanted garbage unicode strings)
  616. default:
  617. if (([ev modifierFlags] & NSEventModifierFlagCommand) != 0
  618. || (keyCode >= NSF1FunctionKey && keyCode <= NSF35FunctionKey))
  619. textCharacter = 0;
  620. break;
  621. }
  622. used = handleKeyUpOrDown (true) || used;
  623. used = handleKeyPress (keyCode, textCharacter) || used;
  624. }
  625. return used;
  626. }
  627. if (handleKeyUpOrDown (false))
  628. return true;
  629. }
  630. return false;
  631. }
  632. bool redirectKeyDown (NSEvent* ev)
  633. {
  634. // (need to retain this in case a modal loop runs in handleKeyEvent and
  635. // our event object gets lost)
  636. const std::unique_ptr<NSEvent, NSObjectDeleter> r ([ev retain]);
  637. updateKeysDown (ev, true);
  638. bool used = handleKeyEvent (ev, true);
  639. if (([ev modifierFlags] & NSEventModifierFlagCommand) != 0)
  640. {
  641. // for command keys, the key-up event is thrown away, so simulate one..
  642. updateKeysDown (ev, false);
  643. used = (isValidPeer (this) && handleKeyEvent (ev, false)) || used;
  644. }
  645. // (If we're running modally, don't allow unused keystrokes to be passed
  646. // along to other blocked views..)
  647. if (Component::getCurrentlyModalComponent() != nullptr)
  648. used = true;
  649. return used;
  650. }
  651. bool redirectKeyUp (NSEvent* ev)
  652. {
  653. updateKeysDown (ev, false);
  654. return handleKeyEvent (ev, false)
  655. || Component::getCurrentlyModalComponent() != nullptr;
  656. }
  657. void redirectModKeyChange (NSEvent* ev)
  658. {
  659. // (need to retain this in case a modal loop runs and our event object gets lost)
  660. const std::unique_ptr<NSEvent, NSObjectDeleter> r ([ev retain]);
  661. keysCurrentlyDown.clear();
  662. handleKeyUpOrDown (true);
  663. updateModifiers (ev);
  664. handleModifierKeysChange();
  665. }
  666. //==============================================================================
  667. void drawRect (NSRect r)
  668. {
  669. if (r.size.width < 1.0f || r.size.height < 1.0f)
  670. return;
  671. auto cg = (CGContextRef) [[NSGraphicsContext currentContext]
  672. #if (defined (MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10)
  673. CGContext];
  674. #else
  675. graphicsPort];
  676. #endif
  677. if (! component.isOpaque())
  678. CGContextClearRect (cg, CGContextGetClipBoundingBox (cg));
  679. float displayScale = 1.0f;
  680. NSScreen* screen = [[view window] screen];
  681. if ([screen respondsToSelector: @selector (backingScaleFactor)])
  682. displayScale = (float) screen.backingScaleFactor;
  683. #if USE_COREGRAPHICS_RENDERING && JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS
  684. // This option invokes a separate paint call for each rectangle of the clip region.
  685. // It's a long story, but this is a basically a workaround for a CGContext not having
  686. // a way of finding whether a rectangle falls within its clip region
  687. if (usingCoreGraphics)
  688. {
  689. const NSRect* rects = nullptr;
  690. NSInteger numRects = 0;
  691. [view getRectsBeingDrawn: &rects count: &numRects];
  692. if (numRects > 1)
  693. {
  694. for (int i = 0; i < numRects; ++i)
  695. {
  696. NSRect rect = rects[i];
  697. CGContextSaveGState (cg);
  698. CGContextClipToRect (cg, CGRectMake (rect.origin.x, rect.origin.y, rect.size.width, rect.size.height));
  699. drawRect (cg, rect, displayScale);
  700. CGContextRestoreGState (cg);
  701. }
  702. return;
  703. }
  704. }
  705. #endif
  706. drawRect (cg, r, displayScale);
  707. }
  708. void drawRect (CGContextRef cg, NSRect r, float displayScale)
  709. {
  710. #if USE_COREGRAPHICS_RENDERING
  711. if (usingCoreGraphics)
  712. {
  713. CoreGraphicsContext context (cg, (float) [view frame].size.height, displayScale);
  714. invokePaint (context);
  715. }
  716. else
  717. #endif
  718. {
  719. const Point<int> offset (-roundToInt (r.origin.x),
  720. -roundToInt ([view frame].size.height - (r.origin.y + r.size.height)));
  721. auto clipW = (int) (r.size.width + 0.5f);
  722. auto clipH = (int) (r.size.height + 0.5f);
  723. RectangleList<int> clip;
  724. getClipRects (clip, offset, clipW, clipH);
  725. if (! clip.isEmpty())
  726. {
  727. Image temp (component.isOpaque() ? Image::RGB : Image::ARGB,
  728. roundToInt (clipW * displayScale),
  729. roundToInt (clipH * displayScale),
  730. ! component.isOpaque());
  731. {
  732. auto intScale = roundToInt (displayScale);
  733. if (intScale != 1)
  734. clip.scaleAll (intScale);
  735. auto context = component.getLookAndFeel()
  736. .createGraphicsContext (temp, offset * intScale, clip);
  737. if (intScale != 1)
  738. context->addTransform (AffineTransform::scale (displayScale));
  739. invokePaint (*context);
  740. }
  741. CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
  742. CGImageRef image = juce_createCoreGraphicsImage (temp, colourSpace, false);
  743. CGColorSpaceRelease (colourSpace);
  744. CGContextDrawImage (cg, CGRectMake (r.origin.x, r.origin.y, clipW, clipH), image);
  745. CGImageRelease (image);
  746. }
  747. }
  748. }
  749. void repaint (const Rectangle<int>& area) override
  750. {
  751. // In 10.11 changes were made to the way the OS handles repaint regions, and it seems that it can
  752. // no longer be trusted to coalesce all the regions, or to even remember them all without losing
  753. // a few when there's a lot of activity.
  754. // As a work around for this, we use a RectangleList to do our own coalescing of regions before
  755. // asynchronously asking the OS to repaint them.
  756. deferredRepaints.add ((float) area.getX(), (float) ([view frame].size.height - area.getBottom()),
  757. (float) area.getWidth(), (float) area.getHeight());
  758. if (isTimerRunning())
  759. return;
  760. auto now = Time::getMillisecondCounter();
  761. auto msSinceLastRepaint = (lastRepaintTime >= now) ? now - lastRepaintTime
  762. : (std::numeric_limits<uint32>::max() - lastRepaintTime) + now;
  763. static uint32 minimumRepaintInterval = 1000 / 30; // 30fps
  764. // When windows are being resized, artificially throttling high-frequency repaints helps
  765. // to stop the event queue getting clogged, and keeps everything working smoothly.
  766. // For some reason Logic also needs this throttling to record parameter events correctly.
  767. if (msSinceLastRepaint < minimumRepaintInterval && shouldThrottleRepaint())
  768. {
  769. startTimer (static_cast<int> (minimumRepaintInterval - msSinceLastRepaint));
  770. return;
  771. }
  772. setNeedsDisplayRectangles();
  773. }
  774. static bool shouldThrottleRepaint()
  775. {
  776. return areAnyWindowsInLiveResize() || ! JUCEApplication::isStandaloneApp();
  777. }
  778. void timerCallback() override
  779. {
  780. setNeedsDisplayRectangles();
  781. stopTimer();
  782. }
  783. void setNeedsDisplayRectangles()
  784. {
  785. for (auto& i : deferredRepaints)
  786. [view setNeedsDisplayInRect: makeNSRect (i)];
  787. lastRepaintTime = Time::getMillisecondCounter();
  788. deferredRepaints.clear();
  789. }
  790. void invokePaint (LowLevelGraphicsContext& context)
  791. {
  792. handlePaint (context);
  793. }
  794. void performAnyPendingRepaintsNow() override
  795. {
  796. [view displayIfNeeded];
  797. }
  798. static bool areAnyWindowsInLiveResize() noexcept
  799. {
  800. for (NSWindow* w in [NSApp windows])
  801. if ([w inLiveResize])
  802. return true;
  803. return false;
  804. }
  805. //==============================================================================
  806. bool isBlockedByModalComponent()
  807. {
  808. if (auto* modal = Component::getCurrentlyModalComponent())
  809. {
  810. if (insideToFrontCall == 0
  811. && (! getComponent().isParentOf (modal))
  812. && getComponent().isCurrentlyBlockedByAnotherModalComponent())
  813. {
  814. return true;
  815. }
  816. }
  817. return false;
  818. }
  819. void sendModalInputAttemptIfBlocked()
  820. {
  821. if (isBlockedByModalComponent())
  822. if (auto* modal = Component::getCurrentlyModalComponent())
  823. modal->inputAttemptWhenModal();
  824. }
  825. bool canBecomeKeyWindow()
  826. {
  827. return component.isVisible() && (getStyleFlags() & juce::ComponentPeer::windowIgnoresKeyPresses) == 0;
  828. }
  829. bool canBecomeMainWindow()
  830. {
  831. return component.isVisible() && dynamic_cast<ResizableWindow*> (&component) != nullptr;
  832. }
  833. bool worksWhenModal() const
  834. {
  835. // In plugins, the host could put our plugin window inside a modal window, so this
  836. // allows us to successfully open other popups. Feels like there could be edge-case
  837. // problems caused by this, so let us know if you spot any issues..
  838. return ! JUCEApplication::isStandaloneApp();
  839. }
  840. void becomeKeyWindow()
  841. {
  842. handleBroughtToFront();
  843. grabFocus();
  844. }
  845. bool windowShouldClose()
  846. {
  847. if (! isValidPeer (this))
  848. return YES;
  849. handleUserClosingWindow();
  850. return NO;
  851. }
  852. void redirectMovedOrResized()
  853. {
  854. updateFullscreenStatus();
  855. handleMovedOrResized();
  856. }
  857. void viewMovedToWindow()
  858. {
  859. if (isSharedWindow)
  860. {
  861. auto newWindow = [view window];
  862. bool shouldSetVisible = (window == nullptr && newWindow != nullptr);
  863. window = newWindow;
  864. if (shouldSetVisible)
  865. getComponent().setVisible (true);
  866. }
  867. }
  868. void liveResizingStart()
  869. {
  870. if (constrainer == nullptr)
  871. return;
  872. constrainer->resizeStart();
  873. isFirstLiveResize = true;
  874. setFullScreenSizeConstraints (*constrainer);
  875. }
  876. void liveResizingEnd()
  877. {
  878. if (constrainer != nullptr)
  879. constrainer->resizeEnd();
  880. }
  881. NSRect constrainRect (const NSRect r)
  882. {
  883. if (constrainer == nullptr || isKioskMode())
  884. return r;
  885. const auto scale = getComponent().getDesktopScaleFactor();
  886. auto pos = ScalingHelpers::unscaledScreenPosToScaled (scale, convertToRectInt (flippedScreenRect (r)));
  887. const auto original = ScalingHelpers::unscaledScreenPosToScaled (scale, convertToRectInt (flippedScreenRect ([window frame])));
  888. const auto screenBounds = Desktop::getInstance().getDisplays().getTotalBounds (true);
  889. const bool inLiveResize = [window inLiveResize];
  890. if (! inLiveResize || isFirstLiveResize)
  891. {
  892. isFirstLiveResize = false;
  893. isStretchingTop = (pos.getY() != original.getY() && pos.getBottom() == original.getBottom());
  894. isStretchingLeft = (pos.getX() != original.getX() && pos.getRight() == original.getRight());
  895. isStretchingBottom = (pos.getY() == original.getY() && pos.getBottom() != original.getBottom());
  896. isStretchingRight = (pos.getX() == original.getX() && pos.getRight() != original.getRight());
  897. }
  898. constrainer->checkBounds (pos, original, screenBounds,
  899. isStretchingTop, isStretchingLeft, isStretchingBottom, isStretchingRight);
  900. return flippedScreenRect (makeNSRect (ScalingHelpers::scaledScreenPosToUnscaled (scale, pos)));
  901. }
  902. static void showArrowCursorIfNeeded()
  903. {
  904. auto& desktop = Desktop::getInstance();
  905. auto mouse = desktop.getMainMouseSource();
  906. if (mouse.getComponentUnderMouse() == nullptr
  907. && desktop.findComponentAt (mouse.getScreenPosition().roundToInt()) == nullptr)
  908. {
  909. [[NSCursor arrowCursor] set];
  910. }
  911. }
  912. static void updateModifiers (NSEvent* e)
  913. {
  914. updateModifiers ([e modifierFlags]);
  915. }
  916. static void updateModifiers (const NSUInteger flags)
  917. {
  918. int m = 0;
  919. if ((flags & NSEventModifierFlagShift) != 0) m |= ModifierKeys::shiftModifier;
  920. if ((flags & NSEventModifierFlagControl) != 0) m |= ModifierKeys::ctrlModifier;
  921. if ((flags & NSEventModifierFlagOption) != 0) m |= ModifierKeys::altModifier;
  922. if ((flags & NSEventModifierFlagCommand) != 0) m |= ModifierKeys::commandModifier;
  923. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withOnlyMouseButtons().withFlags (m);
  924. }
  925. static void updateKeysDown (NSEvent* ev, bool isKeyDown)
  926. {
  927. updateModifiers (ev);
  928. if (auto keyCode = getKeyCodeFromEvent (ev))
  929. {
  930. if (isKeyDown)
  931. keysCurrentlyDown.addIfNotAlreadyThere (keyCode);
  932. else
  933. keysCurrentlyDown.removeFirstMatchingValue (keyCode);
  934. }
  935. }
  936. static int getKeyCodeFromEvent (NSEvent* ev)
  937. {
  938. // Unfortunately, charactersIgnoringModifiers does not ignore the shift key.
  939. // Using [ev keyCode] is not a solution either as this will,
  940. // for example, return VK_KEY_Y if the key is pressed which
  941. // is typically located at the Y key position on a QWERTY
  942. // keyboard. However, on international keyboards this might not
  943. // be the key labeled Y (for example, on German keyboards this key
  944. // has a Z label). Therefore, we need to query the current keyboard
  945. // layout to figure out what character the key would have produced
  946. // if the shift key was not pressed
  947. String unmodified;
  948. #if JUCE_SUPPORT_CARBON
  949. if (TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource())
  950. {
  951. if (auto layoutData = (CFDataRef) TISGetInputSourceProperty (currentKeyboard,
  952. kTISPropertyUnicodeKeyLayoutData))
  953. {
  954. if (auto* layoutPtr = (const UCKeyboardLayout*) CFDataGetBytePtr (layoutData))
  955. {
  956. UInt32 keysDown = 0;
  957. UniChar buffer[4];
  958. UniCharCount actual;
  959. if (UCKeyTranslate (layoutPtr, [ev keyCode], kUCKeyActionDown, 0, LMGetKbdType(),
  960. kUCKeyTranslateNoDeadKeysBit, &keysDown, sizeof (buffer) / sizeof (UniChar),
  961. &actual, buffer) == 0)
  962. unmodified = String (CharPointer_UTF16 (reinterpret_cast<CharPointer_UTF16::CharType*> (buffer)), 4);
  963. }
  964. }
  965. CFRelease (currentKeyboard);
  966. }
  967. // did the above layout conversion fail
  968. if (unmodified.isEmpty())
  969. #endif
  970. {
  971. unmodified = nsStringToJuce ([ev charactersIgnoringModifiers]);
  972. }
  973. auto keyCode = (int) unmodified[0];
  974. if (keyCode == 0x19) // (backwards-tab)
  975. keyCode = '\t';
  976. else if (keyCode == 0x03) // (enter)
  977. keyCode = '\r';
  978. else
  979. keyCode = (int) CharacterFunctions::toUpperCase ((juce_wchar) keyCode);
  980. if (([ev modifierFlags] & NSEventModifierFlagNumericPad) != 0)
  981. {
  982. const int numPadConversions[] = { '0', KeyPress::numberPad0, '1', KeyPress::numberPad1,
  983. '2', KeyPress::numberPad2, '3', KeyPress::numberPad3,
  984. '4', KeyPress::numberPad4, '5', KeyPress::numberPad5,
  985. '6', KeyPress::numberPad6, '7', KeyPress::numberPad7,
  986. '8', KeyPress::numberPad8, '9', KeyPress::numberPad9,
  987. '+', KeyPress::numberPadAdd, '-', KeyPress::numberPadSubtract,
  988. '*', KeyPress::numberPadMultiply, '/', KeyPress::numberPadDivide,
  989. '.', KeyPress::numberPadDecimalPoint,
  990. ',', KeyPress::numberPadDecimalPoint, // (to deal with non-english kbds)
  991. '=', KeyPress::numberPadEquals };
  992. for (int i = 0; i < numElementsInArray (numPadConversions); i += 2)
  993. if (keyCode == numPadConversions [i])
  994. keyCode = numPadConversions [i + 1];
  995. }
  996. return keyCode;
  997. }
  998. static int64 getMouseTime (NSEvent* e) noexcept
  999. {
  1000. return (Time::currentTimeMillis() - Time::getMillisecondCounter())
  1001. + (int64) ([e timestamp] * 1000.0);
  1002. }
  1003. static float getMousePressure (NSEvent* e) noexcept
  1004. {
  1005. @try
  1006. {
  1007. if (e.type != NSEventTypeMouseEntered && e.type != NSEventTypeMouseExited)
  1008. return (float) e.pressure;
  1009. }
  1010. @catch (NSException* e) {}
  1011. @finally {}
  1012. return 0.0f;
  1013. }
  1014. static Point<float> getMousePos (NSEvent* e, NSView* view)
  1015. {
  1016. NSPoint p = [view convertPoint: [e locationInWindow] fromView: nil];
  1017. return { (float) p.x, (float) ([view frame].size.height - p.y) };
  1018. }
  1019. static int getModifierForButtonNumber (const NSInteger num)
  1020. {
  1021. return num == 0 ? ModifierKeys::leftButtonModifier
  1022. : (num == 1 ? ModifierKeys::rightButtonModifier
  1023. : (num == 2 ? ModifierKeys::middleButtonModifier : 0));
  1024. }
  1025. static unsigned int getNSWindowStyleMask (const int flags) noexcept
  1026. {
  1027. unsigned int style = (flags & windowHasTitleBar) != 0 ? NSWindowStyleMaskTitled
  1028. : NSWindowStyleMaskBorderless;
  1029. if ((flags & windowHasMinimiseButton) != 0) style |= NSWindowStyleMaskMiniaturizable;
  1030. if ((flags & windowHasCloseButton) != 0) style |= NSWindowStyleMaskClosable;
  1031. if ((flags & windowIsResizable) != 0) style |= NSWindowStyleMaskResizable;
  1032. return style;
  1033. }
  1034. static NSArray* getSupportedDragTypes()
  1035. {
  1036. return [NSArray arrayWithObjects: (NSString*) kUTTypeFileURL, (NSString*) kPasteboardTypeFileURLPromise, NSPasteboardTypeString, nil];
  1037. }
  1038. BOOL sendDragCallback (const int type, id <NSDraggingInfo> sender)
  1039. {
  1040. NSPasteboard* pasteboard = [sender draggingPasteboard];
  1041. NSString* contentType = [pasteboard availableTypeFromArray: getSupportedDragTypes()];
  1042. if (contentType == nil)
  1043. return false;
  1044. NSPoint p = [view convertPoint: [sender draggingLocation] fromView: nil];
  1045. ComponentPeer::DragInfo dragInfo;
  1046. dragInfo.position.setXY ((int) p.x, (int) ([view frame].size.height - p.y));
  1047. if (contentType == NSPasteboardTypeString)
  1048. dragInfo.text = nsStringToJuce ([pasteboard stringForType: NSPasteboardTypeString]);
  1049. else
  1050. dragInfo.files = getDroppedFiles (pasteboard, contentType);
  1051. if (! dragInfo.isEmpty())
  1052. {
  1053. switch (type)
  1054. {
  1055. case 0: return handleDragMove (dragInfo);
  1056. case 1: return handleDragExit (dragInfo);
  1057. case 2: return handleDragDrop (dragInfo);
  1058. default: jassertfalse; break;
  1059. }
  1060. }
  1061. return false;
  1062. }
  1063. StringArray getDroppedFiles (NSPasteboard* pasteboard, NSString* contentType)
  1064. {
  1065. StringArray files;
  1066. NSString* iTunesPasteboardType = nsStringLiteral ("CorePasteboardFlavorType 0x6974756E"); // 'itun'
  1067. if ([contentType isEqualToString: (NSString*) kPasteboardTypeFileURLPromise]
  1068. && [[pasteboard types] containsObject: iTunesPasteboardType])
  1069. {
  1070. id list = [pasteboard propertyListForType: iTunesPasteboardType];
  1071. if ([list isKindOfClass: [NSDictionary class]])
  1072. {
  1073. NSDictionary* iTunesDictionary = (NSDictionary*) list;
  1074. NSArray* tracks = [iTunesDictionary valueForKey: nsStringLiteral ("Tracks")];
  1075. NSEnumerator* enumerator = [tracks objectEnumerator];
  1076. NSDictionary* track;
  1077. while ((track = [enumerator nextObject]) != nil)
  1078. {
  1079. if (id value = [track valueForKey: nsStringLiteral ("Location")])
  1080. {
  1081. NSURL* url = [NSURL URLWithString: value];
  1082. if ([url isFileURL])
  1083. files.add (nsStringToJuce ([url path]));
  1084. }
  1085. }
  1086. }
  1087. }
  1088. else
  1089. {
  1090. NSArray* items = [pasteboard readObjectsForClasses:@[[NSURL class]] options: nil];
  1091. for (unsigned int i = 0; i < [items count]; ++i)
  1092. {
  1093. NSURL* url = [items objectAtIndex: i];
  1094. if ([url isFileURL])
  1095. files.add (nsStringToJuce ([url path]));
  1096. }
  1097. }
  1098. return files;
  1099. }
  1100. //==============================================================================
  1101. void viewFocusGain()
  1102. {
  1103. if (currentlyFocusedPeer != this)
  1104. {
  1105. if (ComponentPeer::isValidPeer (currentlyFocusedPeer))
  1106. currentlyFocusedPeer->handleFocusLoss();
  1107. currentlyFocusedPeer = this;
  1108. handleFocusGain();
  1109. }
  1110. }
  1111. void viewFocusLoss()
  1112. {
  1113. if (currentlyFocusedPeer == this)
  1114. {
  1115. currentlyFocusedPeer = nullptr;
  1116. handleFocusLoss();
  1117. }
  1118. }
  1119. bool isFocused() const override
  1120. {
  1121. return (isSharedWindow || ! JUCEApplication::isStandaloneApp())
  1122. ? this == currentlyFocusedPeer
  1123. : [window isKeyWindow];
  1124. }
  1125. void grabFocus() override
  1126. {
  1127. if (window != nil)
  1128. {
  1129. [window makeKeyWindow];
  1130. [window makeFirstResponder: view];
  1131. viewFocusGain();
  1132. }
  1133. }
  1134. void textInputRequired (Point<int>, TextInputTarget&) override {}
  1135. void resetWindowPresentation()
  1136. {
  1137. if (hasNativeTitleBar())
  1138. {
  1139. [window setStyleMask: (NSViewComponentPeer::getNSWindowStyleMask (getStyleFlags()))];
  1140. setTitle (getComponent().getName()); // required to force the OS to update the title
  1141. }
  1142. [NSApp setPresentationOptions: NSApplicationPresentationDefault];
  1143. }
  1144. //==============================================================================
  1145. NSWindow* window = nil;
  1146. NSView* view = nil;
  1147. WeakReference<Component> safeComponent;
  1148. bool isSharedWindow = false, fullScreen = false;
  1149. bool isWindowInKioskMode = false;
  1150. #if USE_COREGRAPHICS_RENDERING
  1151. bool usingCoreGraphics = true;
  1152. #else
  1153. bool usingCoreGraphics = false;
  1154. #endif
  1155. bool isZooming = false, isFirstLiveResize = false, textWasInserted = false;
  1156. bool isStretchingTop = false, isStretchingLeft = false, isStretchingBottom = false, isStretchingRight = false;
  1157. bool windowRepresentsFile = false;
  1158. bool isAlwaysOnTop = false, wasAlwaysOnTop = false;
  1159. String stringBeingComposed;
  1160. NSNotificationCenter* notificationCenter = nil;
  1161. RectangleList<float> deferredRepaints;
  1162. uint32 lastRepaintTime;
  1163. static ComponentPeer* currentlyFocusedPeer;
  1164. static Array<int> keysCurrentlyDown;
  1165. static int insideToFrontCall;
  1166. private:
  1167. static NSView* createViewInstance();
  1168. static NSWindow* createWindowInstance();
  1169. static void setOwner (id viewOrWindow, NSViewComponentPeer* newOwner)
  1170. {
  1171. object_setInstanceVariable (viewOrWindow, "owner", newOwner);
  1172. }
  1173. void getClipRects (RectangleList<int>& clip, Point<int> offset, int clipW, int clipH)
  1174. {
  1175. const NSRect* rects = nullptr;
  1176. NSInteger numRects = 0;
  1177. [view getRectsBeingDrawn: &rects count: &numRects];
  1178. const Rectangle<int> clipBounds (clipW, clipH);
  1179. auto viewH = [view frame].size.height;
  1180. clip.ensureStorageAllocated ((int) numRects);
  1181. for (int i = 0; i < numRects; ++i)
  1182. clip.addWithoutMerging (clipBounds.getIntersection (Rectangle<int> (roundToInt (rects[i].origin.x) + offset.x,
  1183. roundToInt (viewH - (rects[i].origin.y + rects[i].size.height)) + offset.y,
  1184. roundToInt (rects[i].size.width),
  1185. roundToInt (rects[i].size.height))));
  1186. }
  1187. static void appFocusChanged()
  1188. {
  1189. keysCurrentlyDown.clear();
  1190. if (isValidPeer (currentlyFocusedPeer))
  1191. {
  1192. if (Process::isForegroundProcess())
  1193. {
  1194. currentlyFocusedPeer->handleFocusGain();
  1195. ModalComponentManager::getInstance()->bringModalComponentsToFront();
  1196. }
  1197. else
  1198. {
  1199. currentlyFocusedPeer->handleFocusLoss();
  1200. }
  1201. }
  1202. }
  1203. static bool checkEventBlockedByModalComps (NSEvent* e)
  1204. {
  1205. if (Component::getNumCurrentlyModalComponents() == 0)
  1206. return false;
  1207. NSWindow* const w = [e window];
  1208. if (w == nil || [w worksWhenModal])
  1209. return false;
  1210. bool isKey = false, isInputAttempt = false;
  1211. switch ([e type])
  1212. {
  1213. case NSEventTypeKeyDown:
  1214. case NSEventTypeKeyUp:
  1215. isKey = isInputAttempt = true;
  1216. break;
  1217. case NSEventTypeLeftMouseDown:
  1218. case NSEventTypeRightMouseDown:
  1219. case NSEventTypeOtherMouseDown:
  1220. isInputAttempt = true;
  1221. break;
  1222. case NSEventTypeLeftMouseDragged:
  1223. case NSEventTypeRightMouseDragged:
  1224. case NSEventTypeLeftMouseUp:
  1225. case NSEventTypeRightMouseUp:
  1226. case NSEventTypeOtherMouseUp:
  1227. case NSEventTypeOtherMouseDragged:
  1228. if (Desktop::getInstance().getDraggingMouseSource(0) != nullptr)
  1229. return false;
  1230. break;
  1231. case NSEventTypeMouseMoved:
  1232. case NSEventTypeMouseEntered:
  1233. case NSEventTypeMouseExited:
  1234. case NSEventTypeCursorUpdate:
  1235. case NSEventTypeScrollWheel:
  1236. case NSEventTypeTabletPoint:
  1237. case NSEventTypeTabletProximity:
  1238. break;
  1239. case NSEventTypeFlagsChanged:
  1240. case NSEventTypeAppKitDefined:
  1241. case NSEventTypeSystemDefined:
  1242. case NSEventTypeApplicationDefined:
  1243. case NSEventTypePeriodic:
  1244. case NSEventTypeGesture:
  1245. case NSEventTypeMagnify:
  1246. case NSEventTypeSwipe:
  1247. case NSEventTypeRotate:
  1248. case NSEventTypeBeginGesture:
  1249. case NSEventTypeEndGesture:
  1250. case NSEventTypeQuickLook:
  1251. #if JUCE_64BIT
  1252. case NSEventTypeSmartMagnify:
  1253. case NSEventTypePressure:
  1254. #endif
  1255. #if defined (MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12
  1256. #if JUCE_64BIT
  1257. case NSEventTypeDirectTouch:
  1258. #endif
  1259. #if defined (MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15
  1260. case NSEventTypeChangeMode:
  1261. #endif
  1262. #endif
  1263. default:
  1264. return false;
  1265. }
  1266. for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
  1267. {
  1268. if (auto* peer = dynamic_cast<NSViewComponentPeer*> (ComponentPeer::getPeer (i)))
  1269. {
  1270. if ([peer->view window] == w)
  1271. {
  1272. if (isKey)
  1273. {
  1274. if (peer->view == [w firstResponder])
  1275. return false;
  1276. }
  1277. else
  1278. {
  1279. if (peer->isSharedWindow
  1280. ? NSPointInRect ([peer->view convertPoint: [e locationInWindow] fromView: nil], [peer->view bounds])
  1281. : NSPointInRect ([e locationInWindow], NSMakeRect (0, 0, [w frame].size.width, [w frame].size.height)))
  1282. return false;
  1283. }
  1284. }
  1285. }
  1286. }
  1287. if (isInputAttempt)
  1288. {
  1289. if (! [NSApp isActive])
  1290. [NSApp activateIgnoringOtherApps: YES];
  1291. if (auto* modal = Component::getCurrentlyModalComponent())
  1292. modal->inputAttemptWhenModal();
  1293. }
  1294. return true;
  1295. }
  1296. void setFullScreenSizeConstraints (const ComponentBoundsConstrainer& c)
  1297. {
  1298. const auto minSize = NSMakeSize (static_cast<float> (c.getMinimumWidth()),
  1299. 0.0f);
  1300. [window setMinFullScreenContentSize: minSize];
  1301. }
  1302. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NSViewComponentPeer)
  1303. };
  1304. int NSViewComponentPeer::insideToFrontCall = 0;
  1305. //==============================================================================
  1306. struct JuceNSViewClass : public ObjCClass<NSView>
  1307. {
  1308. JuceNSViewClass() : ObjCClass<NSView> ("JUCEView_")
  1309. {
  1310. addIvar<NSViewComponentPeer*> ("owner");
  1311. addMethod (@selector (isOpaque), isOpaque, "c@:");
  1312. addMethod (@selector (drawRect:), drawRect, "v@:", @encode (NSRect));
  1313. addMethod (@selector (mouseDown:), mouseDown, "v@:@");
  1314. addMethod (@selector (asyncMouseDown:), asyncMouseDown, "v@:@");
  1315. addMethod (@selector (mouseUp:), mouseUp, "v@:@");
  1316. addMethod (@selector (asyncMouseUp:), asyncMouseUp, "v@:@");
  1317. addMethod (@selector (mouseDragged:), mouseDragged, "v@:@");
  1318. addMethod (@selector (mouseMoved:), mouseMoved, "v@:@");
  1319. addMethod (@selector (mouseEntered:), mouseEntered, "v@:@");
  1320. addMethod (@selector (mouseExited:), mouseExited, "v@:@");
  1321. addMethod (@selector (rightMouseDown:), mouseDown, "v@:@");
  1322. addMethod (@selector (rightMouseDragged:), mouseDragged, "v@:@");
  1323. addMethod (@selector (rightMouseUp:), mouseUp, "v@:@");
  1324. addMethod (@selector (otherMouseDown:), mouseDown, "v@:@");
  1325. addMethod (@selector (otherMouseDragged:), mouseDragged, "v@:@");
  1326. addMethod (@selector (otherMouseUp:), mouseUp, "v@:@");
  1327. addMethod (@selector (scrollWheel:), scrollWheel, "v@:@");
  1328. addMethod (@selector (magnifyWithEvent:), magnify, "v@:@");
  1329. addMethod (@selector (acceptsFirstMouse:), acceptsFirstMouse, "c@:@");
  1330. addMethod (@selector (frameChanged:), frameChanged, "v@:@");
  1331. addMethod (@selector (windowWillMiniaturize:), windowWillMiniaturize, "v@:@");
  1332. addMethod (@selector (windowDidDeminiaturize:), windowDidDeminiaturize, "v@:@");
  1333. addMethod (@selector (wantsDefaultClipping:), wantsDefaultClipping, "c@:");
  1334. addMethod (@selector (worksWhenModal), worksWhenModal, "c@:");
  1335. addMethod (@selector (viewDidMoveToWindow), viewDidMoveToWindow, "v@:");
  1336. addMethod (@selector (keyDown:), keyDown, "v@:@");
  1337. addMethod (@selector (keyUp:), keyUp, "v@:@");
  1338. addMethod (@selector (insertText:), insertText, "v@:@");
  1339. addMethod (@selector (doCommandBySelector:), doCommandBySelector, "v@::");
  1340. addMethod (@selector (setMarkedText:selectedRange:), setMarkedText, "v@:@", @encode (NSRange));
  1341. addMethod (@selector (unmarkText), unmarkText, "v@:");
  1342. addMethod (@selector (hasMarkedText), hasMarkedText, "c@:");
  1343. addMethod (@selector (conversationIdentifier), conversationIdentifier, "l@:");
  1344. addMethod (@selector (attributedSubstringFromRange:), attributedSubstringFromRange, "@@:", @encode (NSRange));
  1345. addMethod (@selector (markedRange), markedRange, @encode (NSRange), "@:");
  1346. addMethod (@selector (selectedRange), selectedRange, @encode (NSRange), "@:");
  1347. addMethod (@selector (firstRectForCharacterRange:), firstRectForCharacterRange, @encode (NSRect), "@:", @encode (NSRange));
  1348. addMethod (@selector (characterIndexForPoint:), characterIndexForPoint, "L@:", @encode (NSPoint));
  1349. addMethod (@selector (validAttributesForMarkedText), validAttributesForMarkedText, "@@:");
  1350. addMethod (@selector (flagsChanged:), flagsChanged, "v@:@");
  1351. addMethod (@selector (becomeFirstResponder), becomeFirstResponder, "c@:");
  1352. addMethod (@selector (resignFirstResponder), resignFirstResponder, "c@:");
  1353. addMethod (@selector (acceptsFirstResponder), acceptsFirstResponder, "c@:");
  1354. addMethod (@selector (draggingEntered:), draggingEntered, @encode (NSDragOperation), "@:@");
  1355. addMethod (@selector (draggingUpdated:), draggingUpdated, @encode (NSDragOperation), "@:@");
  1356. addMethod (@selector (draggingEnded:), draggingEnded, "v@:@");
  1357. addMethod (@selector (draggingExited:), draggingExited, "v@:@");
  1358. addMethod (@selector (prepareForDragOperation:), prepareForDragOperation, "c@:@");
  1359. addMethod (@selector (performDragOperation:), performDragOperation, "c@:@");
  1360. addMethod (@selector (concludeDragOperation:), concludeDragOperation, "v@:@");
  1361. addMethod (@selector (paste:), paste, "v@:@");
  1362. addMethod (@selector (copy:), copy, "v@:@");
  1363. addMethod (@selector (cut:), cut, "v@:@");
  1364. addMethod (@selector (viewWillMoveToWindow:), willMoveToWindow, "v@:@");
  1365. addProtocol (@protocol (NSTextInput));
  1366. registerClass();
  1367. }
  1368. private:
  1369. static NSViewComponentPeer* getOwner (id self)
  1370. {
  1371. return getIvar<NSViewComponentPeer*> (self, "owner");
  1372. }
  1373. static void mouseDown (id self, SEL s, NSEvent* ev)
  1374. {
  1375. if (JUCEApplicationBase::isStandaloneApp())
  1376. asyncMouseDown (self, s, ev);
  1377. else
  1378. // In some host situations, the host will stop modal loops from working
  1379. // correctly if they're called from a mouse event, so we'll trigger
  1380. // the event asynchronously..
  1381. [self performSelectorOnMainThread: @selector (asyncMouseDown:)
  1382. withObject: ev
  1383. waitUntilDone: NO];
  1384. }
  1385. static void mouseUp (id self, SEL s, NSEvent* ev)
  1386. {
  1387. if (JUCEApplicationBase::isStandaloneApp())
  1388. asyncMouseUp (self, s, ev);
  1389. else
  1390. // In some host situations, the host will stop modal loops from working
  1391. // correctly if they're called from a mouse event, so we'll trigger
  1392. // the event asynchronously..
  1393. [self performSelectorOnMainThread: @selector (asyncMouseUp:)
  1394. withObject: ev
  1395. waitUntilDone: NO];
  1396. }
  1397. static void asyncMouseDown (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseDown (ev); }
  1398. static void asyncMouseUp (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseUp (ev); }
  1399. static void mouseDragged (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseDrag (ev); }
  1400. static void mouseMoved (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseMove (ev); }
  1401. static void mouseEntered (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseEnter (ev); }
  1402. static void mouseExited (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseExit (ev); }
  1403. static void scrollWheel (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseWheel (ev); }
  1404. static void magnify (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMagnify (ev); }
  1405. static void copy (id self, SEL, NSObject* s) { if (auto* p = getOwner (self)) p->redirectCopy (s); }
  1406. static void paste (id self, SEL, NSObject* s) { if (auto* p = getOwner (self)) p->redirectPaste (s); }
  1407. static void cut (id self, SEL, NSObject* s) { if (auto* p = getOwner (self)) p->redirectCut (s); }
  1408. static void willMoveToWindow (id self, SEL, NSWindow* w) { if (auto* p = getOwner (self)) p->redirectWillMoveToWindow (w); }
  1409. static BOOL acceptsFirstMouse (id, SEL, NSEvent*) { return YES; }
  1410. static BOOL wantsDefaultClipping (id, SEL) { return YES; } // (this is the default, but may want to customise it in future)
  1411. static BOOL worksWhenModal (id self, SEL) { if (auto* p = getOwner (self)) return p->worksWhenModal(); return NO; }
  1412. static void drawRect (id self, SEL, NSRect r) { if (auto* p = getOwner (self)) p->drawRect (r); }
  1413. static void frameChanged (id self, SEL, NSNotification*) { if (auto* p = getOwner (self)) p->redirectMovedOrResized(); }
  1414. static void viewDidMoveToWindow (id self, SEL) { if (auto* p = getOwner (self)) p->viewMovedToWindow(); }
  1415. static void windowWillMiniaturize (id self, SEL, NSNotification*)
  1416. {
  1417. if (auto* p = getOwner (self))
  1418. {
  1419. if (p->isAlwaysOnTop)
  1420. {
  1421. // there is a bug when restoring minimised always on top windows so we need
  1422. // to remove this behaviour before minimising and restore it afterwards
  1423. p->setAlwaysOnTop (false);
  1424. p->wasAlwaysOnTop = true;
  1425. }
  1426. }
  1427. }
  1428. static void windowDidDeminiaturize (id self, SEL, NSNotification*)
  1429. {
  1430. if (auto* p = getOwner (self))
  1431. {
  1432. if (p->wasAlwaysOnTop)
  1433. p->setAlwaysOnTop (true);
  1434. p->redirectMovedOrResized();
  1435. }
  1436. }
  1437. static BOOL isOpaque (id self, SEL)
  1438. {
  1439. auto* owner = getOwner (self);
  1440. return owner == nullptr || owner->getComponent().isOpaque();
  1441. }
  1442. //==============================================================================
  1443. static void keyDown (id self, SEL, NSEvent* ev)
  1444. {
  1445. if (auto* owner = getOwner (self))
  1446. {
  1447. auto* target = owner->findCurrentTextInputTarget();
  1448. owner->textWasInserted = false;
  1449. if (target != nullptr)
  1450. [(NSView*) self interpretKeyEvents: [NSArray arrayWithObject: ev]];
  1451. else
  1452. owner->stringBeingComposed.clear();
  1453. if (! (owner->textWasInserted || owner->redirectKeyDown (ev)))
  1454. {
  1455. objc_super s = { self, [NSView class] };
  1456. getMsgSendSuperFn() (&s, @selector (keyDown:), ev);
  1457. }
  1458. }
  1459. }
  1460. static void keyUp (id self, SEL, NSEvent* ev)
  1461. {
  1462. auto* owner = getOwner (self);
  1463. if (owner == nullptr || ! owner->redirectKeyUp (ev))
  1464. {
  1465. objc_super s = { self, [NSView class] };
  1466. getMsgSendSuperFn() (&s, @selector (keyUp:), ev);
  1467. }
  1468. }
  1469. //==============================================================================
  1470. static void insertText (id self, SEL, id aString)
  1471. {
  1472. // This commits multi-byte text when return is pressed, or after every keypress for western keyboards
  1473. if (auto* owner = getOwner (self))
  1474. {
  1475. NSString* newText = [aString isKindOfClass: [NSAttributedString class]] ? [aString string] : aString;
  1476. if ([newText length] > 0)
  1477. {
  1478. if (auto* target = owner->findCurrentTextInputTarget())
  1479. {
  1480. target->insertTextAtCaret (nsStringToJuce (newText));
  1481. owner->textWasInserted = true;
  1482. }
  1483. }
  1484. owner->stringBeingComposed.clear();
  1485. }
  1486. }
  1487. static void doCommandBySelector (id, SEL, SEL) {}
  1488. static void setMarkedText (id self, SEL, id aString, NSRange)
  1489. {
  1490. if (auto* owner = getOwner (self))
  1491. {
  1492. owner->stringBeingComposed = nsStringToJuce ([aString isKindOfClass: [NSAttributedString class]]
  1493. ? [aString string] : aString);
  1494. if (auto* target = owner->findCurrentTextInputTarget())
  1495. {
  1496. auto currentHighlight = target->getHighlightedRegion();
  1497. target->insertTextAtCaret (owner->stringBeingComposed);
  1498. target->setHighlightedRegion (currentHighlight.withLength (owner->stringBeingComposed.length()));
  1499. owner->textWasInserted = true;
  1500. }
  1501. }
  1502. }
  1503. static void unmarkText (id self, SEL)
  1504. {
  1505. if (auto* owner = getOwner (self))
  1506. {
  1507. if (owner->stringBeingComposed.isNotEmpty())
  1508. {
  1509. if (auto* target = owner->findCurrentTextInputTarget())
  1510. {
  1511. target->insertTextAtCaret (owner->stringBeingComposed);
  1512. owner->textWasInserted = true;
  1513. }
  1514. owner->stringBeingComposed.clear();
  1515. }
  1516. }
  1517. }
  1518. static BOOL hasMarkedText (id self, SEL)
  1519. {
  1520. auto* owner = getOwner (self);
  1521. return owner != nullptr && owner->stringBeingComposed.isNotEmpty();
  1522. }
  1523. static long conversationIdentifier (id self, SEL)
  1524. {
  1525. return (long) (pointer_sized_int) self;
  1526. }
  1527. static NSAttributedString* attributedSubstringFromRange (id self, SEL, NSRange theRange)
  1528. {
  1529. if (auto* owner = getOwner (self))
  1530. {
  1531. if (auto* target = owner->findCurrentTextInputTarget())
  1532. {
  1533. Range<int> r ((int) theRange.location,
  1534. (int) (theRange.location + theRange.length));
  1535. return [[[NSAttributedString alloc] initWithString: juceStringToNS (target->getTextInRange (r))] autorelease];
  1536. }
  1537. }
  1538. return nil;
  1539. }
  1540. static NSRange markedRange (id self, SEL)
  1541. {
  1542. if (auto* owner = getOwner (self))
  1543. if (owner->stringBeingComposed.isNotEmpty())
  1544. return NSMakeRange (0, (NSUInteger) owner->stringBeingComposed.length());
  1545. return NSMakeRange (NSNotFound, 0);
  1546. }
  1547. static NSRange selectedRange (id self, SEL)
  1548. {
  1549. if (auto* owner = getOwner (self))
  1550. {
  1551. if (auto* target = owner->findCurrentTextInputTarget())
  1552. {
  1553. auto highlight = target->getHighlightedRegion();
  1554. if (! highlight.isEmpty())
  1555. return NSMakeRange ((NSUInteger) highlight.getStart(),
  1556. (NSUInteger) highlight.getLength());
  1557. }
  1558. }
  1559. return NSMakeRange (NSNotFound, 0);
  1560. }
  1561. static NSRect firstRectForCharacterRange (id self, SEL, NSRange)
  1562. {
  1563. if (auto* owner = getOwner (self))
  1564. if (auto* comp = dynamic_cast<Component*> (owner->findCurrentTextInputTarget()))
  1565. return flippedScreenRect (makeNSRect (comp->getScreenBounds()));
  1566. return NSZeroRect;
  1567. }
  1568. static NSUInteger characterIndexForPoint (id, SEL, NSPoint) { return NSNotFound; }
  1569. static NSArray* validAttributesForMarkedText (id, SEL) { return [NSArray array]; }
  1570. //==============================================================================
  1571. static void flagsChanged (id self, SEL, NSEvent* ev)
  1572. {
  1573. if (auto* owner = getOwner (self))
  1574. owner->redirectModKeyChange (ev);
  1575. }
  1576. static BOOL becomeFirstResponder (id self, SEL)
  1577. {
  1578. if (auto* owner = getOwner (self))
  1579. owner->viewFocusGain();
  1580. return YES;
  1581. }
  1582. static BOOL resignFirstResponder (id self, SEL)
  1583. {
  1584. if (auto* owner = getOwner (self))
  1585. owner->viewFocusLoss();
  1586. return YES;
  1587. }
  1588. static BOOL acceptsFirstResponder (id self, SEL)
  1589. {
  1590. auto* owner = getOwner (self);
  1591. return owner != nullptr && owner->canBecomeKeyWindow();
  1592. }
  1593. //==============================================================================
  1594. static NSDragOperation draggingEntered (id self, SEL s, id<NSDraggingInfo> sender)
  1595. {
  1596. return draggingUpdated (self, s, sender);
  1597. }
  1598. static NSDragOperation draggingUpdated (id self, SEL, id<NSDraggingInfo> sender)
  1599. {
  1600. if (auto* owner = getOwner (self))
  1601. if (owner->sendDragCallback (0, sender))
  1602. return NSDragOperationGeneric;
  1603. return NSDragOperationNone;
  1604. }
  1605. static void draggingEnded (id self, SEL s, id<NSDraggingInfo> sender)
  1606. {
  1607. draggingExited (self, s, sender);
  1608. }
  1609. static void draggingExited (id self, SEL, id<NSDraggingInfo> sender)
  1610. {
  1611. if (auto* owner = getOwner (self))
  1612. owner->sendDragCallback (1, sender);
  1613. }
  1614. static BOOL prepareForDragOperation (id, SEL, id<NSDraggingInfo>)
  1615. {
  1616. return YES;
  1617. }
  1618. static BOOL performDragOperation (id self, SEL, id<NSDraggingInfo> sender)
  1619. {
  1620. auto* owner = getOwner (self);
  1621. return owner != nullptr && owner->sendDragCallback (2, sender);
  1622. }
  1623. static void concludeDragOperation (id, SEL, id<NSDraggingInfo>) {}
  1624. };
  1625. //==============================================================================
  1626. struct JuceNSWindowClass : public ObjCClass<NSWindow>
  1627. {
  1628. JuceNSWindowClass() : ObjCClass<NSWindow> ("JUCEWindow_")
  1629. {
  1630. addIvar<NSViewComponentPeer*> ("owner");
  1631. addMethod (@selector (canBecomeKeyWindow), canBecomeKeyWindow, "c@:");
  1632. addMethod (@selector (canBecomeMainWindow), canBecomeMainWindow, "c@:");
  1633. addMethod (@selector (becomeKeyWindow), becomeKeyWindow, "v@:");
  1634. addMethod (@selector (windowShouldClose:), windowShouldClose, "c@:@");
  1635. addMethod (@selector (constrainFrameRect:toScreen:), constrainFrameRect, @encode (NSRect), "@:", @encode (NSRect), "@");
  1636. addMethod (@selector (windowWillResize:toSize:), windowWillResize, @encode (NSSize), "@:@", @encode (NSSize));
  1637. addMethod (@selector (windowDidExitFullScreen:), windowDidExitFullScreen, "v@:@");
  1638. addMethod (@selector (windowWillEnterFullScreen:), windowWillEnterFullScreen, "v@:@");
  1639. addMethod (@selector (zoom:), zoom, "v@:@");
  1640. addMethod (@selector (windowWillMove:), windowWillMove, "v@:@");
  1641. addMethod (@selector (windowWillStartLiveResize:), windowWillStartLiveResize, "v@:@");
  1642. addMethod (@selector (windowDidEndLiveResize:), windowDidEndLiveResize, "v@:@");
  1643. addMethod (@selector (window:shouldPopUpDocumentPathMenu:), shouldPopUpPathMenu, "B@:@", @encode (NSMenu*));
  1644. addMethod (@selector (window:shouldDragDocumentWithEvent:from:withPasteboard:),
  1645. shouldAllowIconDrag, "B@:@", @encode (NSEvent*), @encode (NSPoint), @encode (NSPasteboard*));
  1646. addProtocol (@protocol (NSWindowDelegate));
  1647. registerClass();
  1648. }
  1649. private:
  1650. static NSViewComponentPeer* getOwner (id self)
  1651. {
  1652. return getIvar<NSViewComponentPeer*> (self, "owner");
  1653. }
  1654. //==============================================================================
  1655. static BOOL canBecomeKeyWindow (id self, SEL)
  1656. {
  1657. auto* owner = getOwner (self);
  1658. return owner != nullptr
  1659. && owner->canBecomeKeyWindow()
  1660. && ! owner->isBlockedByModalComponent();
  1661. }
  1662. static BOOL canBecomeMainWindow (id self, SEL)
  1663. {
  1664. auto* owner = getOwner (self);
  1665. return owner != nullptr
  1666. && owner->canBecomeMainWindow()
  1667. && ! owner->isBlockedByModalComponent();
  1668. }
  1669. static void becomeKeyWindow (id self, SEL)
  1670. {
  1671. sendSuperclassMessage (self, @selector (becomeKeyWindow));
  1672. if (auto* owner = getOwner (self))
  1673. {
  1674. if (owner->canBecomeKeyWindow())
  1675. {
  1676. owner->becomeKeyWindow();
  1677. return;
  1678. }
  1679. // this fixes a bug causing hidden windows to sometimes become visible when the app regains focus
  1680. if (! owner->getComponent().isVisible())
  1681. [(NSWindow*) self orderOut: nil];
  1682. }
  1683. }
  1684. static BOOL windowShouldClose (id self, SEL, id /*window*/)
  1685. {
  1686. auto* owner = getOwner (self);
  1687. return owner == nullptr || owner->windowShouldClose();
  1688. }
  1689. static NSRect constrainFrameRect (id self, SEL, NSRect frameRect, NSScreen*)
  1690. {
  1691. if (auto* owner = getOwner (self))
  1692. frameRect = owner->constrainRect (frameRect);
  1693. return frameRect;
  1694. }
  1695. static NSSize windowWillResize (id self, SEL, NSWindow*, NSSize proposedFrameSize)
  1696. {
  1697. auto* owner = getOwner (self);
  1698. if (owner == nullptr || owner->isZooming)
  1699. return proposedFrameSize;
  1700. NSRect frameRect = [(NSWindow*) self frame];
  1701. frameRect.origin.y -= proposedFrameSize.height - frameRect.size.height;
  1702. frameRect.size = proposedFrameSize;
  1703. frameRect = owner->constrainRect (frameRect);
  1704. if (owner->hasNativeTitleBar())
  1705. owner->sendModalInputAttemptIfBlocked();
  1706. return frameRect.size;
  1707. }
  1708. static void windowDidExitFullScreen (id self, SEL, NSNotification*)
  1709. {
  1710. if (auto* owner = getOwner (self))
  1711. owner->resetWindowPresentation();
  1712. }
  1713. static void windowWillEnterFullScreen (id self, SEL, NSNotification*)
  1714. {
  1715. if (SystemStats::getOperatingSystemType() <= SystemStats::MacOSX_10_9)
  1716. return;
  1717. if (auto* owner = getOwner (self))
  1718. if (owner->hasNativeTitleBar() && (owner->getStyleFlags() & ComponentPeer::windowIsResizable) == 0)
  1719. [owner->window setStyleMask: NSWindowStyleMaskBorderless];
  1720. }
  1721. static void zoom (id self, SEL, id sender)
  1722. {
  1723. if (auto* owner = getOwner (self))
  1724. {
  1725. owner->isZooming = true;
  1726. objc_super s = { self, [NSWindow class] };
  1727. getMsgSendSuperFn() (&s, @selector (zoom:), sender);
  1728. owner->isZooming = false;
  1729. owner->redirectMovedOrResized();
  1730. }
  1731. }
  1732. static void windowWillMove (id self, SEL, NSNotification*)
  1733. {
  1734. if (auto* owner = getOwner (self))
  1735. if (owner->hasNativeTitleBar())
  1736. owner->sendModalInputAttemptIfBlocked();
  1737. }
  1738. static void windowWillStartLiveResize (id self, SEL, NSNotification*)
  1739. {
  1740. if (auto* owner = getOwner (self))
  1741. owner->liveResizingStart();
  1742. }
  1743. static void windowDidEndLiveResize (id self, SEL, NSNotification*)
  1744. {
  1745. if (auto* owner = getOwner (self))
  1746. owner->liveResizingEnd();
  1747. }
  1748. static bool shouldPopUpPathMenu (id self, SEL, id /*window*/, NSMenu*)
  1749. {
  1750. if (auto* owner = getOwner (self))
  1751. return owner->windowRepresentsFile;
  1752. return false;
  1753. }
  1754. static bool shouldAllowIconDrag (id self, SEL, id /*window*/, NSEvent*, NSPoint, NSPasteboard*)
  1755. {
  1756. if (auto* owner = getOwner (self))
  1757. return owner->windowRepresentsFile;
  1758. return false;
  1759. }
  1760. };
  1761. NSView* NSViewComponentPeer::createViewInstance()
  1762. {
  1763. static JuceNSViewClass cls;
  1764. return cls.createInstance();
  1765. }
  1766. NSWindow* NSViewComponentPeer::createWindowInstance()
  1767. {
  1768. static JuceNSWindowClass cls;
  1769. return cls.createInstance();
  1770. }
  1771. //==============================================================================
  1772. ComponentPeer* NSViewComponentPeer::currentlyFocusedPeer = nullptr;
  1773. Array<int> NSViewComponentPeer::keysCurrentlyDown;
  1774. //==============================================================================
  1775. bool KeyPress::isKeyCurrentlyDown (int keyCode)
  1776. {
  1777. if (NSViewComponentPeer::keysCurrentlyDown.contains (keyCode))
  1778. return true;
  1779. if (keyCode >= 'A' && keyCode <= 'Z'
  1780. && NSViewComponentPeer::keysCurrentlyDown.contains ((int) CharacterFunctions::toLowerCase ((juce_wchar) keyCode)))
  1781. return true;
  1782. if (keyCode >= 'a' && keyCode <= 'z'
  1783. && NSViewComponentPeer::keysCurrentlyDown.contains ((int) CharacterFunctions::toUpperCase ((juce_wchar) keyCode)))
  1784. return true;
  1785. return false;
  1786. }
  1787. //==============================================================================
  1788. bool MouseInputSource::SourceList::addSource()
  1789. {
  1790. if (sources.size() == 0)
  1791. {
  1792. addSource (0, MouseInputSource::InputSourceType::mouse);
  1793. return true;
  1794. }
  1795. return false;
  1796. }
  1797. bool MouseInputSource::SourceList::canUseTouch()
  1798. {
  1799. return false;
  1800. }
  1801. //==============================================================================
  1802. void Desktop::setKioskComponent (Component* kioskComp, bool shouldBeEnabled, bool allowMenusAndBars)
  1803. {
  1804. auto* peer = dynamic_cast<NSViewComponentPeer*> (kioskComp->getPeer());
  1805. jassert (peer != nullptr); // (this should have been checked by the caller)
  1806. if (peer->hasNativeTitleBar()
  1807. && [peer->window respondsToSelector: @selector (toggleFullScreen:)])
  1808. {
  1809. if (shouldBeEnabled && ! allowMenusAndBars)
  1810. [NSApp setPresentationOptions: NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar];
  1811. else if (! shouldBeEnabled)
  1812. [NSApp setPresentationOptions: NSApplicationPresentationDefault];
  1813. [peer->window performSelector: @selector (toggleFullScreen:) withObject: nil];
  1814. }
  1815. else
  1816. {
  1817. if (shouldBeEnabled)
  1818. {
  1819. if (peer->hasNativeTitleBar())
  1820. [peer->window setStyleMask: NSWindowStyleMaskBorderless];
  1821. [NSApp setPresentationOptions: (allowMenusAndBars ? (NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)
  1822. : (NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar))];
  1823. kioskComp->setBounds (getDisplays().findDisplayForRect (kioskComp->getScreenBounds()).totalArea);
  1824. peer->becomeKeyWindow();
  1825. }
  1826. else
  1827. {
  1828. peer->resetWindowPresentation();
  1829. }
  1830. }
  1831. }
  1832. void Desktop::allowedOrientationsChanged() {}
  1833. //==============================================================================
  1834. ComponentPeer* Component::createNewPeer (int styleFlags, void* windowToAttachTo)
  1835. {
  1836. return new NSViewComponentPeer (*this, styleFlags, (NSView*) windowToAttachTo);
  1837. }
  1838. //==============================================================================
  1839. const int KeyPress::spaceKey = ' ';
  1840. const int KeyPress::returnKey = 0x0d;
  1841. const int KeyPress::escapeKey = 0x1b;
  1842. const int KeyPress::backspaceKey = 0x7f;
  1843. const int KeyPress::leftKey = NSLeftArrowFunctionKey;
  1844. const int KeyPress::rightKey = NSRightArrowFunctionKey;
  1845. const int KeyPress::upKey = NSUpArrowFunctionKey;
  1846. const int KeyPress::downKey = NSDownArrowFunctionKey;
  1847. const int KeyPress::pageUpKey = NSPageUpFunctionKey;
  1848. const int KeyPress::pageDownKey = NSPageDownFunctionKey;
  1849. const int KeyPress::endKey = NSEndFunctionKey;
  1850. const int KeyPress::homeKey = NSHomeFunctionKey;
  1851. const int KeyPress::deleteKey = NSDeleteFunctionKey;
  1852. const int KeyPress::insertKey = -1;
  1853. const int KeyPress::tabKey = 9;
  1854. const int KeyPress::F1Key = NSF1FunctionKey;
  1855. const int KeyPress::F2Key = NSF2FunctionKey;
  1856. const int KeyPress::F3Key = NSF3FunctionKey;
  1857. const int KeyPress::F4Key = NSF4FunctionKey;
  1858. const int KeyPress::F5Key = NSF5FunctionKey;
  1859. const int KeyPress::F6Key = NSF6FunctionKey;
  1860. const int KeyPress::F7Key = NSF7FunctionKey;
  1861. const int KeyPress::F8Key = NSF8FunctionKey;
  1862. const int KeyPress::F9Key = NSF9FunctionKey;
  1863. const int KeyPress::F10Key = NSF10FunctionKey;
  1864. const int KeyPress::F11Key = NSF11FunctionKey;
  1865. const int KeyPress::F12Key = NSF12FunctionKey;
  1866. const int KeyPress::F13Key = NSF13FunctionKey;
  1867. const int KeyPress::F14Key = NSF14FunctionKey;
  1868. const int KeyPress::F15Key = NSF15FunctionKey;
  1869. const int KeyPress::F16Key = NSF16FunctionKey;
  1870. const int KeyPress::F17Key = NSF17FunctionKey;
  1871. const int KeyPress::F18Key = NSF18FunctionKey;
  1872. const int KeyPress::F19Key = NSF19FunctionKey;
  1873. const int KeyPress::F20Key = NSF20FunctionKey;
  1874. const int KeyPress::F21Key = NSF21FunctionKey;
  1875. const int KeyPress::F22Key = NSF22FunctionKey;
  1876. const int KeyPress::F23Key = NSF23FunctionKey;
  1877. const int KeyPress::F24Key = NSF24FunctionKey;
  1878. const int KeyPress::F25Key = NSF25FunctionKey;
  1879. const int KeyPress::F26Key = NSF26FunctionKey;
  1880. const int KeyPress::F27Key = NSF27FunctionKey;
  1881. const int KeyPress::F28Key = NSF28FunctionKey;
  1882. const int KeyPress::F29Key = NSF29FunctionKey;
  1883. const int KeyPress::F30Key = NSF30FunctionKey;
  1884. const int KeyPress::F31Key = NSF31FunctionKey;
  1885. const int KeyPress::F32Key = NSF32FunctionKey;
  1886. const int KeyPress::F33Key = NSF33FunctionKey;
  1887. const int KeyPress::F34Key = NSF34FunctionKey;
  1888. const int KeyPress::F35Key = NSF35FunctionKey;
  1889. const int KeyPress::numberPad0 = 0x30020;
  1890. const int KeyPress::numberPad1 = 0x30021;
  1891. const int KeyPress::numberPad2 = 0x30022;
  1892. const int KeyPress::numberPad3 = 0x30023;
  1893. const int KeyPress::numberPad4 = 0x30024;
  1894. const int KeyPress::numberPad5 = 0x30025;
  1895. const int KeyPress::numberPad6 = 0x30026;
  1896. const int KeyPress::numberPad7 = 0x30027;
  1897. const int KeyPress::numberPad8 = 0x30028;
  1898. const int KeyPress::numberPad9 = 0x30029;
  1899. const int KeyPress::numberPadAdd = 0x3002a;
  1900. const int KeyPress::numberPadSubtract = 0x3002b;
  1901. const int KeyPress::numberPadMultiply = 0x3002c;
  1902. const int KeyPress::numberPadDivide = 0x3002d;
  1903. const int KeyPress::numberPadSeparator = 0x3002e;
  1904. const int KeyPress::numberPadDecimalPoint = 0x3002f;
  1905. const int KeyPress::numberPadEquals = 0x30030;
  1906. const int KeyPress::numberPadDelete = 0x30031;
  1907. const int KeyPress::playKey = 0x30000;
  1908. const int KeyPress::stopKey = 0x30001;
  1909. const int KeyPress::fastForwardKey = 0x30002;
  1910. const int KeyPress::rewindKey = 0x30003;
  1911. } // namespace juce