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