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 #if TARGET_OS_VISION
217 CGFloat scale = 2.0;
218 #else
219 CGFloat scale = [UIScreen mainScreen].scale;
220 #endif
221
222 self.layer.frame = self.frame;
223 self.layer.opaque = YES;
224 self.layer.contentsScale = scale;
225 self.contentScaleFactor = scale;
226
227 CAMetalLayer* mtlLayer = (CAMetalLayer*) self.layer;
228 mtlLayer.framebufferOnly = YES;
229 mtlLayer.device = MTLCreateSystemDefaultDevice();
230
231#if defined IGRAPHICS_GL
232 EGLDisplay display = eglGetPlatformDisplay(EGLenum(EGL_PLATFORM_ANGLE_ANGLE), 0, 0);
233
234 if (!display) {
235 DBGMSG("eglGetPlatformDisplay() returned error %i", eglGetError());
236 }
237
238 if (eglInitialize(display, nil, nil) == 0) {
239 DBGMSG("eglInitialize() returned error %i", eglGetError());
240 }
241
242 const EGLint configAttribs[9] = {
243 EGL_BLUE_SIZE, 8,
244 EGL_GREEN_SIZE, 8,
245 EGL_RED_SIZE, 8,
246 EGL_DEPTH_SIZE, 24,
247 EGL_NONE};
248 EGLint numConfigs = 0;
249 EGLConfig configs[1];
250
251 if (eglChooseConfig(display, configAttribs, configs, 1, &numConfigs) == 0) {
252 DBGMSG("eglChooseConfig() returned error %i", eglGetError());
253 }
254
255 if (!configs[0]) {
256 DBGMSG("Empty config returned in eglChooseConfig()");
257 }
258
259#if defined IGRAPHICS_GLES2
260 const EGLint contextAttribs [5] = {
261 EGL_CONTEXT_MAJOR_VERSION, 2,
262 EGL_CONTEXT_MINOR_VERSION, 0,
263 EGL_NONE,
264 };
265#elif defined IGRAPHICS_GLES3
266 const EGLint contextAttribs [5] = {
267 EGL_CONTEXT_MAJOR_VERSION, 3,
268 EGL_CONTEXT_MINOR_VERSION, 0,
269 EGL_NONE,
270 };
271#endif
272
273 EGLContext context = eglCreateContext(display, configs[0], nullptr, contextAttribs);
274
275 if (!context) {
276 DBGMSG("eglCreateContext() returned error %d", eglGetError());
277 }
278
279 EGLSurface surface = eglCreateWindowSurface(display, configs[0], (__bridge EGLNativeWindowType) [self layer], nullptr);
280
281 if (!surface) {
282 DBGMSG("eglCreateWindowSurface() returned error %d", eglGetError());
283 }
284
285 mEGLSurface = surface;
286 mEGLDisplay = display;
287 mEGLContext = context;
288#endif
289
290 self.multipleTouchEnabled = NO;
291
292 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackgroundNotification:) name:UIApplicationDidEnterBackgroundNotification object:nil];
293 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForegroundNotification:) name:UIApplicationWillEnterForegroundNotification object:nil];
294 mColorPickerHandlerFunc = nullptr;
295
296 UIHoverGestureRecognizer* hoverGestureRecognizer =
297 [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(onHoverGesture:)];
298 [self addGestureRecognizer: hoverGestureRecognizer];
299
300 return self;
301}
302
303- (void) setFrame:(CGRect) frame
304{
305 [super setFrame:frame];
306
307#if TARGET_OS_VISION
308 CGFloat scale = 2.0;
309#else
310 CGFloat scale = [UIScreen mainScreen].scale;
311 if (self.window) {
312 scale = self.window.screen.scale;
313 }
314#endif
315
316 [CATransaction begin];
317 [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
318 [self.layer setFrame:frame];
319
320 // CAMetalLayer is correct for both Metal and GLES - ANGLE uses Metal backend on iOS
321 CAMetalLayer* mtlLayer = (CAMetalLayer*) self.layer;
322 CGSize drawableSize = self.bounds.size;
323 drawableSize.width *= scale;
324 drawableSize.height *= scale;
325 mtlLayer.drawableSize = drawableSize;
326
327 [CATransaction commit];
328}
329
330- (void) onTouchEvent:(ETouchEvent) eventType withTouches:(NSSet*) touches withEvent:(UIEvent*) event
331{
332 if(mGraphics == nullptr) //TODO: why?
333 return;
334
335 NSEnumerator* pEnumerator = [[event allTouches] objectEnumerator];
336 UITouch* pTouch;
337
338 std::vector<IMouseInfo> points;
339
340 while ((pTouch = [pEnumerator nextObject]))
341 {
342 CGPoint pos = [pTouch locationInView:pTouch.view];
343
344 IMouseInfo point;
345
346 auto ds = mGraphics->GetDrawScale();
347
348 point.ms.L = true;
349 point.ms.touchID = reinterpret_cast<ITouchID>(pTouch);
350 point.ms.touchRadius = [pTouch majorRadius];
351
352 point.x = pos.x / ds;
353 point.y = pos.y / ds;
354 CGPoint posPrev = [pTouch previousLocationInView: self];
355 point.dX = (pos.x - posPrev.x) / ds;
356 point.dY = (pos.y - posPrev.y) / ds;
357
358 if([touches containsObject:pTouch])
359 {
360 mPrevX = point.x;
361 mPrevY = point.y;
362 points.push_back(point);
363 }
364 }
365
366// DBGMSG("%lu\n", points[0].ms.idx);
367
368 if(eventType == ETouchEvent::Began)
369 mGraphics->OnMouseDown(points);
370
371 if(eventType == ETouchEvent::Moved)
372 mGraphics->OnMouseDrag(points);
373
374 if(eventType == ETouchEvent::Ended)
375 mGraphics->OnMouseUp(points);
376
377 if(eventType == ETouchEvent::Cancelled)
378 mGraphics->OnTouchCancelled(points);
379}
380
381- (void) touchesBegan:(NSSet*) touches withEvent:(UIEvent*) event
382{
383 [self onTouchEvent:ETouchEvent::Began withTouches:touches withEvent:event];
384}
385
386- (void) touchesMoved:(NSSet*) touches withEvent:(UIEvent*) event
387{
388 [self onTouchEvent:ETouchEvent::Moved withTouches:touches withEvent:event];
389}
390
391- (void) touchesEnded:(NSSet*) touches withEvent:(UIEvent*) event
392{
393 [self onTouchEvent:ETouchEvent::Ended withTouches:touches withEvent:event];
394}
395
396- (void) touchesCancelled:(NSSet*) touches withEvent:(UIEvent*) event
397{
398 [self onTouchEvent:ETouchEvent::Cancelled withTouches:touches withEvent:event];
399}
400
401+ (Class) layerClass
402{
403 return CAMetalLayer.class;
404}
405
406- (void) didMoveToSuperview
407{
408 [super didMoveToSuperview];
409 if (self.superview)
410 {
411 self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(redraw:)];
412 [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
413 self.displayLink.preferredFramesPerSecond = mGraphics->FPS();
414 }
415 else
416 {
417 [self.displayLink invalidate];
418 self.displayLink = nil;
419 }
420}
421
422- (void) drawRect:(CGRect)rect
423{
424 IRECTList rects;
425
426 if(mGraphics)
427 {
428 mGraphics->SetPlatformContext(UIGraphicsGetCurrentContext());
429 IGraphics::ScopedGLContext scopedGLContext{mGraphics};
430
431 if (mGraphics->IsDirty(rects))
432 {
433 mGraphics->SetAllControlsClean();
434 mGraphics->Draw(rects);
435 }
436 [self swapBuffers];
437 }
438}
439
440- (void) redraw:(CADisplayLink*) displayLink
441{
442#ifdef IGRAPHICS_CPU
443 [self setNeedsDisplay];
444#else
445 [self drawRect:CGRect()];
446#endif
447}
448
449- (BOOL) isOpaque
450{
451 return YES;
452}
453
454- (BOOL) acceptsFirstResponder
455{
456 return YES;
457}
458
459- (BOOL) canBecomeFirstResponder
460{
461 return YES;
462}
463
464- (void) removeFromSuperview
465{
466 [self.displayLink invalidate];
467 self.displayLink = nil;
468 mTextField = nil;
469 mGraphics = nil;
470 mMenuTableController = nil;
471 mMenuNavigationController = nil;
472
473#if defined IGRAPHICS_GL
474 if (mEGLDisplay != EGL_NO_DISPLAY)
475 {
476 eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
477 if (mEGLSurface != EGL_NO_SURFACE)
478 eglDestroySurface(mEGLDisplay, mEGLSurface);
479 if (mEGLContext != EGL_NO_CONTEXT)
480 eglDestroyContext(mEGLDisplay, mEGLContext);
481 eglTerminate(mEGLDisplay);
482 }
483 mEGLDisplay = EGL_NO_DISPLAY;
484 mEGLSurface = EGL_NO_SURFACE;
485 mEGLContext = EGL_NO_CONTEXT;
486#endif
487}
488
489- (BOOL) textFieldShouldReturn:(UITextField*) textField
490{
491 if (textField == mTextField)
492 {
493 mGraphics->SetControlValueAfterTextEdit([[mTextField text] UTF8String]);
494 mGraphics->SetAllControlsDirty();
495
496 [self endUserInput];
497 }
498 return YES;
499}
500
501- (BOOL) textField:(UITextField*) textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*) string
502{
503 if (!string.length)
504 return YES;
505
506 // verify max length has not been exceeded
507 NSString* proposedText = [mTextField.text stringByReplacingCharactersInRange:range withString:string];
508
509 if (proposedText.length > mTextFieldLength)
510 return NO;
511
512 IControl* pInTextEntry = mGraphics->GetControlInTextEntry();
513
514 if(pInTextEntry)
515 {
516 const IParam* pParam = pInTextEntry->GetParam();
517
518 if (pParam)
519 {
520 NSMutableCharacterSet *characterSet = [[NSMutableCharacterSet alloc] init];
521
522 switch ( pParam->Type() )
523 {
524 case IParam::kTypeEnum:
525 case IParam::kTypeInt:
526 case IParam::kTypeBool:
527 [characterSet addCharactersInString:@"0123456789-+"];
528 break;
529 case IParam::kTypeDouble:
530 [characterSet addCharactersInString:@"0123456789.-+"];
531 break;
532 default:
533 break;
534 }
535
536 if ([string rangeOfCharacterFromSet:characterSet.invertedSet].location != NSNotFound)
537 return NO;
538 }
539 }
540
541 return YES;
542}
543
544- (UIModalPresentationStyle) adaptivePresentationStyleForPresentationController:(UIPresentationController*) controller
545{
546 return UIModalPresentationNone;
547}
548
549- (BOOL) presentationControllerShouldDismiss:(UIPopoverPresentationController*) popoverPresentationController
550{
551 return YES;
552}
553
554- (IPopupMenu*) createPopupMenu: (IPopupMenu&) menu : (CGRect) bounds;
555{
556 mMenuTableController = [[IGRAPHICS_UITABLEVC alloc] initWithIPopupMenuAndIGraphics:&menu : mGraphics];
557 [mMenuTableController setTitle: [NSString stringWithUTF8String:menu.GetRootTitle()]];
558
559 mMenuNavigationController = [[UINavigationController alloc] initWithRootViewController:mMenuTableController];
560
561 mMenuNavigationController.modalPresentationStyle = UIModalPresentationPopover;
562 mMenuNavigationController.popoverPresentationController.sourceView = self;
563 mMenuNavigationController.popoverPresentationController.sourceRect = bounds;
564// mMenuNavigationController.popoverPresentationController.permittedArrowDirections = UIPopoverArrowDirectionUp;
565 mMenuNavigationController.popoverPresentationController.delegate = self;
566
567 [self.window.rootViewController presentViewController:mMenuNavigationController animated:YES completion:nil];
568
569 return nullptr;
570}
571
572- (void) createTextEntry: (int) paramIdx : (const IText&) text : (const char*) str : (int) length : (CGRect) areaRect
573{
574 if (mTextField)
575 return;
576
577 mAlertController = [UIAlertController alertControllerWithTitle:@"Input a value:" message:@"" preferredStyle:UIAlertControllerStyleAlert];
578
579 __weak IGRAPHICS_VIEW* weakSelf = self;
580
581 void (^cancelHandler)(UIAlertAction*) = ^(UIAlertAction *action)
582 {
583 __strong IGRAPHICS_VIEW* strongSelf = weakSelf;
584 strongSelf->mGraphics->SetAllControlsDirty();
585 [strongSelf endUserInput];
586 };
587
588 UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:cancelHandler];
589 [mAlertController addAction:cancelAction];
590
591 void (^okHandler)(UIAlertAction*) = ^(UIAlertAction *action)
592 {
593 __strong IGRAPHICS_VIEW* strongSelf = weakSelf;
594 strongSelf->mGraphics->SetControlValueAfterTextEdit([[strongSelf->mTextField text] UTF8String]);
595 strongSelf->mGraphics->SetAllControlsDirty();
596 [strongSelf endUserInput];
597 };
598
599 UIAlertAction* okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:okHandler];
600 [mAlertController addAction:okAction];
601 [mAlertController setPreferredAction:okAction];
602
603 [mAlertController addTextFieldWithConfigurationHandler:^(UITextField* aTextField) {
604 __strong IGRAPHICS_VIEW* strongSelf = weakSelf;
605 strongSelf->mTextField = aTextField;
606 strongSelf->mTextFieldLength = length;
607 aTextField.delegate = strongSelf;
608 [aTextField setText:[NSString stringWithUTF8String:str]];
609 }];
610 [self.window.rootViewController presentViewController:mAlertController animated:YES completion:nil];
611}
612
613- (void) endUserInput
614{
615 [self becomeFirstResponder];
616 [self.window.rootViewController dismissViewControllerAnimated:NO completion:nil];
617 [mTextField setDelegate: nil];
618 mAlertController = nullptr;
619 mTextField = nullptr;
620 mGraphics->ClearInTextEntryControl();
621}
622
623- (void) showMessageBox: (const char*) str : (const char*) title : (EMsgBoxType) type : (IMsgBoxCompletionHandlerFunc) completionHandler
624{
625 [self endUserInput];
626
627 NSString* message = [NSString stringWithUTF8String:str];
628 NSString* titleNs = [NSString stringWithUTF8String:title];
629
630 UIAlertController* alertController = [UIAlertController alertControllerWithTitle:titleNs message:message preferredStyle:UIAlertControllerStyleAlert];
631
632 void (^handlerBlock)(UIAlertAction*) =
633 ^(UIAlertAction* action) {
634
635 if (completionHandler != nullptr)
636 {
637 EMsgBoxResult result = EMsgBoxResult::kCANCEL;
638
639 if ([action.title isEqualToString:@"OK"])
640 result = EMsgBoxResult::kOK;
641 if ([action.title isEqualToString:@"Cancel"])
642 result = EMsgBoxResult::kCANCEL;
643 if ([action.title isEqualToString:@"Yes"])
644 result = EMsgBoxResult::kYES;
645 if ([action.title isEqualToString:@"No"])
646 result = EMsgBoxResult::kNO;
647 if ([action.title isEqualToString:@"Retry"])
648 result = EMsgBoxResult::kRETRY;
649
650 completionHandler(result);
651 }
652
653 };
654
655 if (type == kMB_OK || type == kMB_OKCANCEL)
656 {
657 UIAlertAction* okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:handlerBlock];
658 [alertController addAction:okAction];
659 }
660
661 if (type == kMB_YESNO || type == kMB_YESNOCANCEL)
662 {
663 UIAlertAction* yesAction = [UIAlertAction actionWithTitle:@"Yes" style:UIAlertActionStyleDefault handler:handlerBlock];
664 [alertController addAction:yesAction];
665
666 UIAlertAction* noAction = [UIAlertAction actionWithTitle:@"No" style:UIAlertActionStyleDefault handler:handlerBlock];
667 [alertController addAction:noAction];
668 }
669
670 if (type == kMB_RETRYCANCEL)
671 {
672 UIAlertAction* retryAction = [UIAlertAction actionWithTitle:@"Retry" style:UIAlertActionStyleDefault handler:handlerBlock];
673 [alertController addAction:retryAction];
674 }
675
676 if (type == kMB_OKCANCEL || type == kMB_YESNOCANCEL || type == kMB_RETRYCANCEL)
677 {
678 UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:handlerBlock];
679 [alertController addAction:cancelAction];
680 }
681
682 [[NSOperationQueue mainQueue] addOperationWithBlock:^{
683 [self.window.rootViewController presentViewController:alertController animated:YES completion:nil];
684 }];
685}
686
687- (void) promptForFile: (NSString*) fileName : (NSString*) path : (EFileAction) action : (NSArray*) contentTypes : (IFileDialogCompletionHandlerFunc) completionHandler
688{
689 [self endUserInput];
690
691 mFileDialogFunc = completionHandler;
692
693 UIDocumentPickerViewController* vc = NULL;
694 NSURL* url = [[NSURL alloc] initFileURLWithPath:path];
695
696 if (action == EFileAction::Open)
697 {
698 vc = [[UIDocumentPickerViewController alloc] initForOpeningContentTypes:contentTypes asCopy:YES];
699 [vc setDirectoryURL:url];
700 }
701 else
702 {
703 vc = [[UIDocumentPickerViewController alloc] initForExportingURLs:@[url]];
704 }
705
706 [vc setDelegate:self];
707
708 [self.window.rootViewController presentViewController:vc animated:YES completion:nil];
709}
710
711- (void) promptForDirectory: (NSString*) path : (IFileDialogCompletionHandlerFunc) completionHandler
712{
713 [self endUserInput];
714
715 mFileDialogFunc = completionHandler;
716
717 UIDocumentPickerViewController* vc = NULL;
718 NSURL* url = [[NSURL alloc] initFileURLWithPath:path];
719
720 NSMutableArray* pFileTypes = [[NSMutableArray alloc] init];
721 UTType* directoryType = [UTType typeWithIdentifier:@"public.folder"];
722 [pFileTypes addObject:directoryType];
723
724 vc = [[UIDocumentPickerViewController alloc] initForOpeningContentTypes:pFileTypes];
725 [vc setDirectoryURL:url];
726
727 [vc setDelegate:self];
728
729 [self.window.rootViewController presentViewController:vc animated:YES completion:nil];
730}
731
732- (BOOL) promptForColor: (IColor&) color : (const char*) str : (IColorPickerHandlerFunc) func
733{
734 [self endUserInput];
735
736 UIColorPickerViewController* colorSelectionController = [[UIColorPickerViewController alloc] init];
737
738 colorSelectionController.modalPresentationStyle = UIModalPresentationPopover;
739 colorSelectionController.popoverPresentationController.delegate = self;
740 colorSelectionController.popoverPresentationController.sourceView = self;
741
742 float x, y;
743 mGraphics->GetMouseLocation(x, y);
744 colorSelectionController.popoverPresentationController.sourceRect = CGRectMake(x, y, 1, 1);
745
746 colorSelectionController.delegate = self;
747 colorSelectionController.selectedColor = ToUIColor(color);
748 colorSelectionController.supportsAlpha = YES;
749
750 mColorPickerHandlerFunc = func;
751
752 [self.window.rootViewController presentViewController:colorSelectionController animated:YES completion:nil];
753
754 return false;
755}
756
757- (void) attachGestureRecognizer: (EGestureType) type
758{
759 UIGestureRecognizer* gestureRecognizer;
760
761 switch (type)
762 {
763 case EGestureType::DoubleTap:
764 case EGestureType::TripleTap:
765 {
766 gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapGesture:)];
767 [(UITapGestureRecognizer*) gestureRecognizer setNumberOfTapsRequired: type == EGestureType::DoubleTap ? 2 : 3];
768 [(UITapGestureRecognizer*) gestureRecognizer setNumberOfTouchesRequired:1];
769 break;
770 }
771 case EGestureType::LongPress1:
772 case EGestureType::LongPress2:
773 {
774 gestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPressGesture:)];
775 [(UILongPressGestureRecognizer*) gestureRecognizer setNumberOfTouchesRequired: type == EGestureType::LongPress1 ? 1 : 2];
776 break;
777 }
778 case EGestureType::SwipeLeft:
779 {
780 gestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(onSwipeGesture:)];
781 [(UISwipeGestureRecognizer*) gestureRecognizer setDirection:UISwipeGestureRecognizerDirectionLeft];
782 break;
783 }
784 case EGestureType::SwipeRight:
785 {
786 gestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(onSwipeGesture:)];
787 [(UISwipeGestureRecognizer*) gestureRecognizer setDirection:UISwipeGestureRecognizerDirectionRight];
788 break;
789 }
790 case EGestureType::SwipeUp:
791 {
792 gestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(onSwipeGesture:)];
793 [(UISwipeGestureRecognizer*) gestureRecognizer setDirection:UISwipeGestureRecognizerDirectionUp];
794 break;
795 }
796 case EGestureType::SwipeDown:
797 {
798 gestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(onSwipeGesture:)];
799 [(UISwipeGestureRecognizer*) gestureRecognizer setDirection:UISwipeGestureRecognizerDirectionDown];
800 break;
801 }
802 case EGestureType::Pinch:
803 {
804 gestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(onPinchGesture:)];
805 break;
806 }
807 case EGestureType::Rotate:
808 {
809 gestureRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(onRotateGesture:)];
810 break;
811 }
812 default:
813 return;
814 }
815
816 gestureRecognizer.delegate = self;
817 gestureRecognizer.cancelsTouchesInView = YES;
818 gestureRecognizer.delaysTouchesBegan = YES;
819 [self addGestureRecognizer:gestureRecognizer];
820}
821
822- (void) onTapGesture: (UITapGestureRecognizer*) recognizer
823{
824 CGPoint p = [recognizer locationInView:self];
825 auto ds = mGraphics->GetDrawScale();
826 IGestureInfo info;
827 info.x = p.x / ds;
828 info.y = p.y / ds;
829 info.type = recognizer.numberOfTapsRequired == 2 ? EGestureType::DoubleTap : EGestureType::TripleTap;
830
831 mGraphics->OnGestureRecognized(info);
832}
833
834- (void) onLongPressGesture: (UILongPressGestureRecognizer*) recognizer
835{
836 CGPoint p = [recognizer locationInView:self];
837 auto ds = mGraphics->GetDrawScale();
838 IGestureInfo info;
839 info.x = p.x / ds;
840 info.y = p.y / ds;
841 if(recognizer.state == UIGestureRecognizerStateBegan)
842 info.state = EGestureState::Began;
843 else if(recognizer.state == UIGestureRecognizerStateChanged)
844 info.state = EGestureState::InProcess;
845 else if(recognizer.state == UIGestureRecognizerStateEnded)
846 info.state = EGestureState::Ended;
847
848 info.type = recognizer.numberOfTouchesRequired == 1 ? EGestureType::LongPress1 : EGestureType::LongPress2;
849
850 mGraphics->OnGestureRecognized(info);
851}
852
853- (void) onSwipeGesture: (UISwipeGestureRecognizer*) recognizer
854{
855 CGPoint p = [recognizer locationInView:self];
856 auto ds = mGraphics->GetDrawScale();
857 IGestureInfo info;
858 info.x = p.x / ds;
859 info.y = p.y / ds;
860
861 switch (recognizer.direction) {
862 case UISwipeGestureRecognizerDirectionLeft: info.type = EGestureType::SwipeLeft; break;
863 case UISwipeGestureRecognizerDirectionRight: info.type = EGestureType::SwipeRight; break;
864 case UISwipeGestureRecognizerDirectionUp: info.type = EGestureType::SwipeUp; break;
865 case UISwipeGestureRecognizerDirectionDown: info.type = EGestureType::SwipeDown; break;
866 default:
867 break;
868 }
869
870 mGraphics->OnGestureRecognized(info);
871}
872
873- (void) onPinchGesture: (UIPinchGestureRecognizer*) recognizer
874{
875 CGPoint p = [recognizer locationInView:self];
876 auto ds = mGraphics->GetDrawScale();
877 IGestureInfo info;
878 info.x = p.x / ds;
879 info.y = p.y / ds;
880 info.velocity = recognizer.velocity;
881 info.scale = recognizer.scale;
882
883 if(recognizer.state == UIGestureRecognizerStateBegan)
884 info.state = EGestureState::Began;
885 else if(recognizer.state == UIGestureRecognizerStateChanged)
886 info.state = EGestureState::InProcess;
887 else if(recognizer.state == UIGestureRecognizerStateEnded)
888 info.state = EGestureState::Ended;
889
890 info.type = EGestureType::Pinch;
891
892 mGraphics->OnGestureRecognized(info);
893}
894
895- (void) onRotateGesture: (UIRotationGestureRecognizer*) recognizer
896{
897 CGPoint p = [recognizer locationInView:self];
898 auto ds = mGraphics->GetDrawScale();
899 IGestureInfo info;
900 info.x = p.x / ds;
901 info.y = p.y / ds;
902 info.velocity = recognizer.velocity;
903 info.angle = RadToDeg(recognizer.rotation);
904
905 if(recognizer.state == UIGestureRecognizerStateBegan)
906 info.state = EGestureState::Began;
907 else if(recognizer.state == UIGestureRecognizerStateChanged)
908 info.state = EGestureState::InProcess;
909 else if(recognizer.state == UIGestureRecognizerStateEnded)
910 info.state = EGestureState::Ended;
911
912 info.type = EGestureType::Rotate;
913
914 mGraphics->OnGestureRecognized(info);
915}
916
917- (void) onHoverGesture: (UIHoverGestureRecognizer*) recognizer
918{
919 CGPoint pos = [recognizer locationInView:self];
920
921 IMouseInfo info;
922
923 auto ds = mGraphics->GetDrawScale();
924 info.x = pos.x / ds;
925 info.y = pos.y / ds;
926
927 if (mGraphics)
928 mGraphics->OnMouseOver(info.x, info.y, info.ms);
929}
930
931-(BOOL) gestureRecognizer:(UIGestureRecognizer*) gestureRecognizer shouldReceiveTouch:(UITouch*) touch
932{
933 CGPoint pos = [touch locationInView:touch.view];
934
935 if (mGraphics)
936 {
937 auto ds = mGraphics->GetDrawScale();
938
939 if (mGraphics->RespondsToGesture(pos.x / ds, pos.y / ds))
940 {
941 return TRUE;
942 }
943 }
944
945 return FALSE;
946}
947
948- (void) applicationDidEnterBackgroundNotification:(NSNotification*) notification
949{
950 [self.displayLink setPaused:YES];
951}
952
953- (void) applicationWillEnterForegroundNotification:(NSNotification*) notification
954{
955 [self.displayLink setPaused:NO];
956}
957
958- (BOOL) delaysContentTouches
959{
960 return NO;
961}
962
963- (void) presentationControllerDidDismiss: (UIPresentationController*) presentationController
964{
965 mGraphics->SetControlValueAfterPopupMenu(nullptr);
966}
967
968- (void) documentPicker:(UIDocumentPickerViewController*) controller didPickDocumentsAtURLs:(NSArray <NSURL*>*) urls
969{
970 WDL_String fileName, path;
971
972 if (urls.count == 1)
973 {
974 NSURL* pSource = urls[0];
975 NSString* pFullPath = [pSource path];
976 fileName.Set([pFullPath UTF8String]);
977
978 NSString* pTruncatedPath = [pFullPath stringByDeletingLastPathComponent];
979
980 if (pTruncatedPath)
981 {
982 path.Set([pTruncatedPath UTF8String]);
983 path.Append("/");
984 }
985
986 if (mFileDialogFunc)
987 mFileDialogFunc(fileName, path);
988 }
989 else
990 {
991 // call with empty values
992 if (mFileDialogFunc)
993 mFileDialogFunc(fileName, path);
994 }
995}
996
997- (void) documentPickerWasCancelled:(UIDocumentPickerViewController*) controller
998{
999 WDL_String fileName, path;
1000
1001 if (mFileDialogFunc)
1002 mFileDialogFunc(fileName, path);
1003}
1004
1005- (void) colorPickerViewControllerDidSelectColor:(UIColorPickerViewController*) viewController;
1006{
1007 if (mColorPickerHandlerFunc)
1008 {
1009 IColor c = FromUIColor([viewController selectedColor]);
1010 mColorPickerHandlerFunc(c);
1011 }
1012}
1013
1014- (void) colorPickerViewControllerDidFinish:(UIColorPickerViewController*) viewController;
1015{
1016 mColorPickerHandlerFunc = nullptr;
1017}
1018
1019- (void) traitCollectionDidChange: (UITraitCollection*) previousTraitCollection
1020{
1021 [super traitCollectionDidChange: previousTraitCollection];
1022
1023 if(mGraphics)
1024 {
1025 mGraphics->OnAppearanceChanged([self.traitCollection userInterfaceStyle] == UIUserInterfaceStyleDark ? EUIAppearance::Dark
1026 : EUIAppearance::Light);
1027 }
1028}
1029
1030- (void) getLastTouchLocation: (float&) x : (float&) y
1031{
1032 const float scale = mGraphics->GetDrawScale();
1033 x = mPrevX * scale;
1034 y = mPrevY * scale;
1035}
1036
1037- (void) activateGLContext
1038{
1039#ifdef IGRAPHICS_GL
1040 if (mEGLDisplay != EGL_NO_DISPLAY && mEGLContext != EGL_NO_CONTEXT)
1041 eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
1042#endif
1043}
1044
1045- (void) deactivateGLContext
1046{
1047#ifdef IGRAPHICS_GL
1048 if (mEGLDisplay != EGL_NO_DISPLAY)
1049 eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1050#endif
1051}
1052
1053- (void) swapBuffers
1054{
1055#ifdef IGRAPHICS_GL
1056 if (mEGLDisplay != EGL_NO_DISPLAY && mEGLSurface != EGL_NO_SURFACE)
1057 eglSwapBuffers(mEGLDisplay, mEGLSurface);
1058#endif
1059}
1060@end
1061
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,...