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.

1394 lines
46KB

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