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