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.

juce_mac_NSViewComponentPeer.mm 83KB

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