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.

836 lines
32KB

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