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

838 lines
32KB

  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. namespace juce
  14. {
  15. extern bool isIOSAppActive;
  16. struct AppInactivityCallback // NB: careful, this declaration is duplicated in other modules
  17. {
  18. virtual ~AppInactivityCallback() = default;
  19. virtual void appBecomingInactive() = 0;
  20. };
  21. // This is an internal list of callbacks (but currently used between modules)
  22. Array<AppInactivityCallback*> appBecomingInactiveCallbacks;
  23. }
  24. #if JUCE_PUSH_NOTIFICATIONS && defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
  25. @interface JuceAppStartupDelegate : NSObject <UIApplicationDelegate, UNUserNotificationCenterDelegate>
  26. #else
  27. @interface JuceAppStartupDelegate : NSObject <UIApplicationDelegate>
  28. #endif
  29. {
  30. UIBackgroundTaskIdentifier appSuspendTask;
  31. }
  32. @property (strong, nonatomic) UIWindow *window;
  33. - (id) init;
  34. - (void) dealloc;
  35. - (void) applicationDidFinishLaunching: (UIApplication*) application;
  36. - (void) applicationWillTerminate: (UIApplication*) application;
  37. - (void) applicationDidEnterBackground: (UIApplication*) application;
  38. - (void) applicationWillEnterForeground: (UIApplication*) application;
  39. - (void) applicationDidBecomeActive: (UIApplication*) application;
  40. - (void) applicationWillResignActive: (UIApplication*) application;
  41. - (void) application: (UIApplication*) application handleEventsForBackgroundURLSession: (NSString*) identifier
  42. completionHandler: (void (^)(void)) completionHandler;
  43. - (void) applicationDidReceiveMemoryWarning: (UIApplication *) application;
  44. #if JUCE_PUSH_NOTIFICATIONS
  45. - (void) application: (UIApplication*) application didRegisterUserNotificationSettings: (UIUserNotificationSettings*) notificationSettings;
  46. - (void) application: (UIApplication*) application didRegisterForRemoteNotificationsWithDeviceToken: (NSData*) deviceToken;
  47. - (void) application: (UIApplication*) application didFailToRegisterForRemoteNotificationsWithError: (NSError*) error;
  48. - (void) application: (UIApplication*) application didReceiveRemoteNotification: (NSDictionary*) userInfo;
  49. - (void) application: (UIApplication*) application didReceiveRemoteNotification: (NSDictionary*) userInfo
  50. fetchCompletionHandler: (void (^)(UIBackgroundFetchResult result)) completionHandler;
  51. - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier
  52. forRemoteNotification: (NSDictionary*) userInfo withResponseInfo: (NSDictionary*) responseInfo
  53. completionHandler: (void(^)()) completionHandler;
  54. - (void) application: (UIApplication*) application didReceiveLocalNotification: (UILocalNotification*) notification;
  55. - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier
  56. forLocalNotification: (UILocalNotification*) notification completionHandler: (void(^)()) completionHandler;
  57. - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier
  58. forLocalNotification: (UILocalNotification*) notification withResponseInfo: (NSDictionary*) responseInfo
  59. completionHandler: (void(^)()) completionHandler;
  60. #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
  61. - (void) userNotificationCenter: (UNUserNotificationCenter*) center willPresentNotification: (UNNotification*) notification
  62. withCompletionHandler: (void (^)(UNNotificationPresentationOptions options)) completionHandler;
  63. - (void) userNotificationCenter: (UNUserNotificationCenter*) center didReceiveNotificationResponse: (UNNotificationResponse*) response
  64. withCompletionHandler: (void(^)())completionHandler;
  65. #endif
  66. #endif
  67. @end
  68. @implementation JuceAppStartupDelegate
  69. NSObject* _pushNotificationsDelegate;
  70. - (id) init
  71. {
  72. self = [super init];
  73. appSuspendTask = UIBackgroundTaskInvalid;
  74. #if JUCE_PUSH_NOTIFICATIONS && defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
  75. [UNUserNotificationCenter currentNotificationCenter].delegate = self;
  76. #endif
  77. return self;
  78. }
  79. - (void) dealloc
  80. {
  81. [super dealloc];
  82. }
  83. - (void) applicationDidFinishLaunching: (UIApplication*) application
  84. {
  85. ignoreUnused (application);
  86. initialiseJuce_GUI();
  87. if (auto* app = JUCEApplicationBase::createInstance())
  88. {
  89. if (! app->initialiseApp())
  90. exit (app->shutdownApp());
  91. }
  92. else
  93. {
  94. jassertfalse; // you must supply an application object for an iOS app!
  95. }
  96. }
  97. - (void) applicationWillTerminate: (UIApplication*) application
  98. {
  99. ignoreUnused (application);
  100. JUCEApplicationBase::appWillTerminateByForce();
  101. }
  102. - (void) applicationDidEnterBackground: (UIApplication*) application
  103. {
  104. if (auto* app = JUCEApplicationBase::getInstance())
  105. {
  106. #if JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK
  107. appSuspendTask = [application beginBackgroundTaskWithName:@"JUCE Suspend Task" expirationHandler:^{
  108. if (appSuspendTask != UIBackgroundTaskInvalid)
  109. {
  110. [application endBackgroundTask:appSuspendTask];
  111. appSuspendTask = UIBackgroundTaskInvalid;
  112. }
  113. }];
  114. MessageManager::callAsync ([app] { app->suspended(); });
  115. #else
  116. ignoreUnused (application);
  117. app->suspended();
  118. #endif
  119. }
  120. }
  121. - (void) applicationWillEnterForeground: (UIApplication*) application
  122. {
  123. ignoreUnused (application);
  124. if (auto* app = JUCEApplicationBase::getInstance())
  125. app->resumed();
  126. }
  127. - (void) applicationDidBecomeActive: (UIApplication*) application
  128. {
  129. application.applicationIconBadgeNumber = 0;
  130. isIOSAppActive = true;
  131. }
  132. - (void) applicationWillResignActive: (UIApplication*) application
  133. {
  134. ignoreUnused (application);
  135. isIOSAppActive = false;
  136. for (int i = appBecomingInactiveCallbacks.size(); --i >= 0;)
  137. appBecomingInactiveCallbacks.getReference(i)->appBecomingInactive();
  138. }
  139. - (void) application: (UIApplication*) application handleEventsForBackgroundURLSession: (NSString*)identifier
  140. completionHandler: (void (^)(void))completionHandler
  141. {
  142. ignoreUnused (application);
  143. URL::DownloadTask::juce_iosURLSessionNotify (nsStringToJuce (identifier));
  144. completionHandler();
  145. }
  146. - (void) applicationDidReceiveMemoryWarning: (UIApplication*) application
  147. {
  148. ignoreUnused (application);
  149. if (auto* app = JUCEApplicationBase::getInstance())
  150. app->memoryWarningReceived();
  151. }
  152. - (void) setPushNotificationsDelegateToUse: (NSObject*) delegate
  153. {
  154. _pushNotificationsDelegate = delegate;
  155. }
  156. #if JUCE_PUSH_NOTIFICATIONS
  157. - (void) application: (UIApplication*) application didRegisterUserNotificationSettings: (UIUserNotificationSettings*) notificationSettings
  158. {
  159. ignoreUnused (application);
  160. SEL selector = @selector (application:didRegisterUserNotificationSettings:);
  161. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  162. {
  163. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  164. [invocation setSelector: selector];
  165. [invocation setTarget: _pushNotificationsDelegate];
  166. [invocation setArgument: &application atIndex:2];
  167. [invocation setArgument: &notificationSettings atIndex:3];
  168. [invocation invoke];
  169. }
  170. }
  171. - (void) application: (UIApplication*) application didRegisterForRemoteNotificationsWithDeviceToken: (NSData*) deviceToken
  172. {
  173. ignoreUnused (application);
  174. SEL selector = @selector (application:didRegisterForRemoteNotificationsWithDeviceToken:);
  175. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  176. {
  177. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  178. [invocation setSelector: selector];
  179. [invocation setTarget: _pushNotificationsDelegate];
  180. [invocation setArgument: &application atIndex:2];
  181. [invocation setArgument: &deviceToken atIndex:3];
  182. [invocation invoke];
  183. }
  184. }
  185. - (void) application: (UIApplication*) application didFailToRegisterForRemoteNotificationsWithError: (NSError*) error
  186. {
  187. ignoreUnused (application);
  188. SEL selector = @selector (application:didFailToRegisterForRemoteNotificationsWithError:);
  189. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  190. {
  191. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  192. [invocation setSelector: selector];
  193. [invocation setTarget: _pushNotificationsDelegate];
  194. [invocation setArgument: &application atIndex:2];
  195. [invocation setArgument: &error atIndex:3];
  196. [invocation invoke];
  197. }
  198. }
  199. - (void) application: (UIApplication*) application didReceiveRemoteNotification: (NSDictionary*) userInfo
  200. {
  201. ignoreUnused (application);
  202. SEL selector = @selector (application:didReceiveRemoteNotification:);
  203. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  204. {
  205. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  206. [invocation setSelector: selector];
  207. [invocation setTarget: _pushNotificationsDelegate];
  208. [invocation setArgument: &application atIndex:2];
  209. [invocation setArgument: &userInfo atIndex:3];
  210. [invocation invoke];
  211. }
  212. }
  213. - (void) application: (UIApplication*) application didReceiveRemoteNotification: (NSDictionary*) userInfo
  214. fetchCompletionHandler: (void (^)(UIBackgroundFetchResult result)) completionHandler
  215. {
  216. ignoreUnused (application);
  217. SEL selector = @selector (application:didReceiveRemoteNotification:fetchCompletionHandler:);
  218. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  219. {
  220. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  221. [invocation setSelector: selector];
  222. [invocation setTarget: _pushNotificationsDelegate];
  223. [invocation setArgument: &application atIndex:2];
  224. [invocation setArgument: &userInfo atIndex:3];
  225. [invocation setArgument: &completionHandler atIndex:4];
  226. [invocation invoke];
  227. }
  228. }
  229. - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier
  230. forRemoteNotification: (NSDictionary*) userInfo withResponseInfo: (NSDictionary*) responseInfo
  231. completionHandler: (void(^)()) completionHandler
  232. {
  233. ignoreUnused (application);
  234. SEL selector = @selector (application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:);
  235. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  236. {
  237. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  238. [invocation setSelector: selector];
  239. [invocation setTarget: _pushNotificationsDelegate];
  240. [invocation setArgument: &application atIndex:2];
  241. [invocation setArgument: &identifier atIndex:3];
  242. [invocation setArgument: &userInfo atIndex:4];
  243. [invocation setArgument: &responseInfo atIndex:5];
  244. [invocation setArgument: &completionHandler atIndex:6];
  245. [invocation invoke];
  246. }
  247. }
  248. - (void) application: (UIApplication*) application didReceiveLocalNotification: (UILocalNotification*) notification
  249. {
  250. ignoreUnused (application);
  251. SEL selector = @selector (application:didReceiveLocalNotification:);
  252. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  253. {
  254. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  255. [invocation setSelector: selector];
  256. [invocation setTarget: _pushNotificationsDelegate];
  257. [invocation setArgument: &application atIndex:2];
  258. [invocation setArgument: &notification atIndex:3];
  259. [invocation invoke];
  260. }
  261. }
  262. - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier
  263. forLocalNotification: (UILocalNotification*) notification completionHandler: (void(^)()) completionHandler
  264. {
  265. ignoreUnused (application);
  266. SEL selector = @selector (application:handleActionWithIdentifier:forLocalNotification:completionHandler:);
  267. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  268. {
  269. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  270. [invocation setSelector: selector];
  271. [invocation setTarget: _pushNotificationsDelegate];
  272. [invocation setArgument: &application atIndex:2];
  273. [invocation setArgument: &identifier atIndex:3];
  274. [invocation setArgument: &notification atIndex:4];
  275. [invocation setArgument: &completionHandler atIndex:5];
  276. [invocation invoke];
  277. }
  278. }
  279. - (void) application: (UIApplication*) application handleActionWithIdentifier: (NSString*) identifier
  280. forLocalNotification: (UILocalNotification*) notification withResponseInfo: (NSDictionary*) responseInfo
  281. completionHandler: (void(^)()) completionHandler
  282. {
  283. ignoreUnused (application);
  284. SEL selector = @selector (application:handleActionWithIdentifier:forLocalNotification:withResponseInfo:completionHandler:);
  285. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  286. {
  287. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  288. [invocation setSelector: selector];
  289. [invocation setTarget: _pushNotificationsDelegate];
  290. [invocation setArgument: &application atIndex:2];
  291. [invocation setArgument: &identifier atIndex:3];
  292. [invocation setArgument: &notification atIndex:4];
  293. [invocation setArgument: &responseInfo atIndex:5];
  294. [invocation setArgument: &completionHandler atIndex:6];
  295. [invocation invoke];
  296. }
  297. }
  298. #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
  299. - (void) userNotificationCenter: (UNUserNotificationCenter*) center willPresentNotification: (UNNotification*) notification
  300. withCompletionHandler: (void (^)(UNNotificationPresentationOptions options)) completionHandler
  301. {
  302. ignoreUnused (center);
  303. SEL selector = @selector (userNotificationCenter:willPresentNotification:withCompletionHandler:);
  304. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  305. {
  306. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  307. [invocation setSelector: selector];
  308. [invocation setTarget: _pushNotificationsDelegate];
  309. [invocation setArgument: &center atIndex:2];
  310. [invocation setArgument: &notification atIndex:3];
  311. [invocation setArgument: &completionHandler atIndex:4];
  312. [invocation invoke];
  313. }
  314. }
  315. - (void) userNotificationCenter: (UNUserNotificationCenter*) center didReceiveNotificationResponse: (UNNotificationResponse*) response
  316. withCompletionHandler: (void(^)()) completionHandler
  317. {
  318. ignoreUnused (center);
  319. SEL selector = @selector (userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:);
  320. if (_pushNotificationsDelegate != nil && [_pushNotificationsDelegate respondsToSelector: selector])
  321. {
  322. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [_pushNotificationsDelegate methodSignatureForSelector: selector]];
  323. [invocation setSelector: selector];
  324. [invocation setTarget: _pushNotificationsDelegate];
  325. [invocation setArgument: &center atIndex:2];
  326. [invocation setArgument: &response atIndex:3];
  327. [invocation setArgument: &completionHandler atIndex:4];
  328. [invocation invoke];
  329. }
  330. }
  331. #endif
  332. #endif
  333. @end
  334. namespace juce
  335. {
  336. int juce_iOSMain (int argc, const char* argv[], void* customDelegatePtr);
  337. int juce_iOSMain (int argc, const char* argv[], void* customDelegatePtr)
  338. {
  339. Class delegateClass = (customDelegatePtr != nullptr ? reinterpret_cast<Class> (customDelegatePtr) : [JuceAppStartupDelegate class]);
  340. return UIApplicationMain (argc, const_cast<char**> (argv), nil, NSStringFromClass (delegateClass));
  341. }
  342. //==============================================================================
  343. void LookAndFeel::playAlertSound()
  344. {
  345. // TODO
  346. }
  347. //==============================================================================
  348. class iOSMessageBox
  349. {
  350. public:
  351. iOSMessageBox (const MessageBoxOptions& opts,
  352. std::unique_ptr<ModalComponentManager::Callback>&& cb,
  353. bool deleteOnCompletion)
  354. : callback (std::move (cb)),
  355. shouldDeleteThis (deleteOnCompletion)
  356. {
  357. if (currentlyFocusedPeer != nullptr)
  358. {
  359. UIAlertController* alert = [UIAlertController alertControllerWithTitle: juceStringToNS (opts.getTitle())
  360. message: juceStringToNS (opts.getMessage())
  361. preferredStyle: UIAlertControllerStyleAlert];
  362. addButton (alert, opts.getButtonText (0));
  363. addButton (alert, opts.getButtonText (1));
  364. addButton (alert, opts.getButtonText (2));
  365. [currentlyFocusedPeer->controller presentViewController: alert
  366. animated: YES
  367. completion: nil];
  368. }
  369. else
  370. {
  371. // Since iOS8, alert windows need to be associated with a window, so you need to
  372. // have at least one window on screen when you use this
  373. jassertfalse;
  374. }
  375. }
  376. int getResult()
  377. {
  378. jassert (callback == nullptr);
  379. JUCE_AUTORELEASEPOOL
  380. {
  381. while (result < 0)
  382. [[NSRunLoop mainRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]];
  383. }
  384. return result;
  385. }
  386. void buttonClicked (int buttonIndex) noexcept
  387. {
  388. result = buttonIndex;
  389. if (callback != nullptr)
  390. callback->modalStateFinished (result);
  391. if (shouldDeleteThis)
  392. delete this;
  393. }
  394. private:
  395. void addButton (UIAlertController* alert, const String& text)
  396. {
  397. if (! text.isEmpty())
  398. {
  399. const auto index = [[alert actions] count];
  400. [alert addAction: [UIAlertAction actionWithTitle: juceStringToNS (text)
  401. style: UIAlertActionStyleDefault
  402. handler: ^(UIAlertAction*) { this->buttonClicked ((int) index); }]];
  403. }
  404. }
  405. int result = -1;
  406. std::unique_ptr<ModalComponentManager::Callback> callback;
  407. const bool shouldDeleteThis;
  408. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSMessageBox)
  409. };
  410. //==============================================================================
  411. static int showDialog (const MessageBoxOptions& options,
  412. ModalComponentManager::Callback* callbackIn,
  413. AlertWindowMappings::MapFn mapFn)
  414. {
  415. #if JUCE_MODAL_LOOPS_PERMITTED
  416. if (callbackIn == nullptr)
  417. {
  418. JUCE_AUTORELEASEPOOL
  419. {
  420. jassert (mapFn != nullptr);
  421. iOSMessageBox messageBox (options, nullptr, false);
  422. return mapFn (messageBox.getResult());
  423. }
  424. }
  425. #endif
  426. const auto showBox = [options, callbackIn, mapFn]
  427. {
  428. new iOSMessageBox (options,
  429. AlertWindowMappings::getWrappedCallback (callbackIn, mapFn),
  430. true);
  431. };
  432. if (MessageManager::getInstance()->isThisTheMessageThread())
  433. showBox();
  434. else
  435. MessageManager::callAsync (showBox);
  436. return 0;
  437. }
  438. #if JUCE_MODAL_LOOPS_PERMITTED
  439. void JUCE_CALLTYPE NativeMessageBox::showMessageBox (MessageBoxIconType /*iconType*/,
  440. const String& title, const String& message,
  441. Component* /*associatedComponent*/)
  442. {
  443. showDialog (MessageBoxOptions()
  444. .withTitle (title)
  445. .withMessage (message)
  446. .withButton (TRANS("OK")),
  447. nullptr, AlertWindowMappings::messageBox);
  448. }
  449. int JUCE_CALLTYPE NativeMessageBox::show (const MessageBoxOptions& options)
  450. {
  451. return showDialog (options, nullptr, AlertWindowMappings::noMapping);
  452. }
  453. #endif
  454. void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (MessageBoxIconType /*iconType*/,
  455. const String& title, const String& message,
  456. Component* /*associatedComponent*/,
  457. ModalComponentManager::Callback* callback)
  458. {
  459. showDialog (MessageBoxOptions()
  460. .withTitle (title)
  461. .withMessage (message)
  462. .withButton (TRANS("OK")),
  463. callback, AlertWindowMappings::messageBox);
  464. }
  465. bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType /*iconType*/,
  466. const String& title, const String& message,
  467. Component* /*associatedComponent*/,
  468. ModalComponentManager::Callback* callback)
  469. {
  470. return showDialog (MessageBoxOptions()
  471. .withTitle (title)
  472. .withMessage (message)
  473. .withButton (TRANS("OK"))
  474. .withButton (TRANS("Cancel")),
  475. callback, AlertWindowMappings::okCancel) != 0;
  476. }
  477. int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType /*iconType*/,
  478. const String& title, const String& message,
  479. Component* /*associatedComponent*/,
  480. ModalComponentManager::Callback* callback)
  481. {
  482. return showDialog (MessageBoxOptions()
  483. .withTitle (title)
  484. .withMessage (message)
  485. .withButton (TRANS("Yes"))
  486. .withButton (TRANS("No"))
  487. .withButton (TRANS("Cancel")),
  488. callback, AlertWindowMappings::yesNoCancel);
  489. }
  490. int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType /*iconType*/,
  491. const String& title, const String& message,
  492. Component* /*associatedComponent*/,
  493. ModalComponentManager::Callback* callback)
  494. {
  495. return showDialog (MessageBoxOptions()
  496. .withTitle (title)
  497. .withMessage (message)
  498. .withButton (TRANS("Yes"))
  499. .withButton (TRANS("No")),
  500. callback, AlertWindowMappings::okCancel);
  501. }
  502. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  503. ModalComponentManager::Callback* callback)
  504. {
  505. showDialog (options, callback, AlertWindowMappings::noMapping);
  506. }
  507. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  508. std::function<void (int)> callback)
  509. {
  510. showAsync (options, ModalCallbackFunction::create (callback));
  511. }
  512. //==============================================================================
  513. bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray&, bool, Component*, std::function<void()>)
  514. {
  515. jassertfalse; // no such thing on iOS!
  516. return false;
  517. }
  518. bool DragAndDropContainer::performExternalDragDropOfText (const String&, Component*, std::function<void()>)
  519. {
  520. jassertfalse; // no such thing on iOS!
  521. return false;
  522. }
  523. //==============================================================================
  524. void Desktop::setScreenSaverEnabled (const bool isEnabled)
  525. {
  526. if (! SystemStats::isRunningInAppExtensionSandbox())
  527. [[UIApplication sharedApplication] setIdleTimerDisabled: ! isEnabled];
  528. }
  529. bool Desktop::isScreenSaverEnabled()
  530. {
  531. if (SystemStats::isRunningInAppExtensionSandbox())
  532. return true;
  533. return ! [[UIApplication sharedApplication] isIdleTimerDisabled];
  534. }
  535. //==============================================================================
  536. bool juce_areThereAnyAlwaysOnTopWindows()
  537. {
  538. return false;
  539. }
  540. //==============================================================================
  541. Image juce_createIconForFile (const File&)
  542. {
  543. return Image();
  544. }
  545. //==============================================================================
  546. void SystemClipboard::copyTextToClipboard (const String& text)
  547. {
  548. [[UIPasteboard generalPasteboard] setValue: juceStringToNS (text)
  549. forPasteboardType: @"public.text"];
  550. }
  551. String SystemClipboard::getTextFromClipboard()
  552. {
  553. return nsStringToJuce ([[UIPasteboard generalPasteboard] string]);
  554. }
  555. //==============================================================================
  556. bool MouseInputSource::SourceList::addSource()
  557. {
  558. addSource (sources.size(), MouseInputSource::InputSourceType::touch);
  559. return true;
  560. }
  561. bool MouseInputSource::SourceList::canUseTouch()
  562. {
  563. return true;
  564. }
  565. bool Desktop::canUseSemiTransparentWindows() noexcept
  566. {
  567. return true;
  568. }
  569. bool Desktop::isDarkModeActive() const
  570. {
  571. #if defined (__IPHONE_12_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_0
  572. if (@available (iOS 12.0, *))
  573. return [[[UIScreen mainScreen] traitCollection] userInterfaceStyle] == UIUserInterfaceStyleDark;
  574. #endif
  575. return false;
  576. }
  577. class Desktop::NativeDarkModeChangeDetectorImpl
  578. {
  579. public:
  580. NativeDarkModeChangeDetectorImpl()
  581. {
  582. static DelegateClass delegateClass;
  583. delegate = [delegateClass.createInstance() init];
  584. object_setInstanceVariable (delegate, "owner", this);
  585. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  586. [[NSNotificationCenter defaultCenter] addObserver: delegate
  587. selector: @selector (darkModeChanged:)
  588. name: UIViewComponentPeer::getDarkModeNotificationName()
  589. object: nil];
  590. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  591. }
  592. ~NativeDarkModeChangeDetectorImpl()
  593. {
  594. object_setInstanceVariable (delegate, "owner", nullptr);
  595. [[NSNotificationCenter defaultCenter] removeObserver: delegate];
  596. [delegate release];
  597. }
  598. void darkModeChanged()
  599. {
  600. Desktop::getInstance().darkModeChanged();
  601. }
  602. private:
  603. struct DelegateClass : public ObjCClass<NSObject>
  604. {
  605. DelegateClass() : ObjCClass<NSObject> ("JUCEDelegate_")
  606. {
  607. addIvar<NativeDarkModeChangeDetectorImpl*> ("owner");
  608. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  609. addMethod (@selector (darkModeChanged:), darkModeChanged);
  610. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  611. registerClass();
  612. }
  613. static void darkModeChanged (id self, SEL, NSNotification*)
  614. {
  615. if (auto* owner = getIvar<NativeDarkModeChangeDetectorImpl*> (self, "owner"))
  616. owner->darkModeChanged();
  617. }
  618. };
  619. id delegate = nil;
  620. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl)
  621. };
  622. std::unique_ptr<Desktop::NativeDarkModeChangeDetectorImpl> Desktop::createNativeDarkModeChangeDetectorImpl()
  623. {
  624. return std::make_unique<NativeDarkModeChangeDetectorImpl>();
  625. }
  626. Point<float> MouseInputSource::getCurrentRawMousePosition()
  627. {
  628. return juce_lastMousePos;
  629. }
  630. void MouseInputSource::setRawMousePosition (Point<float>)
  631. {
  632. }
  633. double Desktop::getDefaultMasterScale()
  634. {
  635. return 1.0;
  636. }
  637. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
  638. {
  639. UIInterfaceOrientation orientation = SystemStats::isRunningInAppExtensionSandbox() ? UIInterfaceOrientationPortrait
  640. : getWindowOrientation();
  641. return Orientations::convertToJuce (orientation);
  642. }
  643. // The most straightforward way of retrieving the screen area available to an iOS app
  644. // seems to be to create a new window (which will take up all available space) and to
  645. // query its frame.
  646. struct TemporaryWindow
  647. {
  648. UIWindow* window = [[UIWindow alloc] init];
  649. ~TemporaryWindow() noexcept { [window release]; }
  650. };
  651. static Rectangle<int> getRecommendedWindowBounds()
  652. {
  653. return convertToRectInt (TemporaryWindow().window.frame);
  654. }
  655. static BorderSize<int> getSafeAreaInsets (float masterScale)
  656. {
  657. #if defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0
  658. if (@available (iOS 11.0, *))
  659. {
  660. UIEdgeInsets safeInsets = TemporaryWindow().window.safeAreaInsets;
  661. auto getInset = [&] (CGFloat original) { return roundToInt (original / masterScale); };
  662. return { getInset (safeInsets.top), getInset (safeInsets.left),
  663. getInset (safeInsets.bottom), getInset (safeInsets.right) };
  664. }
  665. #endif
  666. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
  667. auto statusBarSize = [UIApplication sharedApplication].statusBarFrame.size;
  668. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  669. auto statusBarHeight = jmin (statusBarSize.width, statusBarSize.height);
  670. return { roundToInt (statusBarHeight / masterScale), 0, 0, 0 };
  671. }
  672. void Displays::findDisplays (float masterScale)
  673. {
  674. JUCE_AUTORELEASEPOOL
  675. {
  676. UIScreen* s = [UIScreen mainScreen];
  677. Display d;
  678. d.totalArea = convertToRectInt ([s bounds]) / masterScale;
  679. d.userArea = getRecommendedWindowBounds() / masterScale;
  680. d.safeAreaInsets = getSafeAreaInsets (masterScale);
  681. d.isMain = true;
  682. d.scale = masterScale * s.scale;
  683. d.dpi = 160 * d.scale;
  684. displays.add (d);
  685. }
  686. }
  687. } // namespace juce