The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

1388 lines
46KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. #include "juce_mac_CGMetalLayerRenderer.h"
  19. #if TARGET_OS_SIMULATOR && JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS
  20. #warning JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS uses parts of the Metal API that are currently unsupported in the simulator - falling back to JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS=0
  21. #undef JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS
  22. #endif
  23. #if defined (__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
  24. #define JUCE_HAS_IOS_POINTER_SUPPORT 1
  25. #else
  26. #define JUCE_HAS_IOS_POINTER_SUPPORT 0
  27. #endif
  28. namespace juce
  29. {
  30. class UIViewComponentPeer;
  31. static UIInterfaceOrientation getWindowOrientation()
  32. {
  33. UIApplication* sharedApplication = [UIApplication sharedApplication];
  34. #if defined (__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
  35. if (@available (iOS 13.0, *))
  36. {
  37. for (UIScene* scene in [sharedApplication connectedScenes])
  38. if ([scene isKindOfClass: [UIWindowScene class]])
  39. return [(UIWindowScene*) scene interfaceOrientation];
  40. }
  41. #endif
  42. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
  43. return [sharedApplication statusBarOrientation];
  44. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  45. }
  46. namespace Orientations
  47. {
  48. static Desktop::DisplayOrientation convertToJuce (UIInterfaceOrientation orientation)
  49. {
  50. switch (orientation)
  51. {
  52. case UIInterfaceOrientationPortrait: return Desktop::upright;
  53. case UIInterfaceOrientationPortraitUpsideDown: return Desktop::upsideDown;
  54. case UIInterfaceOrientationLandscapeLeft: return Desktop::rotatedClockwise;
  55. case UIInterfaceOrientationLandscapeRight: return Desktop::rotatedAntiClockwise;
  56. case UIInterfaceOrientationUnknown:
  57. default: jassertfalse; // unknown orientation!
  58. }
  59. return Desktop::upright;
  60. }
  61. static UIInterfaceOrientation convertFromJuce (Desktop::DisplayOrientation orientation)
  62. {
  63. switch (orientation)
  64. {
  65. case Desktop::upright: return UIInterfaceOrientationPortrait;
  66. case Desktop::upsideDown: return UIInterfaceOrientationPortraitUpsideDown;
  67. case Desktop::rotatedClockwise: return UIInterfaceOrientationLandscapeLeft;
  68. case Desktop::rotatedAntiClockwise: return UIInterfaceOrientationLandscapeRight;
  69. case Desktop::allOrientations:
  70. default: jassertfalse; // unknown orientation!
  71. }
  72. return UIInterfaceOrientationPortrait;
  73. }
  74. static NSUInteger getSupportedOrientations()
  75. {
  76. NSUInteger allowed = 0;
  77. auto& d = Desktop::getInstance();
  78. if (d.isOrientationEnabled (Desktop::upright)) allowed |= UIInterfaceOrientationMaskPortrait;
  79. if (d.isOrientationEnabled (Desktop::upsideDown)) allowed |= UIInterfaceOrientationMaskPortraitUpsideDown;
  80. if (d.isOrientationEnabled (Desktop::rotatedClockwise)) allowed |= UIInterfaceOrientationMaskLandscapeLeft;
  81. if (d.isOrientationEnabled (Desktop::rotatedAntiClockwise)) allowed |= UIInterfaceOrientationMaskLandscapeRight;
  82. return allowed;
  83. }
  84. }
  85. enum class MouseEventFlags
  86. {
  87. none,
  88. down,
  89. up,
  90. upAndCancel,
  91. };
  92. //==============================================================================
  93. } // namespace juce
  94. using namespace juce;
  95. struct CADisplayLinkDeleter
  96. {
  97. void operator() (CADisplayLink* displayLink) const noexcept
  98. {
  99. [displayLink invalidate];
  100. [displayLink release];
  101. }
  102. };
  103. @interface JuceUIView : UIView <UITextViewDelegate>
  104. {
  105. @public
  106. UIViewComponentPeer* owner;
  107. UITextView* hiddenTextView;
  108. std::unique_ptr<CADisplayLink, CADisplayLinkDeleter> displayLink;
  109. }
  110. - (JuceUIView*) initWithOwner: (UIViewComponentPeer*) owner withFrame: (CGRect) frame;
  111. - (void) dealloc;
  112. + (Class) layerClass;
  113. - (void) displayLinkCallback: (CADisplayLink*) dl;
  114. - (void) drawRect: (CGRect) r;
  115. - (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event;
  116. - (void) touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event;
  117. - (void) touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event;
  118. - (void) touchesCancelled: (NSSet*) touches withEvent: (UIEvent*) event;
  119. #if JUCE_HAS_IOS_POINTER_SUPPORT
  120. - (void) onHover: (UIHoverGestureRecognizer*) gesture API_AVAILABLE (ios (13.0));
  121. - (void) onScroll: (UIPanGestureRecognizer*) gesture;
  122. #endif
  123. - (BOOL) becomeFirstResponder;
  124. - (BOOL) resignFirstResponder;
  125. - (BOOL) canBecomeFirstResponder;
  126. - (BOOL) textView: (UITextView*) textView shouldChangeTextInRange: (NSRange) range replacementText: (NSString*) text;
  127. - (void) traitCollectionDidChange: (UITraitCollection*) previousTraitCollection;
  128. - (BOOL) isAccessibilityElement;
  129. - (CGRect) accessibilityFrame;
  130. - (NSArray*) accessibilityElements;
  131. @end
  132. //==============================================================================
  133. @interface JuceUIViewController : UIViewController
  134. {
  135. }
  136. - (JuceUIViewController*) init;
  137. - (NSUInteger) supportedInterfaceOrientations;
  138. - (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) interfaceOrientation;
  139. - (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) toInterfaceOrientation duration: (NSTimeInterval) duration;
  140. - (void) didRotateFromInterfaceOrientation: (UIInterfaceOrientation) fromInterfaceOrientation;
  141. - (void) viewWillTransitionToSize: (CGSize) size withTransitionCoordinator: (id<UIViewControllerTransitionCoordinator>) coordinator;
  142. - (BOOL) prefersStatusBarHidden;
  143. - (UIStatusBarStyle) preferredStatusBarStyle;
  144. - (void) viewDidLoad;
  145. - (void) viewWillAppear: (BOOL) animated;
  146. - (void) viewDidAppear: (BOOL) animated;
  147. - (void) viewWillLayoutSubviews;
  148. - (void) viewDidLayoutSubviews;
  149. @end
  150. //==============================================================================
  151. @interface JuceUIWindow : UIWindow
  152. {
  153. @private
  154. UIViewComponentPeer* owner;
  155. }
  156. - (void) setOwner: (UIViewComponentPeer*) owner;
  157. - (void) becomeKeyWindow;
  158. @end
  159. //==============================================================================
  160. //==============================================================================
  161. namespace juce
  162. {
  163. struct UIViewPeerControllerReceiver
  164. {
  165. virtual ~UIViewPeerControllerReceiver() = default;
  166. virtual void setViewController (UIViewController*) = 0;
  167. };
  168. class UIViewComponentPeer : public ComponentPeer,
  169. private UIViewPeerControllerReceiver
  170. {
  171. public:
  172. UIViewComponentPeer (Component&, int windowStyleFlags, UIView* viewToAttachTo);
  173. ~UIViewComponentPeer() override;
  174. //==============================================================================
  175. void* getNativeHandle() const override { return view; }
  176. void setVisible (bool shouldBeVisible) override;
  177. void setTitle (const String& title) override;
  178. void setBounds (const Rectangle<int>&, bool isNowFullScreen) override;
  179. void setViewController (UIViewController* newController) override
  180. {
  181. jassert (controller == nullptr);
  182. controller = [newController retain];
  183. }
  184. Rectangle<int> getBounds() const override { return getBounds (! isSharedWindow); }
  185. Rectangle<int> getBounds (bool global) const;
  186. Point<float> localToGlobal (Point<float> relativePosition) override;
  187. Point<float> globalToLocal (Point<float> screenPosition) override;
  188. using ComponentPeer::localToGlobal;
  189. using ComponentPeer::globalToLocal;
  190. void setAlpha (float newAlpha) override;
  191. void setMinimised (bool) override {}
  192. bool isMinimised() const override { return false; }
  193. void setFullScreen (bool shouldBeFullScreen) override;
  194. bool isFullScreen() const override { return fullScreen; }
  195. bool contains (Point<int> localPos, bool trueIfInAChildWindow) const override;
  196. OptionalBorderSize getFrameSizeIfPresent() const override { return {}; }
  197. BorderSize<int> getFrameSize() const override { return BorderSize<int>(); }
  198. bool setAlwaysOnTop (bool alwaysOnTop) override;
  199. void toFront (bool makeActiveWindow) override;
  200. void toBehind (ComponentPeer* other) override;
  201. void setIcon (const Image& newIcon) override;
  202. StringArray getAvailableRenderingEngines() override { return StringArray ("CoreGraphics Renderer"); }
  203. void displayLinkCallback();
  204. void drawRect (CGRect);
  205. void drawRectWithContext (CGContextRef, CGRect);
  206. bool canBecomeKeyWindow();
  207. //==============================================================================
  208. void viewFocusGain();
  209. void viewFocusLoss();
  210. bool isFocused() const override;
  211. void grabFocus() override;
  212. void textInputRequired (Point<int>, TextInputTarget&) override;
  213. void dismissPendingTextInput() override;
  214. BOOL textViewReplaceCharacters (Range<int>, const String&);
  215. void updateHiddenTextContent (TextInputTarget&);
  216. void updateScreenBounds();
  217. void handleTouches (UIEvent*, MouseEventFlags);
  218. #if JUCE_HAS_IOS_POINTER_SUPPORT
  219. API_AVAILABLE (ios (13.0)) void onHover (UIHoverGestureRecognizer*);
  220. void onScroll (UIPanGestureRecognizer*);
  221. #endif
  222. //==============================================================================
  223. void repaint (const Rectangle<int>& area) override;
  224. void performAnyPendingRepaintsNow() override;
  225. //==============================================================================
  226. UIWindow* window = nil;
  227. JuceUIView* view = nil;
  228. UIViewController* controller = nil;
  229. const bool isSharedWindow, isAppex;
  230. bool fullScreen = false, insideDrawRect = false;
  231. static int64 getMouseTime (NSTimeInterval timestamp) noexcept
  232. {
  233. return (Time::currentTimeMillis() - Time::getMillisecondCounter())
  234. + (int64) (timestamp * 1000.0);
  235. }
  236. static int64 getMouseTime (UIEvent* e) noexcept
  237. {
  238. return getMouseTime ([e timestamp]);
  239. }
  240. static NSString* getDarkModeNotificationName()
  241. {
  242. return @"ViewDarkModeChanged";
  243. }
  244. static MultiTouchMapper<UITouch*> currentTouches;
  245. private:
  246. void appStyleChanged() override
  247. {
  248. [controller setNeedsStatusBarAppearanceUpdate];
  249. }
  250. //==============================================================================
  251. class AsyncRepaintMessage : public CallbackMessage
  252. {
  253. public:
  254. UIViewComponentPeer* const peer;
  255. const Rectangle<int> rect;
  256. AsyncRepaintMessage (UIViewComponentPeer* const p, const Rectangle<int>& r)
  257. : peer (p), rect (r)
  258. {
  259. }
  260. void messageCallback() override
  261. {
  262. if (ComponentPeer::isValidPeer (peer))
  263. peer->repaint (rect);
  264. }
  265. };
  266. std::unique_ptr<CoreGraphicsMetalLayerRenderer> metalRenderer;
  267. RectangleList<float> deferredRepaints;
  268. //==============================================================================
  269. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIViewComponentPeer)
  270. };
  271. static UIViewComponentPeer* getViewPeer (JuceUIViewController* c)
  272. {
  273. if (JuceUIView* juceView = (JuceUIView*) [c view])
  274. return juceView->owner;
  275. jassertfalse;
  276. return nullptr;
  277. }
  278. static void sendScreenBoundsUpdate (JuceUIViewController* c)
  279. {
  280. if (auto* peer = getViewPeer (c))
  281. peer->updateScreenBounds();
  282. }
  283. static bool isKioskModeView (JuceUIViewController* c)
  284. {
  285. if (auto* peer = getViewPeer (c))
  286. return Desktop::getInstance().getKioskModeComponent() == &(peer->getComponent());
  287. return false;
  288. }
  289. MultiTouchMapper<UITouch*> UIViewComponentPeer::currentTouches;
  290. } // namespace juce
  291. //==============================================================================
  292. //==============================================================================
  293. @implementation JuceUIViewController
  294. - (JuceUIViewController*) init
  295. {
  296. self = [super init];
  297. return self;
  298. }
  299. - (NSUInteger) supportedInterfaceOrientations
  300. {
  301. return Orientations::getSupportedOrientations();
  302. }
  303. - (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) interfaceOrientation
  304. {
  305. return Desktop::getInstance().isOrientationEnabled (Orientations::convertToJuce (interfaceOrientation));
  306. }
  307. - (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) toInterfaceOrientation
  308. duration: (NSTimeInterval) duration
  309. {
  310. ignoreUnused (toInterfaceOrientation, duration);
  311. [UIView setAnimationsEnabled: NO]; // disable this because it goes the wrong way and looks like crap.
  312. }
  313. - (void) didRotateFromInterfaceOrientation: (UIInterfaceOrientation) fromInterfaceOrientation
  314. {
  315. ignoreUnused (fromInterfaceOrientation);
  316. sendScreenBoundsUpdate (self);
  317. [UIView setAnimationsEnabled: YES];
  318. }
  319. - (void) viewWillTransitionToSize: (CGSize) size withTransitionCoordinator: (id<UIViewControllerTransitionCoordinator>) coordinator
  320. {
  321. [super viewWillTransitionToSize: size withTransitionCoordinator: coordinator];
  322. [coordinator animateAlongsideTransition: nil completion: ^void (id<UIViewControllerTransitionCoordinatorContext>)
  323. {
  324. sendScreenBoundsUpdate (self);
  325. }];
  326. }
  327. - (BOOL) prefersStatusBarHidden
  328. {
  329. if (isKioskModeView (self))
  330. return true;
  331. return [[[NSBundle mainBundle] objectForInfoDictionaryKey: @"UIStatusBarHidden"] boolValue];
  332. }
  333. #if defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0
  334. - (BOOL) prefersHomeIndicatorAutoHidden
  335. {
  336. return isKioskModeView (self);
  337. }
  338. #endif
  339. - (UIStatusBarStyle) preferredStatusBarStyle
  340. {
  341. #if defined (__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
  342. if (@available (iOS 13.0, *))
  343. {
  344. if (auto* peer = getViewPeer (self))
  345. {
  346. switch (peer->getAppStyle())
  347. {
  348. case ComponentPeer::Style::automatic:
  349. return UIStatusBarStyleDefault;
  350. case ComponentPeer::Style::light:
  351. return UIStatusBarStyleDarkContent;
  352. case ComponentPeer::Style::dark:
  353. return UIStatusBarStyleLightContent;
  354. }
  355. }
  356. }
  357. #endif
  358. return UIStatusBarStyleDefault;
  359. }
  360. - (void) viewDidLoad
  361. {
  362. sendScreenBoundsUpdate (self);
  363. [super viewDidLoad];
  364. }
  365. - (void) viewWillAppear: (BOOL) animated
  366. {
  367. sendScreenBoundsUpdate (self);
  368. [super viewWillAppear:animated];
  369. }
  370. - (void) viewDidAppear: (BOOL) animated
  371. {
  372. sendScreenBoundsUpdate (self);
  373. [super viewDidAppear:animated];
  374. }
  375. - (void) viewWillLayoutSubviews
  376. {
  377. sendScreenBoundsUpdate (self);
  378. }
  379. - (void) viewDidLayoutSubviews
  380. {
  381. sendScreenBoundsUpdate (self);
  382. }
  383. @end
  384. @implementation JuceUIView
  385. - (JuceUIView*) initWithOwner: (UIViewComponentPeer*) peer
  386. withFrame: (CGRect) frame
  387. {
  388. [super initWithFrame: frame];
  389. owner = peer;
  390. displayLink.reset ([CADisplayLink displayLinkWithTarget: self
  391. selector: @selector (displayLinkCallback:)]);
  392. [displayLink.get() addToRunLoop: [NSRunLoop mainRunLoop]
  393. forMode: NSDefaultRunLoopMode];
  394. hiddenTextView = [[UITextView alloc] initWithFrame: CGRectZero];
  395. [self addSubview: hiddenTextView];
  396. hiddenTextView.delegate = self;
  397. hiddenTextView.autocapitalizationType = UITextAutocapitalizationTypeNone;
  398. hiddenTextView.autocorrectionType = UITextAutocorrectionTypeNo;
  399. hiddenTextView.inputAssistantItem.leadingBarButtonGroups = @[];
  400. hiddenTextView.inputAssistantItem.trailingBarButtonGroups = @[];
  401. #if JUCE_HAS_IOS_POINTER_SUPPORT
  402. if (@available (iOS 13.4, *))
  403. {
  404. auto hoverRecognizer = [[[UIHoverGestureRecognizer alloc] initWithTarget: self action: @selector (onHover:)] autorelease];
  405. [hoverRecognizer setCancelsTouchesInView: NO];
  406. [hoverRecognizer setRequiresExclusiveTouchType: YES];
  407. [self addGestureRecognizer: hoverRecognizer];
  408. auto panRecognizer = [[[UIPanGestureRecognizer alloc] initWithTarget: self action: @selector (onScroll:)] autorelease];
  409. [panRecognizer setCancelsTouchesInView: NO];
  410. [panRecognizer setRequiresExclusiveTouchType: YES];
  411. [panRecognizer setAllowedScrollTypesMask: UIScrollTypeMaskAll];
  412. [panRecognizer setMaximumNumberOfTouches: 0];
  413. [self addGestureRecognizer: panRecognizer];
  414. }
  415. #endif
  416. return self;
  417. }
  418. - (void) dealloc
  419. {
  420. [hiddenTextView removeFromSuperview];
  421. [hiddenTextView release];
  422. displayLink = nullptr;
  423. [super dealloc];
  424. }
  425. //==============================================================================
  426. + (Class) layerClass
  427. {
  428. #if JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS
  429. if (@available (iOS 12, *))
  430. return [CAMetalLayer class];
  431. #endif
  432. return [CALayer class];
  433. }
  434. - (void) displayLinkCallback: (CADisplayLink*) dl
  435. {
  436. if (owner != nullptr)
  437. owner->displayLinkCallback();
  438. }
  439. //==============================================================================
  440. - (void) drawRect: (CGRect) r
  441. {
  442. if (owner != nullptr)
  443. owner->drawRect (r);
  444. }
  445. //==============================================================================
  446. - (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event
  447. {
  448. ignoreUnused (touches);
  449. if (owner != nullptr)
  450. owner->handleTouches (event, MouseEventFlags::down);
  451. }
  452. - (void) touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event
  453. {
  454. ignoreUnused (touches);
  455. if (owner != nullptr)
  456. owner->handleTouches (event, MouseEventFlags::none);
  457. }
  458. - (void) touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event
  459. {
  460. ignoreUnused (touches);
  461. if (owner != nullptr)
  462. owner->handleTouches (event, MouseEventFlags::up);
  463. }
  464. - (void) touchesCancelled: (NSSet*) touches withEvent: (UIEvent*) event
  465. {
  466. if (owner != nullptr)
  467. owner->handleTouches (event, MouseEventFlags::upAndCancel);
  468. [self touchesEnded: touches withEvent: event];
  469. }
  470. #if JUCE_HAS_IOS_POINTER_SUPPORT
  471. - (void) onHover: (UIHoverGestureRecognizer*) gesture
  472. {
  473. if (owner != nullptr)
  474. owner->onHover (gesture);
  475. }
  476. - (void) onScroll: (UIPanGestureRecognizer*) gesture
  477. {
  478. if (owner != nullptr)
  479. owner->onScroll (gesture);
  480. }
  481. #endif
  482. //==============================================================================
  483. - (BOOL) becomeFirstResponder
  484. {
  485. if (owner != nullptr)
  486. owner->viewFocusGain();
  487. return true;
  488. }
  489. - (BOOL) resignFirstResponder
  490. {
  491. if (owner != nullptr)
  492. owner->viewFocusLoss();
  493. return [super resignFirstResponder];
  494. }
  495. - (BOOL) canBecomeFirstResponder
  496. {
  497. return owner != nullptr && owner->canBecomeKeyWindow();
  498. }
  499. - (BOOL) textView: (UITextView*) textView shouldChangeTextInRange: (NSRange) range replacementText: (NSString*) text
  500. {
  501. ignoreUnused (textView);
  502. return owner->textViewReplaceCharacters (Range<int> ((int) range.location, (int) (range.location + range.length)),
  503. nsStringToJuce (text));
  504. }
  505. - (void) traitCollectionDidChange: (UITraitCollection*) previousTraitCollection
  506. {
  507. [super traitCollectionDidChange: previousTraitCollection];
  508. #if defined (__IPHONE_12_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_0
  509. if (@available (iOS 12.0, *))
  510. {
  511. const auto wasDarkModeActive = ([previousTraitCollection userInterfaceStyle] == UIUserInterfaceStyleDark);
  512. if (wasDarkModeActive != Desktop::getInstance().isDarkModeActive())
  513. [[NSNotificationCenter defaultCenter] postNotificationName: UIViewComponentPeer::getDarkModeNotificationName()
  514. object: nil];
  515. }
  516. #endif
  517. }
  518. - (BOOL) isAccessibilityElement
  519. {
  520. return NO;
  521. }
  522. - (CGRect) accessibilityFrame
  523. {
  524. if (owner != nullptr)
  525. if (auto* handler = owner->getComponent().getAccessibilityHandler())
  526. return convertToCGRect (handler->getComponent().getScreenBounds());
  527. return CGRectZero;
  528. }
  529. - (NSArray*) accessibilityElements
  530. {
  531. if (owner != nullptr)
  532. if (auto* handler = owner->getComponent().getAccessibilityHandler())
  533. return getContainerAccessibilityElements (*handler);
  534. return nil;
  535. }
  536. @end
  537. //==============================================================================
  538. @implementation JuceUIWindow
  539. - (void) setOwner: (UIViewComponentPeer*) peer
  540. {
  541. owner = peer;
  542. }
  543. - (void) becomeKeyWindow
  544. {
  545. [super becomeKeyWindow];
  546. if (owner != nullptr)
  547. owner->grabFocus();
  548. }
  549. @end
  550. //==============================================================================
  551. //==============================================================================
  552. namespace juce
  553. {
  554. bool KeyPress::isKeyCurrentlyDown (int)
  555. {
  556. return false;
  557. }
  558. Point<float> juce_lastMousePos;
  559. //==============================================================================
  560. UIViewComponentPeer::UIViewComponentPeer (Component& comp, int windowStyleFlags, UIView* viewToAttachTo)
  561. : ComponentPeer (comp, windowStyleFlags),
  562. isSharedWindow (viewToAttachTo != nil),
  563. isAppex (SystemStats::isRunningInAppExtensionSandbox())
  564. {
  565. CGRect r = convertToCGRect (component.getBounds());
  566. view = [[JuceUIView alloc] initWithOwner: this withFrame: r];
  567. view.multipleTouchEnabled = YES;
  568. view.hidden = true;
  569. view.opaque = component.isOpaque();
  570. view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent: 0];
  571. #if JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS
  572. if (@available (iOS 12, *))
  573. metalRenderer = std::make_unique<CoreGraphicsMetalLayerRenderer> ((CAMetalLayer*) view.layer, comp);
  574. #endif
  575. if ((windowStyleFlags & ComponentPeer::windowRequiresSynchronousCoreGraphicsRendering) == 0)
  576. [[view layer] setDrawsAsynchronously: YES];
  577. if (isSharedWindow)
  578. {
  579. window = [viewToAttachTo window];
  580. [viewToAttachTo addSubview: view];
  581. }
  582. else
  583. {
  584. r = convertToCGRect (component.getBounds());
  585. r.origin.y = [UIScreen mainScreen].bounds.size.height - (r.origin.y + r.size.height);
  586. window = [[JuceUIWindow alloc] initWithFrame: r];
  587. [((JuceUIWindow*) window) setOwner: this];
  588. controller = [[JuceUIViewController alloc] init];
  589. controller.view = view;
  590. window.rootViewController = controller;
  591. window.hidden = true;
  592. window.opaque = component.isOpaque();
  593. window.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent: 0];
  594. if (component.isAlwaysOnTop())
  595. window.windowLevel = UIWindowLevelAlert;
  596. view.frame = CGRectMake (0, 0, r.size.width, r.size.height);
  597. }
  598. setTitle (component.getName());
  599. setVisible (component.isVisible());
  600. }
  601. static UIViewComponentPeer* currentlyFocusedPeer = nullptr;
  602. UIViewComponentPeer::~UIViewComponentPeer()
  603. {
  604. if (currentlyFocusedPeer == this)
  605. currentlyFocusedPeer = nullptr;
  606. currentTouches.deleteAllTouchesForPeer (this);
  607. view->owner = nullptr;
  608. [view removeFromSuperview];
  609. [view release];
  610. [controller release];
  611. if (! isSharedWindow)
  612. {
  613. [((JuceUIWindow*) window) setOwner: nil];
  614. #if defined (__IPHONE_13_0)
  615. if (@available (iOS 13.0, *))
  616. window.windowScene = nil;
  617. #endif
  618. [window release];
  619. }
  620. }
  621. //==============================================================================
  622. void UIViewComponentPeer::setVisible (bool shouldBeVisible)
  623. {
  624. if (! isSharedWindow)
  625. window.hidden = ! shouldBeVisible;
  626. view.hidden = ! shouldBeVisible;
  627. }
  628. void UIViewComponentPeer::setTitle (const String&)
  629. {
  630. // xxx is this possible?
  631. }
  632. void UIViewComponentPeer::setBounds (const Rectangle<int>& newBounds, const bool isNowFullScreen)
  633. {
  634. fullScreen = isNowFullScreen;
  635. if (isSharedWindow)
  636. {
  637. CGRect r = convertToCGRect (newBounds);
  638. if (view.frame.size.width != r.size.width || view.frame.size.height != r.size.height)
  639. [view setNeedsDisplay];
  640. view.frame = r;
  641. }
  642. else
  643. {
  644. window.frame = convertToCGRect (newBounds);
  645. view.frame = CGRectMake (0, 0, (CGFloat) newBounds.getWidth(), (CGFloat) newBounds.getHeight());
  646. handleMovedOrResized();
  647. }
  648. }
  649. Rectangle<int> UIViewComponentPeer::getBounds (const bool global) const
  650. {
  651. auto r = view.frame;
  652. if (global)
  653. {
  654. if (view.window != nil)
  655. {
  656. r = [view convertRect: r toView: view.window];
  657. r = [view.window convertRect: r toWindow: nil];
  658. }
  659. else if (window != nil)
  660. {
  661. r.origin.x += window.frame.origin.x;
  662. r.origin.y += window.frame.origin.y;
  663. }
  664. }
  665. return convertToRectInt (r);
  666. }
  667. Point<float> UIViewComponentPeer::localToGlobal (Point<float> relativePosition)
  668. {
  669. return relativePosition + getBounds (true).getPosition().toFloat();
  670. }
  671. Point<float> UIViewComponentPeer::globalToLocal (Point<float> screenPosition)
  672. {
  673. return screenPosition - getBounds (true).getPosition().toFloat();
  674. }
  675. void UIViewComponentPeer::setAlpha (float newAlpha)
  676. {
  677. [view.window setAlpha: (CGFloat) newAlpha];
  678. }
  679. void UIViewComponentPeer::setFullScreen (bool shouldBeFullScreen)
  680. {
  681. if (! isSharedWindow)
  682. {
  683. auto r = shouldBeFullScreen ? Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea
  684. : lastNonFullscreenBounds;
  685. if ((! shouldBeFullScreen) && r.isEmpty())
  686. r = getBounds();
  687. // (can't call the component's setBounds method because that'll reset our fullscreen flag)
  688. if (! r.isEmpty())
  689. setBounds (ScalingHelpers::scaledScreenPosToUnscaled (component, r), shouldBeFullScreen);
  690. component.repaint();
  691. }
  692. }
  693. void UIViewComponentPeer::updateScreenBounds()
  694. {
  695. auto& desktop = Desktop::getInstance();
  696. auto oldArea = component.getBounds();
  697. auto oldDesktop = desktop.getDisplays().getPrimaryDisplay()->userArea;
  698. forceDisplayUpdate();
  699. if (fullScreen)
  700. {
  701. fullScreen = false;
  702. setFullScreen (true);
  703. }
  704. else if (! isSharedWindow)
  705. {
  706. auto newDesktop = desktop.getDisplays().getPrimaryDisplay()->userArea;
  707. if (newDesktop != oldDesktop)
  708. {
  709. // this will re-centre the window, but leave its size unchanged
  710. auto centreRelX = oldArea.getCentreX() / (float) oldDesktop.getWidth();
  711. auto centreRelY = oldArea.getCentreY() / (float) oldDesktop.getHeight();
  712. auto x = ((int) (newDesktop.getWidth() * centreRelX)) - (oldArea.getWidth() / 2);
  713. auto y = ((int) (newDesktop.getHeight() * centreRelY)) - (oldArea.getHeight() / 2);
  714. component.setBounds (oldArea.withPosition (x, y));
  715. }
  716. }
  717. [view setNeedsDisplay];
  718. }
  719. bool UIViewComponentPeer::contains (Point<int> localPos, bool trueIfInAChildWindow) const
  720. {
  721. if (! ScalingHelpers::scaledScreenPosToUnscaled (component, component.getLocalBounds()).contains (localPos))
  722. return false;
  723. UIView* v = [view hitTest: convertToCGPoint (localPos)
  724. withEvent: nil];
  725. if (trueIfInAChildWindow)
  726. return v != nil;
  727. return v == view;
  728. }
  729. bool UIViewComponentPeer::setAlwaysOnTop (bool alwaysOnTop)
  730. {
  731. if (! isSharedWindow)
  732. window.windowLevel = alwaysOnTop ? UIWindowLevelAlert : UIWindowLevelNormal;
  733. return true;
  734. }
  735. void UIViewComponentPeer::toFront (bool makeActiveWindow)
  736. {
  737. if (isSharedWindow)
  738. [[view superview] bringSubviewToFront: view];
  739. if (makeActiveWindow && window != nil && component.isVisible())
  740. [window makeKeyAndVisible];
  741. }
  742. void UIViewComponentPeer::toBehind (ComponentPeer* other)
  743. {
  744. if (auto* otherPeer = dynamic_cast<UIViewComponentPeer*> (other))
  745. {
  746. if (isSharedWindow)
  747. [[view superview] insertSubview: view belowSubview: otherPeer->view];
  748. }
  749. else
  750. {
  751. jassertfalse; // wrong type of window?
  752. }
  753. }
  754. void UIViewComponentPeer::setIcon (const Image& /*newIcon*/)
  755. {
  756. // to do..
  757. }
  758. //==============================================================================
  759. static float getMaximumTouchForce (UITouch* touch) noexcept
  760. {
  761. if ([touch respondsToSelector: @selector (maximumPossibleForce)])
  762. return (float) touch.maximumPossibleForce;
  763. return 0.0f;
  764. }
  765. static float getTouchForce (UITouch* touch) noexcept
  766. {
  767. if ([touch respondsToSelector: @selector (force)])
  768. return (float) touch.force;
  769. return 0.0f;
  770. }
  771. void UIViewComponentPeer::handleTouches (UIEvent* event, MouseEventFlags mouseEventFlags)
  772. {
  773. NSArray* touches = [[event touchesForView: view] allObjects];
  774. for (unsigned int i = 0; i < [touches count]; ++i)
  775. {
  776. UITouch* touch = [touches objectAtIndex: i];
  777. auto maximumForce = getMaximumTouchForce (touch);
  778. if ([touch phase] == UITouchPhaseStationary && maximumForce <= 0)
  779. continue;
  780. auto pos = convertToPointFloat ([touch locationInView: view]);
  781. juce_lastMousePos = pos + getBounds (true).getPosition().toFloat();
  782. auto time = getMouseTime (event);
  783. auto touchIndex = currentTouches.getIndexOfTouch (this, touch);
  784. auto modsToSend = ModifierKeys::currentModifiers;
  785. auto isUp = [] (MouseEventFlags m)
  786. {
  787. return m == MouseEventFlags::up || m == MouseEventFlags::upAndCancel;
  788. };
  789. if (mouseEventFlags == MouseEventFlags::down)
  790. {
  791. if ([touch phase] != UITouchPhaseBegan)
  792. continue;
  793. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier);
  794. modsToSend = ModifierKeys::currentModifiers;
  795. // this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before.
  796. handleMouseEvent (MouseInputSource::InputSourceType::touch, pos, modsToSend.withoutMouseButtons(),
  797. MouseInputSource::defaultPressure, MouseInputSource::defaultOrientation, time, {}, touchIndex);
  798. if (! isValidPeer (this)) // (in case this component was deleted by the event)
  799. return;
  800. }
  801. else if (isUp (mouseEventFlags))
  802. {
  803. if (! ([touch phase] == UITouchPhaseEnded || [touch phase] == UITouchPhaseCancelled))
  804. continue;
  805. modsToSend = modsToSend.withoutMouseButtons();
  806. currentTouches.clearTouch (touchIndex);
  807. if (! currentTouches.areAnyTouchesActive())
  808. mouseEventFlags = MouseEventFlags::upAndCancel;
  809. }
  810. if (mouseEventFlags == MouseEventFlags::upAndCancel)
  811. {
  812. currentTouches.clearTouch (touchIndex);
  813. modsToSend = ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons();
  814. }
  815. // NB: some devices return 0 or 1.0 if pressure is unknown, so we'll clip our value to a believable range:
  816. auto pressure = maximumForce > 0 ? jlimit (0.0001f, 0.9999f, getTouchForce (touch) / maximumForce)
  817. : MouseInputSource::defaultPressure;
  818. handleMouseEvent (MouseInputSource::InputSourceType::touch,
  819. pos, modsToSend, pressure, MouseInputSource::defaultOrientation, time, { }, touchIndex);
  820. if (! isValidPeer (this)) // (in case this component was deleted by the event)
  821. return;
  822. if (isUp (mouseEventFlags))
  823. {
  824. handleMouseEvent (MouseInputSource::InputSourceType::touch, MouseInputSource::offscreenMousePos, modsToSend,
  825. MouseInputSource::defaultPressure, MouseInputSource::defaultOrientation, time, {}, touchIndex);
  826. if (! isValidPeer (this))
  827. return;
  828. }
  829. }
  830. }
  831. #if JUCE_HAS_IOS_POINTER_SUPPORT
  832. void UIViewComponentPeer::onHover (UIHoverGestureRecognizer* gesture)
  833. {
  834. auto pos = convertToPointFloat ([gesture locationInView: view]);
  835. juce_lastMousePos = pos + getBounds (true).getPosition().toFloat();
  836. handleMouseEvent (MouseInputSource::InputSourceType::touch,
  837. pos,
  838. ModifierKeys::currentModifiers,
  839. MouseInputSource::defaultPressure, MouseInputSource::defaultOrientation,
  840. UIViewComponentPeer::getMouseTime ([[NSProcessInfo processInfo] systemUptime]),
  841. {});
  842. }
  843. void UIViewComponentPeer::onScroll (UIPanGestureRecognizer* gesture)
  844. {
  845. const auto offset = [gesture translationInView: view];
  846. const auto scale = 0.5f / 256.0f;
  847. MouseWheelDetails details;
  848. details.deltaX = scale * (float) offset.x;
  849. details.deltaY = scale * (float) offset.y;
  850. details.isReversed = false;
  851. details.isSmooth = true;
  852. details.isInertial = false;
  853. handleMouseWheel (MouseInputSource::InputSourceType::touch,
  854. convertToPointFloat ([gesture locationInView: view]),
  855. UIViewComponentPeer::getMouseTime ([[NSProcessInfo processInfo] systemUptime]),
  856. details);
  857. }
  858. #endif
  859. //==============================================================================
  860. void UIViewComponentPeer::viewFocusGain()
  861. {
  862. if (currentlyFocusedPeer != this)
  863. {
  864. if (ComponentPeer::isValidPeer (currentlyFocusedPeer))
  865. currentlyFocusedPeer->handleFocusLoss();
  866. currentlyFocusedPeer = this;
  867. handleFocusGain();
  868. }
  869. }
  870. void UIViewComponentPeer::viewFocusLoss()
  871. {
  872. if (currentlyFocusedPeer == this)
  873. {
  874. currentlyFocusedPeer = nullptr;
  875. handleFocusLoss();
  876. }
  877. }
  878. bool UIViewComponentPeer::isFocused() const
  879. {
  880. if (isAppex)
  881. return true;
  882. return isSharedWindow ? this == currentlyFocusedPeer
  883. : (window != nil && [window isKeyWindow]);
  884. }
  885. void UIViewComponentPeer::grabFocus()
  886. {
  887. if (window != nil)
  888. {
  889. [window makeKeyWindow];
  890. viewFocusGain();
  891. }
  892. }
  893. void UIViewComponentPeer::textInputRequired (Point<int> pos, TextInputTarget& target)
  894. {
  895. view->hiddenTextView.frame = CGRectMake (pos.x, pos.y, 0, 0);
  896. updateHiddenTextContent (target);
  897. [view->hiddenTextView becomeFirstResponder];
  898. }
  899. void UIViewComponentPeer::dismissPendingTextInput()
  900. {
  901. closeInputMethodContext();
  902. [view->hiddenTextView resignFirstResponder];
  903. }
  904. static UIKeyboardType getUIKeyboardType (TextInputTarget::VirtualKeyboardType type) noexcept
  905. {
  906. switch (type)
  907. {
  908. case TextInputTarget::textKeyboard: return UIKeyboardTypeAlphabet;
  909. case TextInputTarget::numericKeyboard: return UIKeyboardTypeNumbersAndPunctuation;
  910. case TextInputTarget::decimalKeyboard: return UIKeyboardTypeNumbersAndPunctuation;
  911. case TextInputTarget::urlKeyboard: return UIKeyboardTypeURL;
  912. case TextInputTarget::emailAddressKeyboard: return UIKeyboardTypeEmailAddress;
  913. case TextInputTarget::phoneNumberKeyboard: return UIKeyboardTypePhonePad;
  914. default: jassertfalse; break;
  915. }
  916. return UIKeyboardTypeDefault;
  917. }
  918. void UIViewComponentPeer::updateHiddenTextContent (TextInputTarget& target)
  919. {
  920. view->hiddenTextView.keyboardType = getUIKeyboardType (target.getKeyboardType());
  921. view->hiddenTextView.text = juceStringToNS (target.getTextInRange (Range<int> (0, target.getHighlightedRegion().getStart())));
  922. view->hiddenTextView.selectedRange = NSMakeRange ((NSUInteger) target.getHighlightedRegion().getStart(), 0);
  923. }
  924. BOOL UIViewComponentPeer::textViewReplaceCharacters (Range<int> range, const String& text)
  925. {
  926. if (auto* target = findCurrentTextInputTarget())
  927. {
  928. auto currentSelection = target->getHighlightedRegion();
  929. if (range.getLength() == 1 && text.isEmpty()) // (detect backspace)
  930. if (currentSelection.isEmpty())
  931. target->setHighlightedRegion (currentSelection.withStart (currentSelection.getStart() - 1));
  932. WeakReference<Component> deletionChecker (dynamic_cast<Component*> (target));
  933. if (text == "\r" || text == "\n" || text == "\r\n")
  934. handleKeyPress (KeyPress::returnKey, text[0]);
  935. else
  936. target->insertTextAtCaret (text);
  937. if (deletionChecker != nullptr)
  938. updateHiddenTextContent (*target);
  939. }
  940. return NO;
  941. }
  942. //==============================================================================
  943. void UIViewComponentPeer::displayLinkCallback()
  944. {
  945. if (deferredRepaints.isEmpty())
  946. return;
  947. auto dispatchRectangles = [this] ()
  948. {
  949. // We shouldn't need this preprocessor guard, but when running in the simulator
  950. // CAMetalLayer is flagged as requiring iOS 13
  951. #if JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS
  952. if (metalRenderer != nullptr)
  953. {
  954. if (@available (iOS 12, *))
  955. {
  956. return metalRenderer->drawRectangleList ((CAMetalLayer*) view.layer,
  957. (float) view.contentScaleFactor,
  958. view.frame,
  959. component,
  960. [this] (CGContextRef ctx, CGRect r) { drawRectWithContext (ctx, r); },
  961. deferredRepaints);
  962. }
  963. // The creation of metalRenderer should already be guarded with @available (iOS 12, *).
  964. jassertfalse;
  965. return false;
  966. }
  967. #endif
  968. for (const auto& r : deferredRepaints)
  969. [view setNeedsDisplayInRect: convertToCGRect (r)];
  970. return true;
  971. };
  972. if (dispatchRectangles())
  973. deferredRepaints.clear();
  974. }
  975. //==============================================================================
  976. void UIViewComponentPeer::drawRect (CGRect r)
  977. {
  978. if (r.size.width < 1.0f || r.size.height < 1.0f)
  979. return;
  980. drawRectWithContext (UIGraphicsGetCurrentContext(), r);
  981. }
  982. void UIViewComponentPeer::drawRectWithContext (CGContextRef cg, CGRect)
  983. {
  984. if (! component.isOpaque())
  985. CGContextClearRect (cg, CGContextGetClipBoundingBox (cg));
  986. CGContextConcatCTM (cg, CGAffineTransformMake (1, 0, 0, -1, 0, getComponent().getHeight()));
  987. CoreGraphicsContext g (cg, getComponent().getHeight());
  988. insideDrawRect = true;
  989. handlePaint (g);
  990. insideDrawRect = false;
  991. }
  992. bool UIViewComponentPeer::canBecomeKeyWindow()
  993. {
  994. return (getStyleFlags() & juce::ComponentPeer::windowIgnoresKeyPresses) == 0;
  995. }
  996. //==============================================================================
  997. void Desktop::setKioskComponent (Component* kioskModeComp, bool enableOrDisable, bool /*allowMenusAndBars*/)
  998. {
  999. displays->refresh();
  1000. if (auto* peer = kioskModeComp->getPeer())
  1001. {
  1002. if (auto* uiViewPeer = dynamic_cast<UIViewComponentPeer*> (peer))
  1003. [uiViewPeer->controller setNeedsStatusBarAppearanceUpdate];
  1004. peer->setFullScreen (enableOrDisable);
  1005. }
  1006. }
  1007. void Desktop::allowedOrientationsChanged()
  1008. {
  1009. // if the current orientation isn't allowed anymore then switch orientations
  1010. if (! isOrientationEnabled (getCurrentOrientation()))
  1011. {
  1012. auto newOrientation = [this]
  1013. {
  1014. for (auto orientation : { upright, upsideDown, rotatedClockwise, rotatedAntiClockwise })
  1015. if (isOrientationEnabled (orientation))
  1016. return orientation;
  1017. // you need to support at least one orientation
  1018. jassertfalse;
  1019. return upright;
  1020. }();
  1021. NSNumber* value = [NSNumber numberWithInt: (int) Orientations::convertFromJuce (newOrientation)];
  1022. [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
  1023. [value release];
  1024. }
  1025. }
  1026. //==============================================================================
  1027. void UIViewComponentPeer::repaint (const Rectangle<int>& area)
  1028. {
  1029. if (insideDrawRect || ! MessageManager::getInstance()->isThisTheMessageThread())
  1030. {
  1031. (new AsyncRepaintMessage (this, area))->post();
  1032. return;
  1033. }
  1034. deferredRepaints.add (area.toFloat());
  1035. }
  1036. void UIViewComponentPeer::performAnyPendingRepaintsNow()
  1037. {
  1038. }
  1039. ComponentPeer* Component::createNewPeer (int styleFlags, void* windowToAttachTo)
  1040. {
  1041. return new UIViewComponentPeer (*this, styleFlags, (UIView*) windowToAttachTo);
  1042. }
  1043. //==============================================================================
  1044. const int KeyPress::spaceKey = ' ';
  1045. const int KeyPress::returnKey = 0x0d;
  1046. const int KeyPress::escapeKey = 0x1b;
  1047. const int KeyPress::backspaceKey = 0x7f;
  1048. const int KeyPress::leftKey = 0x1000;
  1049. const int KeyPress::rightKey = 0x1001;
  1050. const int KeyPress::upKey = 0x1002;
  1051. const int KeyPress::downKey = 0x1003;
  1052. const int KeyPress::pageUpKey = 0x1004;
  1053. const int KeyPress::pageDownKey = 0x1005;
  1054. const int KeyPress::endKey = 0x1006;
  1055. const int KeyPress::homeKey = 0x1007;
  1056. const int KeyPress::deleteKey = 0x1008;
  1057. const int KeyPress::insertKey = -1;
  1058. const int KeyPress::tabKey = 9;
  1059. const int KeyPress::F1Key = 0x2001;
  1060. const int KeyPress::F2Key = 0x2002;
  1061. const int KeyPress::F3Key = 0x2003;
  1062. const int KeyPress::F4Key = 0x2004;
  1063. const int KeyPress::F5Key = 0x2005;
  1064. const int KeyPress::F6Key = 0x2006;
  1065. const int KeyPress::F7Key = 0x2007;
  1066. const int KeyPress::F8Key = 0x2008;
  1067. const int KeyPress::F9Key = 0x2009;
  1068. const int KeyPress::F10Key = 0x200a;
  1069. const int KeyPress::F11Key = 0x200b;
  1070. const int KeyPress::F12Key = 0x200c;
  1071. const int KeyPress::F13Key = 0x200d;
  1072. const int KeyPress::F14Key = 0x200e;
  1073. const int KeyPress::F15Key = 0x200f;
  1074. const int KeyPress::F16Key = 0x2010;
  1075. const int KeyPress::F17Key = 0x2011;
  1076. const int KeyPress::F18Key = 0x2012;
  1077. const int KeyPress::F19Key = 0x2013;
  1078. const int KeyPress::F20Key = 0x2014;
  1079. const int KeyPress::F21Key = 0x2015;
  1080. const int KeyPress::F22Key = 0x2016;
  1081. const int KeyPress::F23Key = 0x2017;
  1082. const int KeyPress::F24Key = 0x2018;
  1083. const int KeyPress::F25Key = 0x2019;
  1084. const int KeyPress::F26Key = 0x201a;
  1085. const int KeyPress::F27Key = 0x201b;
  1086. const int KeyPress::F28Key = 0x201c;
  1087. const int KeyPress::F29Key = 0x201d;
  1088. const int KeyPress::F30Key = 0x201e;
  1089. const int KeyPress::F31Key = 0x201f;
  1090. const int KeyPress::F32Key = 0x2020;
  1091. const int KeyPress::F33Key = 0x2021;
  1092. const int KeyPress::F34Key = 0x2022;
  1093. const int KeyPress::F35Key = 0x2023;
  1094. const int KeyPress::numberPad0 = 0x30020;
  1095. const int KeyPress::numberPad1 = 0x30021;
  1096. const int KeyPress::numberPad2 = 0x30022;
  1097. const int KeyPress::numberPad3 = 0x30023;
  1098. const int KeyPress::numberPad4 = 0x30024;
  1099. const int KeyPress::numberPad5 = 0x30025;
  1100. const int KeyPress::numberPad6 = 0x30026;
  1101. const int KeyPress::numberPad7 = 0x30027;
  1102. const int KeyPress::numberPad8 = 0x30028;
  1103. const int KeyPress::numberPad9 = 0x30029;
  1104. const int KeyPress::numberPadAdd = 0x3002a;
  1105. const int KeyPress::numberPadSubtract = 0x3002b;
  1106. const int KeyPress::numberPadMultiply = 0x3002c;
  1107. const int KeyPress::numberPadDivide = 0x3002d;
  1108. const int KeyPress::numberPadSeparator = 0x3002e;
  1109. const int KeyPress::numberPadDecimalPoint = 0x3002f;
  1110. const int KeyPress::numberPadEquals = 0x30030;
  1111. const int KeyPress::numberPadDelete = 0x30031;
  1112. const int KeyPress::playKey = 0x30000;
  1113. const int KeyPress::stopKey = 0x30001;
  1114. const int KeyPress::fastForwardKey = 0x30002;
  1115. const int KeyPress::rewindKey = 0x30003;
  1116. } // namespace juce