iPlug2 - C++ Audio Plug-in Framework
IGraphicsIOS_view.mm
1 /*
2  ==============================================================================
3 
4  This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5 
6  See LICENSE.txt for more info.
7 
8  ==============================================================================
9 */
10 
11 #if !__has_feature(objc_arc)
12 #error This file must be compiled with Arc. Use -fobjc-arc flag
13 #endif
14 
15 #import <QuartzCore/QuartzCore.h>
16 #import <Metal/Metal.h>
17 #ifdef IGRAPHICS_IMGUI
18 #include "imgui.h"
19 #import "imgui_impl_metal.h"
20 #endif
21 
22 #import "IGraphicsIOS_view.h"
23 
24 #include "IGraphicsCoreText.h"
25 #include "IControl.h"
26 #include "IPlugParameter.h"
27 
28 extern StaticStorage<CoreTextFontDescriptor> sFontDescriptorCache;
29 
30 @implementation IGRAPHICS_UITABLEVC
31 
32 - (void) viewDidLoad
33 {
34  [super viewDidLoad];
35  self.tableView = [[UITableView alloc] initWithFrame:self.view.frame];
36  self.tableView.dataSource = self;
37  self.tableView.delegate = self;
38  self.tableView.scrollEnabled = YES;
39  self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
40  self.items = [[NSMutableArray alloc] init];
41 
42  int numItems = mMenu->NItems();
43 
44  NSMutableString* elementTitle;
45 
46  for (int i = 0; i < numItems; ++i)
47  {
48  IPopupMenu::Item* pMenuItem = mMenu->GetItem(i);
49 
50  elementTitle = [[NSMutableString alloc] initWithCString:pMenuItem->GetText() encoding:NSUTF8StringEncoding];
51 
52  if (mMenu->GetPrefix())
53  {
54  NSString* prefixString = nil;
55 
56  switch (mMenu->GetPrefix())
57  {
58  case 1: prefixString = [NSString stringWithFormat:@"%1d: ", i+1]; break;
59  case 2: prefixString = [NSString stringWithFormat:@"%02d: ", i+1]; break;
60  case 3: prefixString = [NSString stringWithFormat:@"%03d: ", i+1]; break;
61  case 0:
62  default:
63  prefixString = [NSString stringWithUTF8String:""]; break;
64  }
65 
66  [elementTitle insertString:prefixString atIndex:0];
67  }
68 
69  [self.items addObject:elementTitle];
70  }
71 
72  [self.view addSubview:self.tableView];
73 }
74 
75 - (id) initWithIPopupMenuAndIGraphics:(IPopupMenu*) pMenu :(IGraphicsIOS*) pGraphics
76 {
77  self = [super init];
78 
79  mGraphics = pGraphics;
80  mMenu = pMenu;
81 
82  return self;
83 }
84 
85 - (NSInteger) tableView:(UITableView*) tableView numberOfRowsInSection:(NSInteger) section
86 {
87  return self.items.count;
88 }
89 
90 - (NSInteger) numberOfSectionsInTableView:(UITableView*) tableView
91 {
92  return 1;
93 }
94 
95 - (UITableViewCell*) tableView:(UITableView*) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath
96 {
97  static NSString *identifer = @"cell";
98  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifer];
99 
100  if (cell == nil)
101  {
102  cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifer];
103  }
104 
105  int cellIndex = static_cast<int>(indexPath.row);
106 
107  cell.textLabel.text = [NSString stringWithFormat:@"%@", self.items[indexPath.row]];
108 
109  IPopupMenu::Item* pItem = mMenu->GetItem(cellIndex);
110 
111  if(pItem->GetChecked())
112  cell.accessoryType = UITableViewCellAccessoryCheckmark;
113  else
114  cell.accessoryType = pItem->GetSubmenu() ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
115 
116  if(!pItem->GetEnabled())
117  {
118  cell.userInteractionEnabled = NO;
119  cell.textLabel.enabled = NO;
120  }
121 
122  return cell;
123 }
124 
125 - (CGFloat) tableView:(UITableView*) tableView heightForRowAtIndexPath:(NSIndexPath*) indexPath
126 {
127  int cellIndex = static_cast<int>(indexPath.row);
128 
129  IPopupMenu::Item* pItem = mMenu->GetItem(cellIndex);
130 
131  if(pItem->GetIsSeparator())
132  return 0.5f;
133  else
134  return self.tableView.rowHeight;
135 }
136 
137 - (void) tableView:(UITableView*) tableView didSelectRowAtIndexPath:(NSIndexPath*) indexPath
138 {
139  int cellIndex = static_cast<int>(indexPath.row);
140 
141  IPopupMenu::Item* pItem = mMenu->GetItem(cellIndex);
142  IPopupMenu* pSubMenu = pItem->GetSubmenu();
143 
144  if(pSubMenu)
145  {
146  IGRAPHICS_UITABLEVC* newViewController = [[IGRAPHICS_UITABLEVC alloc] initWithIPopupMenuAndIGraphics: pSubMenu : mGraphics];
147  [newViewController setTitle:[NSString stringWithUTF8String:CStringHasContents(pSubMenu->GetRootTitle()) ? pSubMenu->GetRootTitle() : pItem->GetText()]];
148  [self.navigationController pushViewController:newViewController animated:YES];
149 
150  return;
151  }
152 
153  if(pItem->GetIsChoosable())
154  {
155  mMenu->SetChosenItemIdx(cellIndex);
156 
157  if(mMenu->GetFunction())
158  mMenu->ExecFunction();
159 
160  mGraphics->SetControlValueAfterPopupMenu(mMenu);
161 
162  [self dismissViewControllerAnimated:YES completion:nil];
163  }
164 }
165 
166 - (CGSize) preferredContentSize
167 {
168  if (self.presentingViewController && self.tableView != nil)
169  {
170  CGSize tempSize = self.presentingViewController.view.bounds.size;
171  tempSize.width = 300;
172  CGSize size = [self.tableView sizeThatFits:tempSize];
173  return size;
174  } else {
175  return [super preferredContentSize];
176  }
177 }
178 
179 - (void)setPreferredContentSize:(CGSize)preferredContentSize
180 {
181  super.preferredContentSize = preferredContentSize;
182 }
183 
184 @end
185 
186 @implementation IGRAPHICS_VIEW
187 
188 - (id) initWithIGraphics: (IGraphicsIOS*) pGraphics
189 {
190  TRACE
191 
192  mGraphics = pGraphics;
193  CGRect r = CGRectMake(0.f, 0.f, (float) pGraphics->WindowWidth(), (float) pGraphics->WindowHeight());
194  self = [super initWithFrame:r];
195 
196  //scrollview
197  [self setContentSize:r.size];
198  self.delegate = self;
199  self.scrollEnabled = NO;
200 
201 #ifdef IGRAPHICS_METAL
202  mMTLLayer = [[CAMetalLayer alloc] init];
203  mMTLLayer.device = MTLCreateSystemDefaultDevice();
204  mMTLLayer.framebufferOnly = YES;
205  mMTLLayer.frame = self.layer.frame;
206  mMTLLayer.opaque = YES;
207  mMTLLayer.contentsScale = [UIScreen mainScreen].scale;
208 
209  [self.layer addSublayer: mMTLLayer];
210 #endif
211 
212  self.multipleTouchEnabled = NO;
213 
214  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
215  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];
216  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackgroundNotification:) name:UIApplicationDidEnterBackgroundNotification object:nil];
217  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForegroundNotification:) name:UIApplicationWillEnterForegroundNotification object:nil];
218  mColorPickerHandlerFunc = nullptr;
219 
220  return self;
221 }
222 
223 - (void) setFrame:(CGRect) frame
224 {
225  [super setFrame:frame];
226 
227  // During the first layout pass, we will not be in a view hierarchy, so we guess our scale
228  CGFloat scale = [UIScreen mainScreen].scale;
229 
230  // If we've moved to a window by the time our frame is being set, we can take its scale as our own
231  if (self.window)
232  scale = self.window.screen.scale;
233 
234  #ifdef IGRAPHICS_METAL
235  [CATransaction begin];
236  [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
237  CGSize drawableSize = self.bounds.size;
238  [self.layer setFrame:frame];
239  mMTLLayer.frame = self.layer.frame;
240 
241  drawableSize.width *= scale;
242  drawableSize.height *= scale;
243 
244  mMTLLayer.drawableSize = drawableSize;
245 
246  [CATransaction commit];
247  #endif
248 }
249 
250 - (void) onTouchEvent:(ETouchEvent) eventType withTouches:(NSSet*) touches withEvent:(UIEvent*) event
251 {
252  if(mGraphics == nullptr) //TODO: why?
253  return;
254 
255  NSEnumerator* pEnumerator = [[event allTouches] objectEnumerator];
256  UITouch* pTouch;
257 
258  std::vector<IMouseInfo> points;
259 
260  while ((pTouch = [pEnumerator nextObject]))
261  {
262  CGPoint pos = [pTouch locationInView:pTouch.view];
263 
264  IMouseInfo point;
265 
266  auto ds = mGraphics->GetDrawScale();
267 
268  point.ms.L = true;
269  point.ms.touchID = reinterpret_cast<ITouchID>(pTouch);
270  point.ms.touchRadius = [pTouch majorRadius];
271 
272  point.x = pos.x / ds;
273  point.y = pos.y / ds;
274  CGPoint posPrev = [pTouch previousLocationInView: self];
275  point.dX = (pos.x - posPrev.x) / ds;
276  point.dY = (pos.y - posPrev.y) / ds;
277 
278  if([touches containsObject:pTouch])
279  {
280  mPrevX = point.x;
281  mPrevY = point.y;
282  points.push_back(point);
283  }
284  }
285 
286 // DBGMSG("%lu\n", points[0].ms.idx);
287 
288  if(eventType == ETouchEvent::Began)
289  mGraphics->OnMouseDown(points);
290 
291  if(eventType == ETouchEvent::Moved)
292  mGraphics->OnMouseDrag(points);
293 
294  if(eventType == ETouchEvent::Ended)
295  mGraphics->OnMouseUp(points);
296 
297  if(eventType == ETouchEvent::Cancelled)
298  mGraphics->OnTouchCancelled(points);
299 }
300 
301 - (void) touchesBegan:(NSSet*) touches withEvent:(UIEvent*) event
302 {
303  [self onTouchEvent:ETouchEvent::Began withTouches:touches withEvent:event];
304 }
305 
306 - (void) touchesMoved:(NSSet*) touches withEvent:(UIEvent*) event
307 {
308  [self onTouchEvent:ETouchEvent::Moved withTouches:touches withEvent:event];
309 }
310 
311 - (void) touchesEnded:(NSSet*) touches withEvent:(UIEvent*) event
312 {
313  [self onTouchEvent:ETouchEvent::Ended withTouches:touches withEvent:event];
314 }
315 
316 - (void) touchesCancelled:(NSSet*) touches withEvent:(UIEvent*) event
317 {
318  [self onTouchEvent:ETouchEvent::Cancelled withTouches:touches withEvent:event];
319 }
320 
321 - (CAMetalLayer*) metalLayer
322 {
323  return mMTLLayer;
324 }
325 
326 - (void) didMoveToSuperview
327 {
328  [super didMoveToSuperview];
329  if (self.superview)
330  {
331  self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(redraw:)];
332  [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
333  self.displayLink.preferredFramesPerSecond = mGraphics->FPS();
334  }
335  else
336  {
337  [self.displayLink invalidate];
338  self.displayLink = nil;
339  }
340 }
341 
342 - (void) drawRect:(CGRect)rect
343 {
344  IRECTList rects;
345 
346  if(mGraphics)
347  {
348  mGraphics->SetPlatformContext(UIGraphicsGetCurrentContext());
349 
350  if (mGraphics->IsDirty(rects))
351  {
352  mGraphics->SetAllControlsClean();
353  mGraphics->Draw(rects);
354  }
355  }
356 }
357 
358 - (void) redraw:(CADisplayLink*) displayLink
359 {
360 #ifdef IGRAPHICS_CPU
361  [self setNeedsDisplay];
362 #else
363  [self drawRect:CGRect()];
364 #endif
365 }
366 
367 - (BOOL) isOpaque
368 {
369  return YES;
370 }
371 
372 - (BOOL) acceptsFirstResponder
373 {
374  return YES;
375 }
376 
377 - (BOOL) canBecomeFirstResponder
378 {
379  return YES;
380 }
381 
382 - (void) removeFromSuperview
383 {
384  [self.displayLink invalidate];
385  self.displayLink = nil;
386  mTextField = nil;
387  mGraphics = nil;
388  mMenuTableController = nil;
389  mMenuNavigationController = nil;
390  [mMTLLayer removeFromSuperlayer];
391  mMTLLayer = nil;
392 }
393 
394 - (void) textFieldDidEndEditing:(UITextField*) textField reason:(UITextFieldDidEndEditingReason) reason
395 {
396  if(textField == mTextField)
397  {
398  mGraphics->SetControlValueAfterTextEdit([[mTextField text] UTF8String]);
399  mGraphics->SetAllControlsDirty();
400 
401  [self endUserInput];
402  }
403 }
404 
405 - (BOOL) textFieldShouldReturn:(UITextField*) textField
406 {
407  if(textField == mTextField)
408  {
409  mGraphics->SetControlValueAfterTextEdit([[mTextField text] UTF8String]);
410  mGraphics->SetAllControlsDirty();
411 
412  [self endUserInput];
413  }
414  return YES;
415 }
416 
417 - (void) textFieldDidEndEditing:(UITextField*) textField
418 {
419  [self endUserInput];
420 }
421 
422 - (BOOL) textField:(UITextField*) textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*) string
423 {
424  if (!string.length)
425  return YES;
426 
427  // verify max length has not been exceeded
428  NSString* proposedText = [mTextField.text stringByReplacingCharactersInRange:range withString:string];
429 
430  if (proposedText.length > mTextFieldLength)
431  return NO;
432 
433  IControl* pInTextEntry = mGraphics->GetControlInTextEntry();
434 
435  if(pInTextEntry)
436  {
437  const IParam* pParam = pInTextEntry->GetParam();
438 
439  if (pParam)
440  {
441  NSMutableCharacterSet *characterSet = [[NSMutableCharacterSet alloc] init];
442 
443  switch ( pParam->Type() )
444  {
445  case IParam::kTypeEnum:
446  case IParam::kTypeInt:
447  case IParam::kTypeBool:
448  [characterSet addCharactersInString:@"0123456789-+"];
449  break;
450  case IParam::kTypeDouble:
451  [characterSet addCharactersInString:@"0123456789.-+"];
452  break;
453  default:
454  break;
455  }
456 
457  if ([string rangeOfCharacterFromSet:characterSet.invertedSet].location != NSNotFound)
458  return NO;
459  }
460  }
461 
462  return YES;
463 }
464 
465 - (UIModalPresentationStyle) adaptivePresentationStyleForPresentationController:(UIPresentationController*) controller
466 {
467  return UIModalPresentationNone;
468 }
469 
470 - (BOOL) presentationControllerShouldDismiss:(UIPopoverPresentationController*) popoverPresentationController
471 {
472  return YES;
473 }
474 
475 - (IPopupMenu*) createPopupMenu: (IPopupMenu&) menu : (CGRect) bounds;
476 {
477  mMenuTableController = [[IGRAPHICS_UITABLEVC alloc] initWithIPopupMenuAndIGraphics:&menu : mGraphics];
478  [mMenuTableController setTitle: [NSString stringWithUTF8String:menu.GetRootTitle()]];
479 
480  mMenuNavigationController = [[UINavigationController alloc] initWithRootViewController:mMenuTableController];
481 
482  mMenuNavigationController.modalPresentationStyle = UIModalPresentationPopover;
483  mMenuNavigationController.popoverPresentationController.sourceView = self;
484  mMenuNavigationController.popoverPresentationController.sourceRect = bounds;
485 // mMenuNavigationController.popoverPresentationController.permittedArrowDirections = UIPopoverArrowDirectionUp;
486  mMenuNavigationController.popoverPresentationController.delegate = self;
487 
488  [self.window.rootViewController presentViewController:mMenuNavigationController animated:YES completion:nil];
489 
490  return nullptr;
491 }
492 
493 - (void) createTextEntry: (int) paramIdx : (const IText&) text : (const char*) str : (int) length : (CGRect) areaRect
494 {
495  if (mTextField)
496  return;
497 
498  mTextField = [[UITextField alloc] initWithFrame:areaRect];
499  mTextFieldLength = length;
500 
501  CoreTextFontDescriptor* CTFontDescriptor = CoreTextHelpers::GetCTFontDescriptor(text, sFontDescriptorCache);
502  UIFontDescriptor* fontDescriptor = (__bridge UIFontDescriptor*) CTFontDescriptor->GetDescriptor();
503  UIFont* font = [UIFont fontWithDescriptor: fontDescriptor size: text.mSize * 0.75];
504  [mTextField setFont: font];
505 
506  [mTextField setText:[NSString stringWithUTF8String:str]];
507  [mTextField setTextColor:ToUIColor(text.mTextEntryFGColor)];
508  [mTextField setBackgroundColor:ToUIColor(text.mTextEntryBGColor)];
509  [mTextField setAutocorrectionType:UITextAutocorrectionTypeNo];
510  [mTextField setDelegate:self];
511 
512  switch (text.mVAlign)
513  {
514  case EVAlign::Top:
515  [mTextField setContentVerticalAlignment:UIControlContentVerticalAlignmentTop];
516  break;
517  case EVAlign::Middle:
518  [mTextField setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter];
519  break;
520  case EVAlign::Bottom:
521  [mTextField setContentVerticalAlignment:UIControlContentVerticalAlignmentBottom];
522  break;
523  default:
524  break;
525  }
526 
527  switch (text.mAlign)
528  {
529  case EAlign::Near:
530  [mTextField setTextAlignment: NSTextAlignmentLeft];
531  break;
532  case EAlign::Center:
533  [mTextField setTextAlignment: NSTextAlignmentCenter];
534  break;
535  case EAlign::Far:
536  [mTextField setTextAlignment: NSTextAlignmentRight];
537  break;
538  default:
539  break;
540  }
541 
542  [self addSubview: mTextField];
543  [mTextField becomeFirstResponder];
544 }
545 
546 - (void) endUserInput
547 {
548  [self becomeFirstResponder];
549  [mTextField setDelegate: nil];
550  [mTextField removeFromSuperview];
551  mTextField = nullptr;
552 }
553 
554 - (void) showMessageBox: (const char*) str : (const char*) caption : (EMsgBoxType) type : (IMsgBoxCompletionHanderFunc) completionHandler
555 {
556  NSString* titleNString = [NSString stringWithUTF8String:str];
557  NSString* captionNString = [NSString stringWithUTF8String:caption];
558 
559  UIAlertController* alertController = [UIAlertController alertControllerWithTitle:titleNString message:captionNString preferredStyle:UIAlertControllerStyleAlert];
560 
561  void (^handlerBlock)(UIAlertAction*) =
562  ^(UIAlertAction* action) {
563 
564  if(completionHandler != nullptr)
565  {
566  EMsgBoxResult result = EMsgBoxResult::kCANCEL;
567 
568  if([action.title isEqualToString:@"OK"])
569  result = EMsgBoxResult::kOK;
570  if([action.title isEqualToString:@"Cancel"])
571  result = EMsgBoxResult::kCANCEL;
572  if([action.title isEqualToString:@"Yes"])
573  result = EMsgBoxResult::kYES;
574  if([action.title isEqualToString:@"No"])
575  result = EMsgBoxResult::kNO;
576  if([action.title isEqualToString:@"Retry"])
577  result = EMsgBoxResult::kRETRY;
578 
579  completionHandler(result);
580  }
581 
582  };
583 
584  if(type == kMB_OK || type == kMB_OKCANCEL)
585  {
586  UIAlertAction* okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:handlerBlock];
587  [alertController addAction:okAction];
588  }
589 
590  if(type == kMB_YESNO || type == kMB_YESNOCANCEL)
591  {
592  UIAlertAction* yesAction = [UIAlertAction actionWithTitle:@"Yes" style:UIAlertActionStyleDefault handler:handlerBlock];
593  [alertController addAction:yesAction];
594 
595  UIAlertAction* noAction = [UIAlertAction actionWithTitle:@"No" style:UIAlertActionStyleDefault handler:handlerBlock];
596  [alertController addAction:noAction];
597  }
598 
599  if(type == kMB_RETRYCANCEL)
600  {
601  UIAlertAction* retryAction = [UIAlertAction actionWithTitle:@"Retry" style:UIAlertActionStyleDefault handler:handlerBlock];
602  [alertController addAction:retryAction];
603  }
604 
605  if(type == kMB_OKCANCEL || type == kMB_YESNOCANCEL || type == kMB_RETRYCANCEL)
606  {
607  UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:handlerBlock];
608  [alertController addAction:cancelAction];
609  }
610 
611  [self.window.rootViewController presentViewController:alertController animated:YES completion:nil];
612 }
613 
614 - (BOOL) promptForColor: (IColor&) color : (const char*) str : (IColorPickerHandlerFunc) func
615 {
616 #ifdef __IPHONE_14_0
617  UIColorPickerViewController* colorSelectionController = [[UIColorPickerViewController alloc] init];
618 
619  UIUserInterfaceIdiom idiom = [[UIDevice currentDevice] userInterfaceIdiom];
620 
621  if(idiom == UIUserInterfaceIdiomPad)
622  colorSelectionController.modalPresentationStyle = UIModalPresentationPopover;
623  else
624  colorSelectionController.modalPresentationStyle = UIModalPresentationPageSheet;
625 
626  colorSelectionController.popoverPresentationController.delegate = self;
627  colorSelectionController.popoverPresentationController.sourceView = self;
628 
629  float x, y;
630  mGraphics->GetMouseLocation(x, y);
631  colorSelectionController.popoverPresentationController.sourceRect = CGRectMake(x, y, 1, 1);
632 
633  colorSelectionController.delegate = self;
634  colorSelectionController.selectedColor = ToUIColor(color);
635  colorSelectionController.supportsAlpha = YES;
636 
637  mColorPickerHandlerFunc = func;
638 
639  [self.window.rootViewController presentViewController:colorSelectionController animated:YES completion:nil];
640 #endif
641 
642  return false;
643 }
644 
645 - (void) attachGestureRecognizer: (EGestureType) type
646 {
647  UIGestureRecognizer* gestureRecognizer;
648 
649  switch (type)
650  {
651  case EGestureType::DoubleTap:
652  case EGestureType::TripleTap:
653  {
654  gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapGesture:)];
655  [(UITapGestureRecognizer*) gestureRecognizer setNumberOfTapsRequired: type == EGestureType::DoubleTap ? 2 : 3];
656  [(UITapGestureRecognizer*) gestureRecognizer setNumberOfTouchesRequired:1];
657  break;
658  }
659  case EGestureType::LongPress1:
660  case EGestureType::LongPress2:
661  {
662  gestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPressGesture:)];
663  [(UILongPressGestureRecognizer*) gestureRecognizer setNumberOfTouchesRequired: type == EGestureType::LongPress1 ? 1 : 2];
664  break;
665  }
666  case EGestureType::SwipeLeft:
667  {
668  gestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(onSwipeGesture:)];
669  [(UISwipeGestureRecognizer*) gestureRecognizer setDirection:UISwipeGestureRecognizerDirectionLeft];
670  break;
671  }
672  case EGestureType::SwipeRight:
673  {
674  gestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(onSwipeGesture:)];
675  [(UISwipeGestureRecognizer*) gestureRecognizer setDirection:UISwipeGestureRecognizerDirectionRight];
676  break;
677  }
678  case EGestureType::SwipeUp:
679  {
680  gestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(onSwipeGesture:)];
681  [(UISwipeGestureRecognizer*) gestureRecognizer setDirection:UISwipeGestureRecognizerDirectionUp];
682  break;
683  }
684  case EGestureType::SwipeDown:
685  {
686  gestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(onSwipeGesture:)];
687  [(UISwipeGestureRecognizer*) gestureRecognizer setDirection:UISwipeGestureRecognizerDirectionDown];
688  break;
689  }
690  case EGestureType::Pinch:
691  {
692  gestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(onPinchGesture:)];
693  break;
694  }
695  case EGestureType::Rotate:
696  {
697  gestureRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(onRotateGesture:)];
698  break;
699  }
700  default:
701  return;
702  }
703 
704  gestureRecognizer.delegate = self;
705  gestureRecognizer.cancelsTouchesInView = YES;
706  gestureRecognizer.delaysTouchesBegan = YES;
707  [self addGestureRecognizer:gestureRecognizer];
708 }
709 
710 - (void) onTapGesture: (UITapGestureRecognizer*) recognizer
711 {
712  CGPoint p = [recognizer locationInView:self];
713  auto ds = mGraphics->GetDrawScale();
714  IGestureInfo info;
715  info.x = p.x / ds;
716  info.y = p.y / ds;
717  info.type = recognizer.numberOfTapsRequired == 2 ? EGestureType::DoubleTap : EGestureType::TripleTap;
718 
719  mGraphics->OnGestureRecognized(info);
720 }
721 
722 - (void) onLongPressGesture: (UILongPressGestureRecognizer*) recognizer
723 {
724  CGPoint p = [recognizer locationInView:self];
725  auto ds = mGraphics->GetDrawScale();
726  IGestureInfo info;
727  info.x = p.x / ds;
728  info.y = p.y / ds;
729  if(recognizer.state == UIGestureRecognizerStateBegan)
730  info.state = EGestureState::Began;
731  else if(recognizer.state == UIGestureRecognizerStateChanged)
732  info.state = EGestureState::InProcess;
733  else if(recognizer.state == UIGestureRecognizerStateEnded)
734  info.state = EGestureState::Ended;
735 
736  info.type = recognizer.numberOfTouchesRequired == 1 ? EGestureType::LongPress1 : EGestureType::LongPress2;
737 
738  mGraphics->OnGestureRecognized(info);
739 }
740 
741 - (void) onSwipeGesture: (UISwipeGestureRecognizer*) recognizer
742 {
743  CGPoint p = [recognizer locationInView:self];
744  auto ds = mGraphics->GetDrawScale();
745  IGestureInfo info;
746  info.x = p.x / ds;
747  info.y = p.y / ds;
748 
749  switch (recognizer.direction) {
750  case UISwipeGestureRecognizerDirectionLeft: info.type = EGestureType::SwipeLeft; break;
751  case UISwipeGestureRecognizerDirectionRight: info.type = EGestureType::SwipeRight; break;
752  case UISwipeGestureRecognizerDirectionUp: info.type = EGestureType::SwipeUp; break;
753  case UISwipeGestureRecognizerDirectionDown: info.type = EGestureType::SwipeDown; break;
754  default:
755  break;
756  }
757 
758  mGraphics->OnGestureRecognized(info);
759 }
760 
761 - (void) onPinchGesture: (UIPinchGestureRecognizer*) recognizer
762 {
763  CGPoint p = [recognizer locationInView:self];
764  auto ds = mGraphics->GetDrawScale();
765  IGestureInfo info;
766  info.x = p.x / ds;
767  info.y = p.y / ds;
768  info.velocity = recognizer.velocity;
769  info.scale = recognizer.scale;
770 
771  if(recognizer.state == UIGestureRecognizerStateBegan)
772  info.state = EGestureState::Began;
773  else if(recognizer.state == UIGestureRecognizerStateChanged)
774  info.state = EGestureState::InProcess;
775  else if(recognizer.state == UIGestureRecognizerStateEnded)
776  info.state = EGestureState::Ended;
777 
778  info.type = EGestureType::Pinch;
779 
780  mGraphics->OnGestureRecognized(info);
781 }
782 
783 - (void) onRotateGesture: (UIRotationGestureRecognizer*) recognizer
784 {
785  CGPoint p = [recognizer locationInView:self];
786  auto ds = mGraphics->GetDrawScale();
787  IGestureInfo info;
788  info.x = p.x / ds;
789  info.y = p.y / ds;
790  info.velocity = recognizer.velocity;
791  info.angle = RadToDeg(recognizer.rotation);
792 
793  if(recognizer.state == UIGestureRecognizerStateBegan)
794  info.state = EGestureState::Began;
795  else if(recognizer.state == UIGestureRecognizerStateChanged)
796  info.state = EGestureState::InProcess;
797  else if(recognizer.state == UIGestureRecognizerStateEnded)
798  info.state = EGestureState::Ended;
799 
800  info.type = EGestureType::Rotate;
801 
802  mGraphics->OnGestureRecognized(info);
803 }
804 
805 -(BOOL) gestureRecognizer:(UIGestureRecognizer*) gestureRecognizer shouldReceiveTouch:(UITouch*) touch
806 {
807  CGPoint pos = [touch locationInView:touch.view];
808 
809  auto ds = mGraphics->GetDrawScale();
810 
811  if(mGraphics->RespondsToGesture(pos.x / ds, pos.y / ds))
812  return TRUE;
813  else
814  return FALSE;
815 }
816 
817 - (void) keyboardWillShow:(NSNotification*) notification
818 {
819  NSDictionary* info = [notification userInfo];
820  CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
821 
822  UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
823  self.contentInset = contentInsets;
824  self.scrollIndicatorInsets = contentInsets;
825 
826  CGRect r = self.frame;
827  r.size.height -= kbSize.height;
828 
829  if (!CGRectContainsPoint(r, CGPointMake(mTextField.frame.origin.x + mTextField.frame.size.width, mTextField.frame.origin.y + mTextField.frame.size.height)) ) {
830  [self scrollRectToVisible:mTextField.frame animated:YES];
831  }
832 }
833 
834 - (void) keyboardWillBeHidden:(NSNotification*) notification
835 {
836  UIEdgeInsets contentInsets = UIEdgeInsetsZero;
837  self.contentInset = contentInsets;
838  self.scrollIndicatorInsets = contentInsets;
839 }
840 
841 - (void) applicationDidEnterBackgroundNotification:(NSNotification*) notification
842 {
843  [self.displayLink setPaused:YES];
844 }
845 
846 - (void) applicationWillEnterForegroundNotification:(NSNotification*) notification
847 {
848  [self.displayLink setPaused:NO];
849 }
850 
851 - (BOOL) delaysContentTouches
852 {
853  return NO;
854 }
855 
856 - (void) scrollViewDidScroll:(UIScrollView*) scrollView
857 {
858  mGraphics->SetTranslation(0, -self.contentOffset.y);
859  mGraphics->SetAllControlsDirty();
860 }
861 
862 - (void) presentationControllerDidDismiss: (UIPresentationController*) presentationController
863 {
864  mGraphics->SetControlValueAfterPopupMenu(nullptr);
865 }
866 
867 #ifdef __IPHONE_14_0
868 - (void) colorPickerViewControllerDidSelectColor:(UIColorPickerViewController*) viewController;
869 {
870  if(mColorPickerHandlerFunc)
871  {
872  IColor c = FromUIColor([viewController selectedColor]);
873  mColorPickerHandlerFunc(c);
874  }
875 }
876 
877 - (void) colorPickerViewControllerDidFinish:(UIColorPickerViewController*) viewController;
878 {
879  mColorPickerHandlerFunc = nullptr;
880 }
881 #endif
882 
883 - (void) getLastTouchLocation: (float&) x : (float&) y
884 {
885  const float scale = mGraphics->GetDrawScale();
886  x = mPrevX * scale;
887  y = mPrevY * scale;
888 }
889 
890 @end
891 
892 #ifdef IGRAPHICS_IMGUI
893 
894 @implementation IGRAPHICS_IMGUIVIEW
895 {
896 }
897 
898 - (id) initWithIGraphicsView: (IGraphicsIOS_View*) pView;
899 {
900  mView = pView;
901  self = [super initWithFrame:[pView frame] device: MTLCreateSystemDefaultDevice()];
902 
903  if(self)
904  {
905  _commandQueue = [self.device newCommandQueue];
906  self.layer.opaque = NO;
907  }
908 
909  return self;
910 }
911 
912 - (void) drawRect:(CGRect)rect
913 {
914  id<MTLCommandBuffer> commandBuffer = [self.commandQueue commandBuffer];
915 
916  MTLRenderPassDescriptor *renderPassDescriptor = self.currentRenderPassDescriptor;
917  if (renderPassDescriptor != nil)
918  {
919  renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0,0,0,0);
920 
921  id <MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
922  [renderEncoder pushDebugGroup:@"ImGui IGraphics"];
923 
924  ImGui_ImplMetal_NewFrame(renderPassDescriptor);
925 
926  mView->mGraphics->mImGuiRenderer->DoFrame();
927 
928  ImDrawData *drawData = ImGui::GetDrawData();
929  ImGui_ImplMetal_RenderDrawData(drawData, commandBuffer, renderEncoder);
930 
931  [renderEncoder popDebugGroup];
932  [renderEncoder endEncoding];
933 
934  [commandBuffer presentDrawable:self.currentDrawable];
935  }
936  [commandBuffer commit];
937 }
938 
939 @end
940 
941 #endif
942 
Used to manage a list of rectangular areas and optimize them for drawing to the screen.
The lowest level base class of an IGraphics control.
Definition: IControl.h:42
const IParam * GetParam(int valIdx=0) const
Get a const pointer to the IParam object (owned by the editor delegate class), associated with this c...
Definition: IControl.cpp:120
IPlug&#39;s parameter class.
Used to manage color data, independent of draw class/platform.
Used to describe a particular gesture.
A class to specify an item of a pop up menu.
IPlug&#39;s parameter class.
This file contains the base IControl implementation, along with some base classes for specific types ...
A class for setting the contents of a pop up menu.
IText is used to manage font and text/text entry style for a piece of text on the UI...
Used to group mouse coordinates with mouse modifier information.
IGraphics platform class for IOS.
Definition: IGraphicsIOS.h:24
EParamType Type() const
Get the parameter&#39;s type.