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

juce_ios_UIViewComponentPeer.mm 34KB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. class UIViewComponentPeer;
  18. // The way rotation works changed in iOS8..
  19. static bool isUsingOldRotationMethod() noexcept
  20. {
  21. static bool isPreV8 = ([[[UIDevice currentDevice] systemVersion] compare: @"8.0"
  22. options: NSNumericSearch] == NSOrderedAscending);
  23. return isPreV8;
  24. }
  25. namespace Orientations
  26. {
  27. static Desktop::DisplayOrientation convertToJuce (UIInterfaceOrientation orientation)
  28. {
  29. switch (orientation)
  30. {
  31. case UIInterfaceOrientationPortrait: return Desktop::upright;
  32. case UIInterfaceOrientationPortraitUpsideDown: return Desktop::upsideDown;
  33. case UIInterfaceOrientationLandscapeLeft: return Desktop::rotatedClockwise;
  34. case UIInterfaceOrientationLandscapeRight: return Desktop::rotatedAntiClockwise;
  35. default: jassertfalse; // unknown orientation!
  36. }
  37. return Desktop::upright;
  38. }
  39. static CGAffineTransform getCGTransformFor (const Desktop::DisplayOrientation orientation) noexcept
  40. {
  41. if (isUsingOldRotationMethod())
  42. {
  43. switch (orientation)
  44. {
  45. case Desktop::upsideDown: return CGAffineTransformMake (-1, 0, 0, -1, 0, 0);
  46. case Desktop::rotatedClockwise: return CGAffineTransformMake (0, -1, 1, 0, 0, 0);
  47. case Desktop::rotatedAntiClockwise: return CGAffineTransformMake (0, 1, -1, 0, 0, 0);
  48. default: break;
  49. }
  50. }
  51. return CGAffineTransformIdentity;
  52. }
  53. static NSUInteger getSupportedOrientations()
  54. {
  55. NSUInteger allowed = 0;
  56. Desktop& d = Desktop::getInstance();
  57. if (d.isOrientationEnabled (Desktop::upright)) allowed |= UIInterfaceOrientationMaskPortrait;
  58. if (d.isOrientationEnabled (Desktop::upsideDown)) allowed |= UIInterfaceOrientationMaskPortraitUpsideDown;
  59. if (d.isOrientationEnabled (Desktop::rotatedClockwise)) allowed |= UIInterfaceOrientationMaskLandscapeLeft;
  60. if (d.isOrientationEnabled (Desktop::rotatedAntiClockwise)) allowed |= UIInterfaceOrientationMaskLandscapeRight;
  61. return allowed;
  62. }
  63. }
  64. //==============================================================================
  65. } // (juce namespace)
  66. using namespace juce;
  67. @interface JuceUIView : UIView <UITextViewDelegate>
  68. {
  69. @public
  70. UIViewComponentPeer* owner;
  71. UITextView* hiddenTextView;
  72. }
  73. - (JuceUIView*) initWithOwner: (UIViewComponentPeer*) owner withFrame: (CGRect) frame;
  74. - (void) dealloc;
  75. - (void) drawRect: (CGRect) r;
  76. - (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event;
  77. - (void) touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event;
  78. - (void) touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event;
  79. - (void) touchesCancelled: (NSSet*) touches withEvent: (UIEvent*) event;
  80. - (BOOL) becomeFirstResponder;
  81. - (BOOL) resignFirstResponder;
  82. - (BOOL) canBecomeFirstResponder;
  83. - (BOOL) textView: (UITextView*) textView shouldChangeTextInRange: (NSRange) range replacementText: (NSString*) text;
  84. @end
  85. //==============================================================================
  86. @interface JuceUIViewController : UIViewController
  87. {
  88. }
  89. - (NSUInteger) supportedInterfaceOrientations;
  90. - (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) interfaceOrientation;
  91. - (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) toInterfaceOrientation duration: (NSTimeInterval) duration;
  92. - (void) didRotateFromInterfaceOrientation: (UIInterfaceOrientation) fromInterfaceOrientation;
  93. - (void) viewWillTransitionToSize: (CGSize) size withTransitionCoordinator: (id<UIViewControllerTransitionCoordinator>) coordinator;
  94. - (void) viewDidLoad;
  95. - (void) viewWillAppear: (BOOL) animated;
  96. - (void) viewDidAppear: (BOOL) animated;
  97. - (void) viewWillLayoutSubviews;
  98. - (void) viewDidLayoutSubviews;
  99. @end
  100. //==============================================================================
  101. @interface JuceUIWindow : UIWindow
  102. {
  103. @private
  104. UIViewComponentPeer* owner;
  105. }
  106. - (void) setOwner: (UIViewComponentPeer*) owner;
  107. - (void) becomeKeyWindow;
  108. @end
  109. //==============================================================================
  110. //==============================================================================
  111. namespace juce
  112. {
  113. class UIViewComponentPeer : public ComponentPeer,
  114. public FocusChangeListener
  115. {
  116. public:
  117. UIViewComponentPeer (Component&, int windowStyleFlags, UIView* viewToAttachTo);
  118. ~UIViewComponentPeer();
  119. //==============================================================================
  120. void* getNativeHandle() const override { return view; }
  121. void setVisible (bool shouldBeVisible) override;
  122. void setTitle (const String& title) override;
  123. void setBounds (const Rectangle<int>&, bool isNowFullScreen) override;
  124. Rectangle<int> getBounds() const override { return getBounds (! isSharedWindow); }
  125. Rectangle<int> getBounds (bool global) const;
  126. Point<float> localToGlobal (Point<float> relativePosition) override;
  127. Point<float> globalToLocal (Point<float> screenPosition) override;
  128. void setAlpha (float newAlpha) override;
  129. void setMinimised (bool) override {}
  130. bool isMinimised() const override { return false; }
  131. void setFullScreen (bool shouldBeFullScreen) override;
  132. bool isFullScreen() const override { return fullScreen; }
  133. bool contains (Point<int> localPos, bool trueIfInAChildWindow) const override;
  134. BorderSize<int> getFrameSize() const override { return BorderSize<int>(); }
  135. bool setAlwaysOnTop (bool alwaysOnTop) override;
  136. void toFront (bool makeActiveWindow) override;
  137. void toBehind (ComponentPeer* other) override;
  138. void setIcon (const Image& newIcon) override;
  139. StringArray getAvailableRenderingEngines() override { return StringArray ("CoreGraphics Renderer"); }
  140. void drawRect (CGRect);
  141. bool canBecomeKeyWindow();
  142. //==============================================================================
  143. void viewFocusGain();
  144. void viewFocusLoss();
  145. bool isFocused() const override;
  146. void grabFocus() override;
  147. void textInputRequired (Point<int>, TextInputTarget&) override;
  148. BOOL textViewReplaceCharacters (Range<int>, const String&);
  149. void updateHiddenTextContent (TextInputTarget*);
  150. void globalFocusChanged (Component*) override;
  151. void updateTransformAndScreenBounds();
  152. void handleTouches (UIEvent*, bool isDown, bool isUp, bool isCancel);
  153. //==============================================================================
  154. void repaint (const Rectangle<int>& area) override;
  155. void performAnyPendingRepaintsNow() override;
  156. //==============================================================================
  157. UIWindow* window;
  158. JuceUIView* view;
  159. JuceUIViewController* controller;
  160. bool isSharedWindow, fullScreen, insideDrawRect;
  161. static ModifierKeys currentModifiers;
  162. static int64 getMouseTime (UIEvent* e) noexcept
  163. {
  164. return (Time::currentTimeMillis() - Time::getMillisecondCounter())
  165. + (int64) ([e timestamp] * 1000.0);
  166. }
  167. static Rectangle<int> rotatedScreenPosToReal (const Rectangle<int>& r)
  168. {
  169. if (isUsingOldRotationMethod())
  170. {
  171. const Rectangle<int> screen (convertToRectInt ([UIScreen mainScreen].bounds));
  172. switch ([[UIApplication sharedApplication] statusBarOrientation])
  173. {
  174. case UIInterfaceOrientationPortrait:
  175. return r;
  176. case UIInterfaceOrientationPortraitUpsideDown:
  177. return Rectangle<int> (screen.getWidth() - r.getRight(), screen.getHeight() - r.getBottom(),
  178. r.getWidth(), r.getHeight());
  179. case UIInterfaceOrientationLandscapeLeft:
  180. return Rectangle<int> (r.getY(), screen.getHeight() - r.getRight(),
  181. r.getHeight(), r.getWidth());
  182. case UIInterfaceOrientationLandscapeRight:
  183. return Rectangle<int> (screen.getWidth() - r.getBottom(), r.getX(),
  184. r.getHeight(), r.getWidth());
  185. default: jassertfalse; // unknown orientation!
  186. }
  187. }
  188. return r;
  189. }
  190. static Rectangle<int> realScreenPosToRotated (const Rectangle<int>& r)
  191. {
  192. if (isUsingOldRotationMethod())
  193. {
  194. const Rectangle<int> screen (convertToRectInt ([UIScreen mainScreen].bounds));
  195. switch ([[UIApplication sharedApplication] statusBarOrientation])
  196. {
  197. case UIInterfaceOrientationPortrait:
  198. return r;
  199. case UIInterfaceOrientationPortraitUpsideDown:
  200. return Rectangle<int> (screen.getWidth() - r.getRight(), screen.getHeight() - r.getBottom(),
  201. r.getWidth(), r.getHeight());
  202. case UIInterfaceOrientationLandscapeLeft:
  203. return Rectangle<int> (screen.getHeight() - r.getBottom(), r.getX(),
  204. r.getHeight(), r.getWidth());
  205. case UIInterfaceOrientationLandscapeRight:
  206. return Rectangle<int> (r.getY(), screen.getWidth() - r.getRight(),
  207. r.getHeight(), r.getWidth());
  208. default: jassertfalse; // unknown orientation!
  209. }
  210. }
  211. return r;
  212. }
  213. MultiTouchMapper<UITouch*> currentTouches;
  214. private:
  215. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIViewComponentPeer)
  216. class AsyncRepaintMessage : public CallbackMessage
  217. {
  218. public:
  219. UIViewComponentPeer* const peer;
  220. const Rectangle<int> rect;
  221. AsyncRepaintMessage (UIViewComponentPeer* const p, const Rectangle<int>& r)
  222. : peer (p), rect (r)
  223. {
  224. }
  225. void messageCallback() override
  226. {
  227. if (ComponentPeer::isValidPeer (peer))
  228. peer->repaint (rect);
  229. }
  230. };
  231. };
  232. static void sendScreenBoundsUpdate (JuceUIViewController* c)
  233. {
  234. JuceUIView* juceView = (JuceUIView*) [c view];
  235. jassert (juceView != nil && juceView->owner != nullptr);
  236. juceView->owner->updateTransformAndScreenBounds();
  237. }
  238. } // (juce namespace)
  239. //==============================================================================
  240. //==============================================================================
  241. @implementation JuceUIViewController
  242. - (NSUInteger) supportedInterfaceOrientations
  243. {
  244. return Orientations::getSupportedOrientations();
  245. }
  246. - (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) interfaceOrientation
  247. {
  248. return Desktop::getInstance().isOrientationEnabled (Orientations::convertToJuce (interfaceOrientation));
  249. }
  250. - (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) toInterfaceOrientation
  251. duration: (NSTimeInterval) duration
  252. {
  253. (void) toInterfaceOrientation;
  254. (void) duration;
  255. [UIView setAnimationsEnabled: NO]; // disable this because it goes the wrong way and looks like crap.
  256. }
  257. - (void) didRotateFromInterfaceOrientation: (UIInterfaceOrientation) fromInterfaceOrientation
  258. {
  259. (void) fromInterfaceOrientation;
  260. sendScreenBoundsUpdate (self);
  261. [UIView setAnimationsEnabled: YES];
  262. }
  263. - (void) viewWillTransitionToSize: (CGSize) size withTransitionCoordinator: (id<UIViewControllerTransitionCoordinator>) coordinator
  264. {
  265. [super viewWillTransitionToSize: size withTransitionCoordinator: coordinator];
  266. sendScreenBoundsUpdate (self);
  267. // On some devices the screen-size isn't yet updated at this point, so also trigger another
  268. // async update to double-check..
  269. MessageManager::callAsync ([=]() { sendScreenBoundsUpdate (self); });
  270. }
  271. - (void) viewDidLoad
  272. {
  273. sendScreenBoundsUpdate (self);
  274. }
  275. - (void) viewWillAppear: (BOOL) animated
  276. {
  277. (void) animated;
  278. [self viewDidLoad];
  279. }
  280. - (void) viewDidAppear: (BOOL) animated
  281. {
  282. (void) animated;
  283. [self viewDidLoad];
  284. }
  285. - (void) viewWillLayoutSubviews
  286. {
  287. [self viewDidLoad];
  288. }
  289. - (void) viewDidLayoutSubviews
  290. {
  291. [self viewDidLoad];
  292. }
  293. @end
  294. @implementation JuceUIView
  295. - (JuceUIView*) initWithOwner: (UIViewComponentPeer*) peer
  296. withFrame: (CGRect) frame
  297. {
  298. [super initWithFrame: frame];
  299. owner = peer;
  300. hiddenTextView = [[UITextView alloc] initWithFrame: CGRectZero];
  301. [self addSubview: hiddenTextView];
  302. hiddenTextView.delegate = self;
  303. hiddenTextView.autocapitalizationType = UITextAutocapitalizationTypeNone;
  304. hiddenTextView.autocorrectionType = UITextAutocorrectionTypeNo;
  305. return self;
  306. }
  307. - (void) dealloc
  308. {
  309. [hiddenTextView removeFromSuperview];
  310. [hiddenTextView release];
  311. [super dealloc];
  312. }
  313. //==============================================================================
  314. - (void) drawRect: (CGRect) r
  315. {
  316. if (owner != nullptr)
  317. owner->drawRect (r);
  318. }
  319. //==============================================================================
  320. - (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event
  321. {
  322. (void) touches;
  323. if (owner != nullptr)
  324. owner->handleTouches (event, true, false, false);
  325. }
  326. - (void) touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event
  327. {
  328. (void) touches;
  329. if (owner != nullptr)
  330. owner->handleTouches (event, false, false, false);
  331. }
  332. - (void) touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event
  333. {
  334. (void) touches;
  335. if (owner != nullptr)
  336. owner->handleTouches (event, false, true, false);
  337. }
  338. - (void) touchesCancelled: (NSSet*) touches withEvent: (UIEvent*) event
  339. {
  340. if (owner != nullptr)
  341. owner->handleTouches (event, false, true, true);
  342. [self touchesEnded: touches withEvent: event];
  343. }
  344. //==============================================================================
  345. - (BOOL) becomeFirstResponder
  346. {
  347. if (owner != nullptr)
  348. owner->viewFocusGain();
  349. return true;
  350. }
  351. - (BOOL) resignFirstResponder
  352. {
  353. if (owner != nullptr)
  354. owner->viewFocusLoss();
  355. return true;
  356. }
  357. - (BOOL) canBecomeFirstResponder
  358. {
  359. return owner != nullptr && owner->canBecomeKeyWindow();
  360. }
  361. - (BOOL) textView: (UITextView*) textView shouldChangeTextInRange: (NSRange) range replacementText: (NSString*) text
  362. {
  363. (void) textView;
  364. return owner->textViewReplaceCharacters (Range<int> ((int) range.location, (int) (range.location + range.length)),
  365. nsStringToJuce (text));
  366. }
  367. @end
  368. //==============================================================================
  369. @implementation JuceUIWindow
  370. - (void) setOwner: (UIViewComponentPeer*) peer
  371. {
  372. owner = peer;
  373. }
  374. - (void) becomeKeyWindow
  375. {
  376. [super becomeKeyWindow];
  377. if (owner != nullptr)
  378. owner->grabFocus();
  379. }
  380. @end
  381. //==============================================================================
  382. //==============================================================================
  383. namespace juce
  384. {
  385. bool KeyPress::isKeyCurrentlyDown (int)
  386. {
  387. return false;
  388. }
  389. ModifierKeys UIViewComponentPeer::currentModifiers;
  390. ModifierKeys ModifierKeys::getCurrentModifiersRealtime() noexcept
  391. {
  392. return UIViewComponentPeer::currentModifiers;
  393. }
  394. void ModifierKeys::updateCurrentModifiers() noexcept
  395. {
  396. currentModifiers = UIViewComponentPeer::currentModifiers;
  397. }
  398. Point<float> juce_lastMousePos;
  399. //==============================================================================
  400. UIViewComponentPeer::UIViewComponentPeer (Component& comp, const int windowStyleFlags, UIView* viewToAttachTo)
  401. : ComponentPeer (comp, windowStyleFlags),
  402. window (nil),
  403. view (nil),
  404. controller (nil),
  405. isSharedWindow (viewToAttachTo != nil),
  406. fullScreen (false),
  407. insideDrawRect (false)
  408. {
  409. CGRect r = convertToCGRect (component.getBounds());
  410. view = [[JuceUIView alloc] initWithOwner: this withFrame: r];
  411. view.multipleTouchEnabled = YES;
  412. view.hidden = true;
  413. view.opaque = component.isOpaque();
  414. view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent: 0];
  415. view.transform = CGAffineTransformIdentity;
  416. if (isSharedWindow)
  417. {
  418. window = [viewToAttachTo window];
  419. [viewToAttachTo addSubview: view];
  420. }
  421. else
  422. {
  423. r = convertToCGRect (rotatedScreenPosToReal (component.getBounds()));
  424. r.origin.y = [UIScreen mainScreen].bounds.size.height - (r.origin.y + r.size.height);
  425. window = [[JuceUIWindow alloc] initWithFrame: r];
  426. [((JuceUIWindow*) window) setOwner: this];
  427. controller = [[JuceUIViewController alloc] init];
  428. controller.view = view;
  429. window.rootViewController = controller;
  430. window.hidden = true;
  431. window.autoresizesSubviews = NO;
  432. window.transform = Orientations::getCGTransformFor (Desktop::getInstance().getCurrentOrientation());
  433. window.opaque = component.isOpaque();
  434. window.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent: 0];
  435. if (component.isAlwaysOnTop())
  436. window.windowLevel = UIWindowLevelAlert;
  437. view.frame = CGRectMake (0, 0, r.size.width, r.size.height);
  438. [window addSubview: view];
  439. }
  440. setTitle (component.getName());
  441. setVisible (component.isVisible());
  442. Desktop::getInstance().addFocusChangeListener (this);
  443. }
  444. UIViewComponentPeer::~UIViewComponentPeer()
  445. {
  446. Desktop::getInstance().removeFocusChangeListener (this);
  447. view->owner = nullptr;
  448. [view removeFromSuperview];
  449. [view release];
  450. [controller release];
  451. if (! isSharedWindow)
  452. {
  453. [((JuceUIWindow*) window) setOwner: nil];
  454. [window release];
  455. }
  456. }
  457. //==============================================================================
  458. void UIViewComponentPeer::setVisible (bool shouldBeVisible)
  459. {
  460. if (! isSharedWindow)
  461. window.hidden = ! shouldBeVisible;
  462. view.hidden = ! shouldBeVisible;
  463. }
  464. void UIViewComponentPeer::setTitle (const String&)
  465. {
  466. // xxx is this possible?
  467. }
  468. void UIViewComponentPeer::setBounds (const Rectangle<int>& newBounds, const bool isNowFullScreen)
  469. {
  470. fullScreen = isNowFullScreen;
  471. if (isSharedWindow)
  472. {
  473. CGRect r = convertToCGRect (newBounds);
  474. if (view.frame.size.width != r.size.width || view.frame.size.height != r.size.height)
  475. [view setNeedsDisplay];
  476. view.frame = r;
  477. }
  478. else
  479. {
  480. window.frame = convertToCGRect (rotatedScreenPosToReal (newBounds));
  481. view.frame = CGRectMake (0, 0, (CGFloat) newBounds.getWidth(), (CGFloat) newBounds.getHeight());
  482. handleMovedOrResized();
  483. }
  484. }
  485. Rectangle<int> UIViewComponentPeer::getBounds (const bool global) const
  486. {
  487. CGRect r = view.frame;
  488. if (global && view.window != nil)
  489. {
  490. r = [view convertRect: r toView: view.window];
  491. r = [view.window convertRect: r toWindow: nil];
  492. return realScreenPosToRotated (convertToRectInt (r));
  493. }
  494. return convertToRectInt (r);
  495. }
  496. Point<float> UIViewComponentPeer::localToGlobal (Point<float> relativePosition)
  497. {
  498. return relativePosition + getBounds (true).getPosition().toFloat();
  499. }
  500. Point<float> UIViewComponentPeer::globalToLocal (Point<float> screenPosition)
  501. {
  502. return screenPosition - getBounds (true).getPosition().toFloat();
  503. }
  504. void UIViewComponentPeer::setAlpha (float newAlpha)
  505. {
  506. [view.window setAlpha: (CGFloat) newAlpha];
  507. }
  508. void UIViewComponentPeer::setFullScreen (bool shouldBeFullScreen)
  509. {
  510. if (! isSharedWindow)
  511. {
  512. Rectangle<int> r (shouldBeFullScreen ? Desktop::getInstance().getDisplays().getMainDisplay().userArea
  513. : lastNonFullscreenBounds);
  514. if ((! shouldBeFullScreen) && r.isEmpty())
  515. r = getBounds();
  516. // (can't call the component's setBounds method because that'll reset our fullscreen flag)
  517. if (! r.isEmpty())
  518. setBounds (r, shouldBeFullScreen);
  519. component.repaint();
  520. }
  521. }
  522. void UIViewComponentPeer::updateTransformAndScreenBounds()
  523. {
  524. Desktop& desktop = Desktop::getInstance();
  525. const Rectangle<int> oldArea (component.getBounds());
  526. const Rectangle<int> oldDesktop (desktop.getDisplays().getMainDisplay().userArea);
  527. const_cast<Desktop::Displays&> (desktop.getDisplays()).refresh();
  528. window.transform = Orientations::getCGTransformFor (desktop.getCurrentOrientation());
  529. view.transform = CGAffineTransformIdentity;
  530. if (fullScreen)
  531. {
  532. fullScreen = false;
  533. setFullScreen (true);
  534. }
  535. else if (! isSharedWindow)
  536. {
  537. // this will re-centre the window, but leave its size unchanged
  538. const float centreRelX = oldArea.getCentreX() / (float) oldDesktop.getWidth();
  539. const float centreRelY = oldArea.getCentreY() / (float) oldDesktop.getHeight();
  540. const Rectangle<int> newDesktop (desktop.getDisplays().getMainDisplay().userArea);
  541. const int x = ((int) (newDesktop.getWidth() * centreRelX)) - (oldArea.getWidth() / 2);
  542. const int y = ((int) (newDesktop.getHeight() * centreRelY)) - (oldArea.getHeight() / 2);
  543. setBounds (oldArea.withPosition (x, y), false);
  544. }
  545. [view setNeedsDisplay];
  546. }
  547. bool UIViewComponentPeer::contains (Point<int> localPos, bool trueIfInAChildWindow) const
  548. {
  549. if (! component.getLocalBounds().contains (localPos))
  550. return false;
  551. UIView* v = [view hitTest: convertToCGPoint (localPos)
  552. withEvent: nil];
  553. if (trueIfInAChildWindow)
  554. return v != nil;
  555. return v == view;
  556. }
  557. bool UIViewComponentPeer::setAlwaysOnTop (bool alwaysOnTop)
  558. {
  559. if (! isSharedWindow)
  560. window.windowLevel = alwaysOnTop ? UIWindowLevelAlert : UIWindowLevelNormal;
  561. return true;
  562. }
  563. void UIViewComponentPeer::toFront (bool makeActiveWindow)
  564. {
  565. if (isSharedWindow)
  566. [[view superview] bringSubviewToFront: view];
  567. if (makeActiveWindow && window != nil && component.isVisible())
  568. [window makeKeyAndVisible];
  569. }
  570. void UIViewComponentPeer::toBehind (ComponentPeer* other)
  571. {
  572. if (UIViewComponentPeer* const otherPeer = dynamic_cast<UIViewComponentPeer*> (other))
  573. {
  574. if (isSharedWindow)
  575. {
  576. [[view superview] insertSubview: view belowSubview: otherPeer->view];
  577. }
  578. else
  579. {
  580. // don't know how to do this
  581. }
  582. }
  583. else
  584. {
  585. jassertfalse; // wrong type of window?
  586. }
  587. }
  588. void UIViewComponentPeer::setIcon (const Image& /*newIcon*/)
  589. {
  590. // to do..
  591. }
  592. //==============================================================================
  593. void UIViewComponentPeer::handleTouches (UIEvent* event, const bool isDown, const bool isUp, bool isCancel)
  594. {
  595. NSArray* touches = [[event touchesForView: view] allObjects];
  596. for (unsigned int i = 0; i < [touches count]; ++i)
  597. {
  598. UITouch* touch = [touches objectAtIndex: i];
  599. if ([touch phase] == UITouchPhaseStationary)
  600. continue;
  601. CGPoint p = [touch locationInView: view];
  602. const Point<float> pos (p.x, p.y);
  603. juce_lastMousePos = pos + getBounds (true).getPosition().toFloat();
  604. const int64 time = getMouseTime (event);
  605. const int touchIndex = currentTouches.getIndexOfTouch (touch);
  606. ModifierKeys modsToSend (currentModifiers);
  607. if (isDown)
  608. {
  609. if ([touch phase] != UITouchPhaseBegan)
  610. continue;
  611. currentModifiers = currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier);
  612. modsToSend = currentModifiers;
  613. // this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before.
  614. handleMouseEvent (touchIndex, pos, modsToSend.withoutMouseButtons(), time);
  615. if (! isValidPeer (this)) // (in case this component was deleted by the event)
  616. return;
  617. }
  618. else if (isUp)
  619. {
  620. if (! ([touch phase] == UITouchPhaseEnded || [touch phase] == UITouchPhaseCancelled))
  621. continue;
  622. modsToSend = modsToSend.withoutMouseButtons();
  623. currentTouches.clearTouch (touchIndex);
  624. if (! currentTouches.areAnyTouchesActive())
  625. isCancel = true;
  626. }
  627. if (isCancel)
  628. {
  629. currentTouches.clearTouch (touchIndex);
  630. modsToSend = currentModifiers = currentModifiers.withoutMouseButtons();
  631. }
  632. handleMouseEvent (touchIndex, pos, modsToSend, time);
  633. if (! isValidPeer (this)) // (in case this component was deleted by the event)
  634. return;
  635. if (isUp || isCancel)
  636. {
  637. handleMouseEvent (touchIndex, Point<float> (-1.0f, -1.0f), modsToSend, time);
  638. if (! isValidPeer (this))
  639. return;
  640. }
  641. }
  642. }
  643. //==============================================================================
  644. static UIViewComponentPeer* currentlyFocusedPeer = nullptr;
  645. void UIViewComponentPeer::viewFocusGain()
  646. {
  647. if (currentlyFocusedPeer != this)
  648. {
  649. if (ComponentPeer::isValidPeer (currentlyFocusedPeer))
  650. currentlyFocusedPeer->handleFocusLoss();
  651. currentlyFocusedPeer = this;
  652. handleFocusGain();
  653. }
  654. }
  655. void UIViewComponentPeer::viewFocusLoss()
  656. {
  657. if (currentlyFocusedPeer == this)
  658. {
  659. currentlyFocusedPeer = nullptr;
  660. handleFocusLoss();
  661. }
  662. }
  663. bool UIViewComponentPeer::isFocused() const
  664. {
  665. return isSharedWindow ? this == currentlyFocusedPeer
  666. : (window != nil && [window isKeyWindow]);
  667. }
  668. void UIViewComponentPeer::grabFocus()
  669. {
  670. if (window != nil)
  671. {
  672. [window makeKeyWindow];
  673. viewFocusGain();
  674. }
  675. }
  676. void UIViewComponentPeer::textInputRequired (Point<int>, TextInputTarget&)
  677. {
  678. }
  679. static bool isIOS4_1() noexcept
  680. {
  681. return [[[UIDevice currentDevice] systemVersion] doubleValue] >= 4.1;
  682. }
  683. static UIKeyboardType getUIKeyboardType (TextInputTarget::VirtualKeyboardType type) noexcept
  684. {
  685. switch (type)
  686. {
  687. case TextInputTarget::textKeyboard: return UIKeyboardTypeAlphabet;
  688. case TextInputTarget::numericKeyboard: return isIOS4_1() ? UIKeyboardTypeNumberPad : UIKeyboardTypeNumbersAndPunctuation;
  689. case TextInputTarget::decimalKeyboard: return isIOS4_1() ? UIKeyboardTypeDecimalPad : UIKeyboardTypeNumbersAndPunctuation;
  690. case TextInputTarget::urlKeyboard: return UIKeyboardTypeURL;
  691. case TextInputTarget::emailAddressKeyboard: return UIKeyboardTypeEmailAddress;
  692. case TextInputTarget::phoneNumberKeyboard: return UIKeyboardTypePhonePad;
  693. default: jassertfalse; break;
  694. }
  695. return UIKeyboardTypeDefault;
  696. }
  697. void UIViewComponentPeer::updateHiddenTextContent (TextInputTarget* target)
  698. {
  699. view->hiddenTextView.keyboardType = getUIKeyboardType (target->getKeyboardType());
  700. view->hiddenTextView.text = juceStringToNS (target->getTextInRange (Range<int> (0, target->getHighlightedRegion().getStart())));
  701. view->hiddenTextView.selectedRange = NSMakeRange ((NSUInteger) target->getHighlightedRegion().getStart(), 0);
  702. }
  703. BOOL UIViewComponentPeer::textViewReplaceCharacters (Range<int> range, const String& text)
  704. {
  705. if (TextInputTarget* const target = findCurrentTextInputTarget())
  706. {
  707. const Range<int> currentSelection (target->getHighlightedRegion());
  708. if (range.getLength() == 1 && text.isEmpty()) // (detect backspace)
  709. if (currentSelection.isEmpty())
  710. target->setHighlightedRegion (currentSelection.withStart (currentSelection.getStart() - 1));
  711. if (text == "\r" || text == "\n" || text == "\r\n")
  712. handleKeyPress (KeyPress::returnKey, text[0]);
  713. else
  714. target->insertTextAtCaret (text);
  715. updateHiddenTextContent (target);
  716. }
  717. return NO;
  718. }
  719. void UIViewComponentPeer::globalFocusChanged (Component*)
  720. {
  721. if (TextInputTarget* const target = findCurrentTextInputTarget())
  722. {
  723. Component* comp = dynamic_cast<Component*> (target);
  724. Point<int> pos (component.getLocalPoint (comp, Point<int>()));
  725. view->hiddenTextView.frame = CGRectMake (pos.x, pos.y, 0, 0);
  726. updateHiddenTextContent (target);
  727. [view->hiddenTextView becomeFirstResponder];
  728. }
  729. else
  730. {
  731. [view->hiddenTextView resignFirstResponder];
  732. }
  733. }
  734. //==============================================================================
  735. void UIViewComponentPeer::drawRect (CGRect r)
  736. {
  737. if (r.size.width < 1.0f || r.size.height < 1.0f)
  738. return;
  739. CGContextRef cg = UIGraphicsGetCurrentContext();
  740. if (! component.isOpaque())
  741. CGContextClearRect (cg, CGContextGetClipBoundingBox (cg));
  742. CGContextConcatCTM (cg, CGAffineTransformMake (1, 0, 0, -1, 0, getComponent().getHeight()));
  743. CoreGraphicsContext g (cg, getComponent().getHeight(), [UIScreen mainScreen].scale);
  744. insideDrawRect = true;
  745. handlePaint (g);
  746. insideDrawRect = false;
  747. }
  748. bool UIViewComponentPeer::canBecomeKeyWindow()
  749. {
  750. return (getStyleFlags() & juce::ComponentPeer::windowIgnoresKeyPresses) == 0;
  751. }
  752. //==============================================================================
  753. void Desktop::setKioskComponent (Component* kioskModeComp, bool enableOrDisable, bool /*allowMenusAndBars*/)
  754. {
  755. [[UIApplication sharedApplication] setStatusBarHidden: enableOrDisable
  756. withAnimation: UIStatusBarAnimationSlide];
  757. displays->refresh();
  758. if (ComponentPeer* const peer = kioskModeComp->getPeer())
  759. peer->setFullScreen (enableOrDisable);
  760. }
  761. //==============================================================================
  762. void UIViewComponentPeer::repaint (const Rectangle<int>& area)
  763. {
  764. if (insideDrawRect || ! MessageManager::getInstance()->isThisTheMessageThread())
  765. (new AsyncRepaintMessage (this, area))->post();
  766. else
  767. [view setNeedsDisplayInRect: convertToCGRect (area)];
  768. }
  769. void UIViewComponentPeer::performAnyPendingRepaintsNow()
  770. {
  771. }
  772. ComponentPeer* Component::createNewPeer (int styleFlags, void* windowToAttachTo)
  773. {
  774. return new UIViewComponentPeer (*this, styleFlags, (UIView*) windowToAttachTo);
  775. }
  776. //==============================================================================
  777. const int KeyPress::spaceKey = ' ';
  778. const int KeyPress::returnKey = 0x0d;
  779. const int KeyPress::escapeKey = 0x1b;
  780. const int KeyPress::backspaceKey = 0x7f;
  781. const int KeyPress::leftKey = 0x1000;
  782. const int KeyPress::rightKey = 0x1001;
  783. const int KeyPress::upKey = 0x1002;
  784. const int KeyPress::downKey = 0x1003;
  785. const int KeyPress::pageUpKey = 0x1004;
  786. const int KeyPress::pageDownKey = 0x1005;
  787. const int KeyPress::endKey = 0x1006;
  788. const int KeyPress::homeKey = 0x1007;
  789. const int KeyPress::deleteKey = 0x1008;
  790. const int KeyPress::insertKey = -1;
  791. const int KeyPress::tabKey = 9;
  792. const int KeyPress::F1Key = 0x2001;
  793. const int KeyPress::F2Key = 0x2002;
  794. const int KeyPress::F3Key = 0x2003;
  795. const int KeyPress::F4Key = 0x2004;
  796. const int KeyPress::F5Key = 0x2005;
  797. const int KeyPress::F6Key = 0x2006;
  798. const int KeyPress::F7Key = 0x2007;
  799. const int KeyPress::F8Key = 0x2008;
  800. const int KeyPress::F9Key = 0x2009;
  801. const int KeyPress::F10Key = 0x200a;
  802. const int KeyPress::F11Key = 0x200b;
  803. const int KeyPress::F12Key = 0x200c;
  804. const int KeyPress::F13Key = 0x200d;
  805. const int KeyPress::F14Key = 0x200e;
  806. const int KeyPress::F15Key = 0x200f;
  807. const int KeyPress::F16Key = 0x2010;
  808. const int KeyPress::numberPad0 = 0x30020;
  809. const int KeyPress::numberPad1 = 0x30021;
  810. const int KeyPress::numberPad2 = 0x30022;
  811. const int KeyPress::numberPad3 = 0x30023;
  812. const int KeyPress::numberPad4 = 0x30024;
  813. const int KeyPress::numberPad5 = 0x30025;
  814. const int KeyPress::numberPad6 = 0x30026;
  815. const int KeyPress::numberPad7 = 0x30027;
  816. const int KeyPress::numberPad8 = 0x30028;
  817. const int KeyPress::numberPad9 = 0x30029;
  818. const int KeyPress::numberPadAdd = 0x3002a;
  819. const int KeyPress::numberPadSubtract = 0x3002b;
  820. const int KeyPress::numberPadMultiply = 0x3002c;
  821. const int KeyPress::numberPadDivide = 0x3002d;
  822. const int KeyPress::numberPadSeparator = 0x3002e;
  823. const int KeyPress::numberPadDecimalPoint = 0x3002f;
  824. const int KeyPress::numberPadEquals = 0x30030;
  825. const int KeyPress::numberPadDelete = 0x30031;
  826. const int KeyPress::playKey = 0x30000;
  827. const int KeyPress::stopKey = 0x30001;
  828. const int KeyPress::fastForwardKey = 0x30002;
  829. const int KeyPress::rewindKey = 0x30003;