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