11#import <QuartzCore/QuartzCore.h>
13#if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
14#import <Metal/Metal.h>
19#import "IGraphicsMac_view.h"
25using namespace igraphics;
27static int MacKeyCodeToVK(
int code)
31 case 51:
return kVK_BACK;
32 case 65:
return kVK_DECIMAL;
33 case 67:
return kVK_MULTIPLY;
34 case 69:
return kVK_ADD;
35 case 71:
return kVK_NUMLOCK;
36 case 75:
return kVK_DIVIDE;
37 case 76:
return kVK_RETURN | 0x8000;
38 case 78:
return kVK_SUBTRACT;
39 case 81:
return kVK_SEPARATOR;
40 case 82:
return kVK_NUMPAD0;
41 case 83:
return kVK_NUMPAD1;
42 case 84:
return kVK_NUMPAD2;
43 case 85:
return kVK_NUMPAD3;
44 case 86:
return kVK_NUMPAD4;
45 case 87:
return kVK_NUMPAD5;
46 case 88:
return kVK_NUMPAD6;
47 case 89:
return kVK_NUMPAD7;
48 case 91:
return kVK_NUMPAD8;
49 case 92:
return kVK_NUMPAD9;
50 case 96:
return kVK_F5;
51 case 97:
return kVK_F6;
52 case 98:
return kVK_F7;
53 case 99:
return kVK_F3;
54 case 100:
return kVK_F8;
55 case 101:
return kVK_F9;
56 case 109:
return kVK_F10;
57 case 103:
return kVK_F11;
58 case 111:
return kVK_F12;
59 case 114:
return kVK_INSERT;
60 case 115:
return kVK_HOME;
61 case 117:
return kVK_DELETE;
62 case 116:
return kVK_PRIOR;
63 case 118:
return kVK_F4;
64 case 119:
return kVK_END;
65 case 120:
return kVK_F2;
66 case 121:
return kVK_NEXT;
67 case 122:
return kVK_F1;
68 case 123:
return kVK_LEFT;
69 case 124:
return kVK_RIGHT;
70 case 125:
return kVK_DOWN;
71 case 126:
return kVK_UP;
72 case 0x69:
return kVK_F13;
73 case 0x6B:
return kVK_F14;
74 case 0x71:
return kVK_F15;
75 case 0x6A:
return kVK_F16;
80static int MacKeyEventToVK(NSEvent* pEvent,
int& flag)
84 const NSInteger mod = [pEvent modifierFlags];
86 if (mod & NSShiftKeyMask) flag |= kFSHIFT;
87 if (mod & NSCommandKeyMask) flag |= kFCONTROL;
88 if (mod & NSAlternateKeyMask) flag |= kFALT;
89 if ((mod & NSControlKeyMask) ) flag |= kFLWIN;
91 int rawcode = [pEvent keyCode];
93 code = MacKeyCodeToVK(rawcode);
98 if (!str || ![str length]) str = [pEvent charactersIgnoringModifiers];
100 if (!str || ![str length])
104 code = 1024 + rawcode;
110 code = [str characterAtIndex:0];
111 if (code >= NSF1FunctionKey && code <= NSF24FunctionKey)
114 code += kVK_F1 - NSF1FunctionKey;
118 if (code >=
'a' && code <=
'z') code +=
'A'-
'a';
119 if (code == 25 && (flag & FSHIFT)) code = kVK_TAB;
120 if (isalnum(code) || code==
' ' || code ==
'\r' || code ==
'\n' || code ==27 || code == kVK_TAB) flag |= kFVIRTKEY;
127 if (code == 8) code =
'\b';
130 if (!(flag & kFVIRTKEY)) flag &= ~kFSHIFT;
135#ifndef IGRAPHICS_MENU_RCVR
136#warning The iPlug2 Obj-C namespace is not customized. Did you forget to include IPlugOBJCPrefix.pch?
139@implementation IGRAPHICS_MENU_RCVR
141- (NSMenuItem*) menuItem
146- (void) onMenuSelection:(
id) sender
153@implementation IGRAPHICS_MENU
155- (id) initWithIPopupMenuAndReceiver: (
IPopupMenu*) pMenu : (NSView*) pView
157 [
self initWithTitle: @""];
159 NSMenuItem* nsMenuItem = nil;
160 NSMutableString* nsMenuItemTitle = nil;
162 [
self setAutoenablesItems:NO];
164 int numItems = pMenu->NItems();
166 for (
int i = 0; i < numItems; ++i)
170 nsMenuItemTitle = [[[NSMutableString alloc] initWithUTF8String:pMenuItem->GetText()] autorelease];
172 if (pMenu->GetPrefix())
174 NSString* prefixString = 0;
176 switch (pMenu->GetPrefix())
178 case 0: prefixString = [NSString stringWithUTF8String:
""]; break;
179 case 1: prefixString = [NSString stringWithFormat:
@"%1d: ", i+1]; break;
180 case 2: prefixString = [NSString stringWithFormat:
@"%02d: ", i+1]; break;
181 case 3: prefixString = [NSString stringWithFormat:
@"%03d: ", i+1]; break;
184 [nsMenuItemTitle insertString:prefixString atIndex:0];
187 if (pMenuItem->GetIsSeparator())
189 [self addItem:[NSMenuItem separatorItem]];
191 else if (pMenuItem->GetSubmenu())
193 nsMenuItem = [self addItemWithTitle:nsMenuItemTitle action:nil keyEquivalent:
@""];
194 NSMenu* subMenu = [[IGRAPHICS_MENU alloc] initWithIPopupMenuAndReceiver:pMenuItem->GetSubmenu() :pView];
195 [self setSubmenu: subMenu forItem:nsMenuItem];
200 nsMenuItem = [self addItemWithTitle:nsMenuItemTitle action:@selector(onMenuSelection:) keyEquivalent:
@""];
202 [nsMenuItem setTarget:pView];
205 if (nsMenuItem && !pMenuItem->GetIsSeparator())
207 [nsMenuItem setIndentationLevel:pMenuItem->GetIsTitle() ? 1 : 0 ];
208 [nsMenuItem setEnabled:pMenuItem->GetEnabled() ? YES : NO];
209 [nsMenuItem setState:pMenuItem->GetChecked() ? NSOnState : NSOffState];
225@implementation IGRAPHICS_TEXTFIELD
227- (bool) becomeFirstResponder;
229 bool success = [
super becomeFirstResponder];
232 NSTextView *textField = (NSTextView*) [self currentEditor];
233 if( [textField respondsToSelector:
@selector(setInsertionPointColor:)] )
234 [textField setInsertionPointColor: [
self textColor]];
255@implementation IGRAPHICS_TEXTFIELDCELL
257- (NSRect) drawingRectForBounds: (NSRect) inRect
260 NSRect outRect = [
super drawingRectForBounds:inRect];
267 if (mIsEditingOrSelecting == NO)
270 NSSize textSize = [
self cellSize];
273 float heightDelta = outRect.size.height - textSize.height;
275 outRect.size.height -= heightDelta;
276 outRect.origin.y += (heightDelta / 2);
282- (void) selectWithFrame: (NSRect) aRect inView: (NSView*) controlView editor: (NSText*) textObj delegate: (
id) anObject start: (NSInteger) selStart length: (NSInteger) selLength
284 aRect = [
self drawingRectForBounds:aRect];
285 mIsEditingOrSelecting = YES;
286 [
super selectWithFrame:aRect inView:controlView editor:textObj delegate:anObject start:selStart length:selLength];
287 mIsEditingOrSelecting = NO;
290- (void) editWithFrame: (NSRect) aRect inView: (NSView*) controlView editor: (NSText*) textObj delegate: (
id) anObject event: (NSEvent*) theEvent
292 aRect = [
self drawingRectForBounds:aRect];
293 mIsEditingOrSelecting = YES;
294 [
super editWithFrame:aRect inView:controlView editor:textObj delegate:anObject event:theEvent];
295 mIsEditingOrSelecting = NO;
300@implementation IGRAPHICS_FORMATTER
304 [filterCharacterSet release];
308- (BOOL) isPartialStringValid:(NSString*) partialString newEditingString:(NSString**) newString errorDescription:(NSString**) error
310 if (filterCharacterSet != nil)
313 int len = (int) [partialString length];
315 for (i = 0; i < len; i++)
317 if (![filterCharacterSet characterIsMember:[partialString characterAtIndex:i]])
326 if ([partialString length] > maxLength)
332 if (maxValue && [partialString intValue] > maxValue)
340- (void) setAcceptableCharacterSet: (NSCharacterSet*) inCharacterSet
342 [inCharacterSet retain];
343 [filterCharacterSet release];
344 filterCharacterSet = inCharacterSet;
347- (void) setMaximumLength: (
int) inLength
349 maxLength = inLength;
352- (void) setMaximumValue: (
int) inValue
357- (NSString*) stringForObjectValue: (
id) anObject
359 if ([anObject isKindOfClass:[NSString
class]])
367- (BOOL) getObjectValue: (
id*) anObject forString:(NSString*) string errorDescription: (NSString **) error
369 if (anObject &&
string)
371 *anObject = [NSString stringWithString:string];
380extern StaticStorage<CoreTextFontDescriptor> sFontDescriptorCache;
382@implementation IGRAPHICS_VIEW
384- (CALayer *)makeBackingLayer
389#if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
390 return [[[CAMetalLayer alloc] init] autorelease];
392 return [[[CALayer alloc] init] autorelease];
400 mGraphics = pGraphics;
401 NSRect r = NSMakeRect(0.f, 0.f, (
float) pGraphics->WindowWidth(), (
float) pGraphics->WindowHeight());
402 self = [
super initWithFrame:r];
404 mMouseOutDuringDrag =
false;
406 self.layer.frame = r;
407 self.wantsLayer = YES;
408 self.layer.opaque = YES;
409 self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
410 [
self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
412 #if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
413 CAMetalLayer* mtlLayer = (CAMetalLayer*) self.layer;
414 [mtlLayer setPixelFormat:MTLPixelFormatBGRA8Unorm];
415 mtlLayer.device = [MTLCreateSystemDefaultDevice() autorelease];
416 mtlLayer.framebufferOnly = YES;
417 #elif defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
418 NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy;
419 #if defined IGRAPHICS_GL3
420 profile = (NSOpenGLPixelFormatAttribute)NSOpenGLProfileVersion3_2Core;
422 const NSOpenGLPixelFormatAttribute attrs[] = {
423 NSOpenGLPFAAccelerated,
424 NSOpenGLPFANoRecovery,
425 NSOpenGLPFADoubleBuffer,
426 NSOpenGLPFAAlphaSize, 8,
427 NSOpenGLPFAColorSize, 24,
428 NSOpenGLPFADepthSize, 0,
429 NSOpenGLPFAStencilSize, 8,
430 NSOpenGLPFAOpenGLProfile, profile,
431 (NSOpenGLPixelFormatAttribute) 0
433 NSOpenGLPixelFormat* pPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
434 NSOpenGLContext* pGLContext = [[NSOpenGLContext alloc] initWithFormat:pPixelFormat shareContext:nil];
440 self.pixelFormat = pPixelFormat;
441 self.openGLContext = pGLContext;
442 self.wantsBestResolutionOpenGLSurface = YES;
445 #if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
446 EGLDisplay display = eglGetPlatformDisplay(EGLenum(EGL_PLATFORM_ANGLE_ANGLE), 0, 0);
449 DBGMSG(
"eglGetPlatformDisplay() returned error %i", eglGetError());
452 if (eglInitialize(display, nil, nil) == 0) {
453 DBGMSG(
"eglInitialize() returned error %i", eglGetError());
456 const EGLint configAttribs[9] = {
462 EGLint numConfigs = 0;
463 EGLConfig configs[1];
465 if (eglChooseConfig(display, configAttribs, configs, 1, &numConfigs) == 0) {
466 DBGMSG(
"eglChooseConfig() returned error %i", eglGetError());
470 DBGMSG(
"Empty config returned in eglChooseConfig()");
473 #if defined IGRAPHICS_GLES2
474 const EGLint contextAttribs [5] = {
475 EGL_CONTEXT_MAJOR_VERSION, 2,
476 EGL_CONTEXT_MINOR_VERSION, 0,
479 #elif defined IGRAPHICS_GLES3
480 const EGLint contextAttribs [5] = {
481 EGL_CONTEXT_MAJOR_VERSION, 3,
482 EGL_CONTEXT_MINOR_VERSION, 0,
487 EGLContext context = eglCreateContext(display, configs[0],
nullptr, contextAttribs);
490 DBGMSG(
"eglCreateContext() returned error %d", eglGetError());
493 EGLSurface surface = eglCreateWindowSurface(display, configs[0], (__bridge EGLNativeWindowType) [self layer],
nullptr);
496 DBGMSG(
"eglCreateWindowSurface() returned error %d", eglGetError());
499 mEGLSurface = surface;
500 mEGLDisplay = display;
501 mEGLContext = context;
505 #if !defined IGRAPHICS_GL2 && !defined IGRAPHICS_GL3
512#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
513- (void) prepareOpenGL
515 [
super prepareOpenGL];
517 [
self activateGLContext];
521 [[
self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
527static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink,
const CVTimeStamp* now,
const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut,
void* displayLinkContext)
529 dispatch_source_t source = (dispatch_source_t) displayLinkContext;
530 dispatch_source_merge_data(source, 1);
532 return kCVReturnSuccess;
535- (void) onTimer: (NSTimer*) pTimer
542#ifdef IGRAPHICS_CVDISPLAYLINK
543 mDisplaySource = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
544 dispatch_source_set_event_handler(mDisplaySource, ^(){
547 dispatch_resume(mDisplaySource);
551 cvReturn = CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink);
553 assert(cvReturn == kCVReturnSuccess);
555 cvReturn = CVDisplayLinkSetOutputCallback(mDisplayLink, &displayLinkCallback, (
void*) mDisplaySource);
556 assert(cvReturn == kCVReturnSuccess);
558 #if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
559 CGLContextObj cglContext = [[
self openGLContext] CGLContextObj];
560 CGLPixelFormatObj cglPixelFormat = [[
self pixelFormat] CGLPixelFormatObj];
561 CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(mDisplayLink, cglContext, cglPixelFormat);
564 CGDirectDisplayID viewDisplayID =
565 (CGDirectDisplayID) [self.window.screen.deviceDescription[
@"NSScreenNumber"] unsignedIntegerValue];;
567 cvReturn = CVDisplayLinkSetCurrentCGDisplay(mDisplayLink, viewDisplayID);
569 assert(cvReturn == kCVReturnSuccess);
571 CVDisplayLinkStart(mDisplayLink);
573 double sec = 1.0 / (double) mGraphics->FPS();
574 mTimer = [NSTimer timerWithTimeInterval:sec target:self selector:@selector(onTimer:) userInfo:nil repeats:YES];
575 [[NSRunLoop currentRunLoop] addTimer: mTimer forMode: (NSString*) kCFRunLoopCommonModes];
581#ifdef IGRAPHICS_CVDISPLAYLINK
582 CVDisplayLinkStop(mDisplayLink);
583 dispatch_source_cancel(mDisplaySource);
584 CVDisplayLinkRelease(mDisplayLink);
594 if([NSColorPanel sharedColorPanelExists])
595 [[NSColorPanel sharedColorPanel] close];
597 mColorPickerFunc =
nullptr;
598 [mMoveCursor release];
599 [mTrackingArea release];
600 [[NSNotificationCenter defaultCenter] removeObserver:self];
607 return mGraphics ? YES : NO;
615- (void) viewDidChangeEffectiveAppearance
617 if (@available(macOS 10.14, *)) {
618 BOOL isDarkMode = [[[
self effectiveAppearance] name] isEqualToString: (NSAppearanceNameDarkAqua)];
619 mGraphics->OnAppearanceChanged(isDarkMode ? EUIAppearance::Dark : EUIAppearance::Light);
623- (BOOL) acceptsFirstResponder
628- (BOOL) acceptsFirstMouse: (NSEvent*) pEvent
633- (void) viewDidMoveToWindow
635 NSWindow* pWindow = [
self window];
639 [pWindow makeFirstResponder: self];
640 [pWindow setAcceptsMouseMovedEvents: YES];
642 CGFloat newScale = [pWindow backingScaleFactor];
645 mGraphics->SetScreenScale(newScale);
647 #if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
648 [[NSNotificationCenter defaultCenter] addObserver:self
649 selector:@selector(frameDidChange:)
650 name:NSViewFrameDidChangeNotification
654 #if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
655 [[NSNotificationCenter defaultCenter] addObserver:self
656 selector:@selector(frameDidChange:)
657 name:NSViewGlobalFrameDidChangeNotification
675- (void) viewDidChangeBackingProperties:(NSNotification*) pNotification
677 NSWindow* pWindow = [
self window];
682 CGFloat newScale = [pWindow backingScaleFactor];
684 mGraphics->SetPlatformContext(
nullptr);
686 if (newScale != mGraphics->GetScreenScale())
687 mGraphics->SetScreenScale(newScale);
689 self.layer.contentsScale = [[
self window] backingScaleFactor];
691#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
692 self.layer.contentsScale = 1./newScale;
693#elif defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
694 [(CAMetalLayer*)[
self layer] setDrawableSize:CGSizeMake(self.frame.size.width * newScale,
695 self.frame.size.height * newScale)];
699- (CGContextRef) getCGContextRef
701 CGContextRef pCGC = [NSGraphicsContext currentContext].CGContext;
702 return [NSGraphicsContext graphicsContextWithCGContext: pCGC flipped: YES].CGContext;
706- (void) drawRect: (NSRect) bounds
708 #if defined IGRAPHICS_CPU
711 mGraphics->SetPlatformContext([self getCGContextRef]);
713 if (mGraphics->GetPlatformContext())
717 [
self getRectsBeingDrawn:&rects count:&numRects];
720 for (
int i = 0; i < numRects; i++)
721 drawRects.Add(ToIRECT(mGraphics, &rects[i]));
723 mGraphics->Draw(drawRects);
733 if (mGraphics->IsDirty(mDirtyRects))
735 mGraphics->SetAllControlsClean();
737 #if defined IGRAPHICS_CPU
738 for (int i = 0; i < mDirtyRects.Size(); i++)
739 [self setNeedsDisplayInRect:ToNSRect(mGraphics, mDirtyRects.Get(i))];
741 IGraphics::ScopedGLContext scopedGLCtx {mGraphics};
743 mGraphics->Draw(mDirtyRects);
749- (void) getMouseXY: (NSEvent*) pEvent : (
float&) x : (
float&) y
753 NSPoint pt = [
self convertPoint:[pEvent locationInWindow] fromView:nil];
754 x = pt.x / mGraphics->GetDrawScale();
755 y = pt.y / mGraphics->GetDrawScale();
757 mGraphics->DoCursorLock(x, y, mPrevX, mPrevY);
758 mGraphics->SetTabletInput(pEvent.subtype == NSTabletPointEventSubtype);
765 [
self getMouseXY:pEvent : info.x : info.y];
766 int mods = (int) [pEvent modifierFlags];
767 info.ms =
IMouseMod(
true, (mods & NSCommandKeyMask), (mods & NSShiftKeyMask), (mods & NSControlKeyMask), (mods & NSAlternateKeyMask));
772- (
IMouseInfo) getMouseRight: (NSEvent*) pEvent
775 [
self getMouseXY:pEvent : info.x : info.y];
776 int mods = (int) [pEvent modifierFlags];
777 info.ms =
IMouseMod(
false,
true, (mods & NSShiftKeyMask), (mods & NSControlKeyMask), (mods & NSAlternateKeyMask));
782- (void) updateTrackingAreas
784 [
super updateTrackingAreas];
786 if (mTrackingArea != nil)
788 [
self removeTrackingArea:mTrackingArea];
789 [mTrackingArea release];
792 int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingEnabledDuringMouseDrag);
793 mTrackingArea = [ [NSTrackingArea alloc] initWithRect:[
self bounds] options:opts owner:self userInfo:nil];
794 [
self addTrackingArea:mTrackingArea];
797- (void) mouseEntered: (NSEvent*) pEvent
799 mMouseOutDuringDrag =
false;
803 mGraphics->OnSetCursor();
807- (void) mouseExited: (NSEvent*) pEvent
811 if (!mGraphics->ControlIsCaptured())
813 mGraphics->OnMouseOut();
817 mMouseOutDuringDrag =
true;
822- (void) mouseDown: (NSEvent*) pEvent
827 if (([pEvent clickCount] - 1) % 2)
829 mGraphics->OnMouseDblClick(info.x, info.y, info.ms);
833 std::vector<IMouseInfo> list {info};
834 mGraphics->OnMouseDown(list);
839- (void) mouseUp: (NSEvent*) pEvent
844 std::vector<IMouseInfo> list {info};
845 mGraphics->OnMouseUp(list);
847 if (mMouseOutDuringDrag)
849 mGraphics->OnMouseOut();
850 mMouseOutDuringDrag =
false;
855- (void) mouseDragged: (NSEvent*) pEvent
858 float prevX = mPrevX;
859 float prevY = mPrevY;
861 if (mGraphics && !mGraphics->IsInPlatformTextEntry())
863 info.dX = info.x - prevX;
864 info.dY = info.y - prevY;
865 std::vector<IMouseInfo> list {info};
866 mGraphics->OnMouseDrag(list);
870- (void) rightMouseDown: (NSEvent*) pEvent
872 IMouseInfo info = [
self getMouseRight:pEvent];
875 if (([pEvent clickCount] - 1) % 2)
877 mGraphics->OnMouseDblClick(info.x, info.y, info.ms);
881 std::vector<IMouseInfo> list {info};
882 mGraphics->OnMouseDown(list);
887- (void) rightMouseUp: (NSEvent*) pEvent
889 IMouseInfo info = [
self getMouseRight:pEvent];
892 std::vector<IMouseInfo> list {info};
893 mGraphics->OnMouseUp(list);
897- (void) rightMouseDragged: (NSEvent*) pEvent
900 float prevX = mPrevX;
901 float prevY = mPrevY;
902 IMouseInfo info = [
self getMouseRight:pEvent];
904 if (mGraphics && !mTextFieldView)
906 info.dX = info.x - prevX;
907 info.dY = info.y - prevY;
908 std::vector<IMouseInfo> list {info};
909 mGraphics->OnMouseDrag(list);
913- (void) mouseMoved: (NSEvent*) pEvent
917 mGraphics->OnMouseOver(info.x, info.y, info.ms);
920- (void) keyDown: (NSEvent*) pEvent
923 int code = MacKeyEventToVK(pEvent, flag);
924 NSString *s = [pEvent charactersIgnoringModifiers];
929 c = [s characterAtIndex:0];
931 if(!
static_cast<bool>(flag & kFVIRTKEY))
937 WDL_MakeUTFChar(utf8, c, 4);
939 IKeyPress keyPress {utf8, code,
static_cast<bool>(flag & kFSHIFT),
940 static_cast<bool>(flag & kFCONTROL),
941 static_cast<bool>(flag & kFALT)};
943 bool handle = mGraphics->OnKeyDown(mPrevX, mPrevY, keyPress);
947 [[
self nextResponder] keyDown:pEvent];
951- (void) keyUp: (NSEvent*) pEvent
954 int code = MacKeyEventToVK(pEvent, flag);
955 NSString *s = [pEvent charactersIgnoringModifiers];
960 c = [s characterAtIndex:0];
962 if(!
static_cast<bool>(flag & kFVIRTKEY))
968 WDL_MakeUTFChar(utf8, c, 4);
970 IKeyPress keyPress {utf8, code,
static_cast<bool>(flag & kFSHIFT),
971 static_cast<bool>(flag & kFCONTROL),
972 static_cast<bool>(flag & kFALT)};
974 bool handle = mGraphics->OnKeyUp(mPrevX, mPrevY, keyPress);
978 [[
self nextResponder] keyUp:pEvent];
982- (void) scrollWheel: (NSEvent*) pEvent
984 if (mTextFieldView) [
self endUserInput ];
986 float d = [pEvent deltaY];
988 mGraphics->OnMouseWheel(info.x, info.y, info.ms, d);
991static void MakeCursorFromName(NSCursor*& cursor,
const char *name)
994 const char* basePath =
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors/";
996 NSString* imagePath = [NSString stringWithFormat:@"%s%s/cursor.pdf", basePath, name];
997 NSString* infoPath = [NSString stringWithFormat:@"file:%s%s/info.plist", basePath, name];
998 NSImage* fileImage = [[NSImage alloc] initByReferencingFile: imagePath];
999 NSImage *cursorImage = [[NSImage alloc] initWithSize:[fileImage size]];
1000 NSDictionary* info = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:infoPath]];
1003 double hotX = [info[@"hotx-scaled"] doubleValue];
1004 double hotY = [info[@"hoty-scaled"] doubleValue];
1005 double blur = [info[@"blur"] doubleValue];
1006 double offsetX = [info[@"shadowoffsetx"] doubleValue];
1007 double offsetY = [info[@"shadowoffsety"] doubleValue];
1008 double red = [info[@"shadowcolor"][0] doubleValue];
1009 double green = [info[@"shadowcolor"][1] doubleValue];
1010 double blue = [info[@"shadowcolor"][2] doubleValue];
1011 double alpha = [info[@"shadowcolor"][3] doubleValue];
1012 CGColorRef shadowColor = CGColorCreateGenericRGB(red, green, blue, alpha);
1014 for (
int scale = 1; scale <= 4; scale++)
1017 NSAffineTransform* xform = [NSAffineTransform transform];
1018 [xform scaleBy:scale];
1019 id hints = @{ NSImageHintCTM: xform };
1020 CGImageRef rasterCGImage = [fileImage CGImageForProposedRect:NULL context:nil hints:hints];
1023 size_t width = CGImageGetWidth(rasterCGImage);
1024 size_t height = CGImageGetHeight(rasterCGImage);
1025 CGSize offset = CGSize {
static_cast<CGFloat
>(offsetX * scale),
static_cast<CGFloat
>(offsetY * scale) };
1026 CGContextRef shadowContext = CGBitmapContextCreate(NULL, width, height, CGImageGetBitsPerComponent(rasterCGImage), 0, CGImageGetColorSpace(rasterCGImage), CGImageGetBitmapInfo(rasterCGImage));
1027 CGContextSetShadowWithColor(shadowContext, offset, blur * scale, shadowColor);
1028 CGContextDrawImage(shadowContext, CGRectMake(0, 0, width, height), rasterCGImage);
1029 CGImageRef shadowCGImage = CGBitmapContextCreateImage(shadowContext);
1032 NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithCGImage:shadowCGImage];
1033 [rep setSize:[fileImage size]];
1034 [cursorImage addRepresentation:rep];
1038 CGContextRelease(shadowContext);
1039 CGImageRelease(shadowCGImage);
1043 cursor = [[NSCursor alloc] initWithImage:cursorImage hotSpot:NSMakePoint(hotX, hotY)];
1046 [cursorImage release];
1047 [fileImage release];
1048 CGColorRelease(shadowColor);
1051- (void) setMouseCursor: (ECursor) cursorType
1053 NSCursor* pCursor =
nullptr;
1055 bool helpCurrent =
false;
1056 bool helpRequested =
false;
1060 case ECursor::ARROW: pCursor = [NSCursor arrowCursor];
break;
1061 case ECursor::IBEAM: pCursor = [NSCursor IBeamCursor];
break;
1063 if ([NSCursor respondsToSelector:
@selector(busyButClickableCursor)])
1064 pCursor = [NSCursor performSelector:@selector(busyButClickableCursor)];
1066 case ECursor::CROSS: pCursor = [NSCursor crosshairCursor];
break;
1067 case ECursor::UPARROW:
1068 if ([NSCursor respondsToSelector:
@selector(_windowResizeNorthCursor)])
1069 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthCursor)];
1071 pCursor = [NSCursor resizeUpCursor];
1073 case ECursor::SIZENWSE:
1074 if ([NSCursor respondsToSelector:
@selector(_windowResizeNorthWestSouthEastCursor)])
1075 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthWestSouthEastCursor)];
1077 case ECursor::SIZENESW:
1078 if ([NSCursor respondsToSelector:
@selector(_windowResizeNorthEastSouthWestCursor)])
1079 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthEastSouthWestCursor)];
1081 case ECursor::SIZEWE:
1082 if ([NSCursor respondsToSelector:
@selector(_windowResizeEastWestCursor)])
1083 pCursor = [NSCursor performSelector:@selector(_windowResizeEastWestCursor)];
1085 pCursor = [NSCursor resizeLeftRightCursor];
1087 case ECursor::SIZENS:
1088 if ([NSCursor respondsToSelector:
@selector(_windowResizeNorthSouthCursor)])
1089 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthSouthCursor)];
1091 pCursor = [NSCursor resizeUpDownCursor];
1093 case ECursor::SIZEALL:
1096 MakeCursorFromName(mMoveCursor,
"move");
1097 pCursor = mMoveCursor;
1100 case ECursor::INO: pCursor = [NSCursor operationNotAllowedCursor];
break;
1101 case ECursor::HAND: pCursor = [NSCursor pointingHandCursor];
break;
1102 case ECursor::APPSTARTING:
1103 if ([NSCursor respondsToSelector:
@selector(busyButClickableCursor)])
1104 pCursor = [NSCursor performSelector:@selector(busyButClickableCursor)];
1107 if ([NSCursor respondsToSelector:
@selector(_helpCursor)])
1108 pCursor = [NSCursor performSelector:@selector(_helpCursor)];
1109 helpRequested =
true;
1111 default: pCursor = [NSCursor arrowCursor];
break;
1114 if ([NSCursor respondsToSelector:
@selector(helpCursorShown)])
1115 helpCurrent = [NSCursor performSelector:@selector(helpCursorShown)];
1117 if (helpCurrent && !helpRequested)
1120#pragma clang diagnostic push
1121#pragma clang diagnostic ignored "-Wobjc-method-access"
1122 [NSCursor _setHelpCursor : false];
1123#pragma clang diagnostic pop
1127 pCursor = [NSCursor arrowCursor];
1132- (void) removeFromSuperview
1135 [
self endUserInput ];
1137 mGraphics->SetPlatformContext(
nullptr);
1140 mGraphics->GetDelegate()->CloseWindow();
1141 [
super removeFromSuperview];
1144- (void) controlTextDidEndEditing: (NSNotification*) aNotification
1146 char* txt = (
char*)[[mTextFieldView stringValue] UTF8String];
1148 mGraphics->SetControlValueAfterTextEdit(txt);
1149 mGraphics->SetAllControlsDirty();
1151 [
self endUserInput ];
1156 IGRAPHICS_MENU_RCVR* pDummyView = [[[IGRAPHICS_MENU_RCVR alloc] initWithFrame:bounds] autorelease];
1157 NSMenu* pNSMenu = [[[IGRAPHICS_MENU alloc] initWithIPopupMenuAndReceiver:&menu : pDummyView] autorelease];
1158 NSPoint wp = {bounds.origin.x, bounds.origin.y + bounds.size.height + 4};
1160 NSMenuItem* pSelectedItem = nil;
1162 auto selectedItemIdx = menu.GetChosenItemIdx();
1164 if (selectedItemIdx > -1)
1166 pSelectedItem = [pNSMenu itemAtIndex:selectedItemIdx];
1169 if (pSelectedItem != nil)
1171 wp = {bounds.origin.x, bounds.origin.y};
1174 [pNSMenu popUpMenuPositioningItem:pSelectedItem atLocation:wp inView:self];
1176 NSMenuItem* pChosenItem = [pDummyView menuItem];
1177 NSMenu* pChosenMenu = [pChosenItem menu];
1178 IPopupMenu* pIPopupMenu = [(IGRAPHICS_MENU*) pChosenMenu iPopupMenu];
1180 long chosenItemIdx = [pChosenMenu indexOfItem: pChosenItem];
1182 if (chosenItemIdx > -1 && pIPopupMenu)
1184 pIPopupMenu->SetChosenItemIdx((
int) chosenItemIdx);
1191- (void) createTextEntry: (
int) paramIdx : (const
IText&) text : (const
char*) str : (
int) length : (NSRect) areaRect;
1196 mTextFieldView = [[IGRAPHICS_TEXTFIELD alloc] initWithFrame: areaRect];
1198 if (text.mVAlign == EVAlign::Middle)
1200 IGRAPHICS_TEXTFIELDCELL* pCell = [[IGRAPHICS_TEXTFIELDCELL alloc] initTextCell:@"textfield"];
1201 [mTextFieldView setCell: pCell];
1202 [mTextFieldView setEditable: TRUE];
1203 [mTextFieldView setDrawsBackground: TRUE];
1206 CoreTextFontDescriptor* CTFontDescriptor = CoreTextHelpers::GetCTFontDescriptor(text, sFontDescriptorCache);
1207 double ratio = CTFontDescriptor->GetEMRatio() * mGraphics->GetDrawScale();
1208 NSFontDescriptor* fontDescriptor = (NSFontDescriptor*) CTFontDescriptor->GetDescriptor();
1209 NSFont* font = [NSFont fontWithDescriptor: fontDescriptor size: text.mSize * ratio];
1210 [mTextFieldView setFont: font];
1212 switch (text.mAlign)
1215 [mTextFieldView setAlignment: NSLeftTextAlignment];
1217 case EAlign::Center:
1218 [mTextFieldView setAlignment: NSCenterTextAlignment];
1221 [mTextFieldView setAlignment: NSRightTextAlignment];
1227 const IParam* pParam = paramIdx > kNoParameter ? mGraphics->GetDelegate()->GetParam(paramIdx) :
nullptr;
1232 NSMutableCharacterSet *characterSet = [[NSMutableCharacterSet alloc] init];
1234 switch ( pParam->
Type() )
1236 case IParam::kTypeEnum:
1237 case IParam::kTypeInt:
1238 case IParam::kTypeBool:
1239 [characterSet addCharactersInString:@"0123456789-+"];
1241 case IParam::kTypeDouble:
1242 [characterSet addCharactersInString:@"0123456789.-+"];
1248 [mTextFieldView setFormatter:[[[IGRAPHICS_FORMATTER alloc] init] autorelease]];
1249 [[mTextFieldView formatter] setAcceptableCharacterSet:characterSet];
1250 [[mTextFieldView formatter] setMaximumLength:length];
1251 [characterSet release];
1254 [[mTextFieldView cell] setLineBreakMode: NSLineBreakByTruncatingTail];
1255 [mTextFieldView setAllowsEditingTextAttributes:NO];
1256 [mTextFieldView setTextColor:ToNSColor(text.mTextEntryFGColor)];
1257 [mTextFieldView setBackgroundColor:ToNSColor(text.mTextEntryBGColor)];
1259 [mTextFieldView setStringValue: [NSString stringWithUTF8String:str]];
1261#ifndef COCOA_TEXTENTRY_BORDERED
1262 [mTextFieldView setBordered: NO];
1263 [mTextFieldView setFocusRingType:NSFocusRingTypeNone];
1266 [mTextFieldView setDelegate: self];
1268 [
self addSubview: mTextFieldView];
1269 NSWindow* pWindow = [
self window];
1270 [pWindow makeKeyAndOrderFront:nil];
1271 [pWindow makeFirstResponder: mTextFieldView];
1274- (void) endUserInput
1276 [mTextFieldView setDelegate: nil];
1277 [mTextFieldView removeFromSuperview];
1279 NSWindow* pWindow = [
self window];
1280 [pWindow makeFirstResponder: self];
1282 mTextFieldView =
nullptr;
1283 mGraphics->ClearInTextEntryControl();
1286- (BOOL) promptForColor: (
IColor&) color : (IColorPickerHandlerFunc) func;
1288 NSColorPanel* colorPanel = [NSColorPanel sharedColorPanel];
1289 mColorPickerFunc = func;
1291 [colorPanel setTarget:self];
1292 [colorPanel setShowsAlpha: TRUE];
1293 [colorPanel setAction:@selector(onColorPicked:)];
1294 [colorPanel setColor:ToNSColor(color)];
1295 [colorPanel orderFront:nil];
1297 return colorPanel != nil;
1300- (void) onColorPicked: (NSColorPanel*) pColorPanel
1302 mColorPickerFunc(FromNSColor([pColorPanel color]));
1305- (NSString*) view: (NSView*) pView stringForToolTip: (NSToolTipTag) tag point: (NSPoint) point userData: (
void*) pData
1307 int c = mGraphics ? GetMouseOver(mGraphics) : -1;
1308 if (c < 0)
return @"";
1310 const char* tooltip = mGraphics->GetControl(c)->GetTooltip();
1311 return CStringHasContents(tooltip) ? [NSString stringWithUTF8String:tooltip] :
@"";
1314- (void) registerToolTip: (
IRECT&) bounds
1316 [
self addToolTipRect: ToNSRect(mGraphics, bounds) owner: self userData: nil];
1319- (NSDragOperation) draggingEntered: (
id<NSDraggingInfo>) sender
1321 NSPasteboard *pPasteBoard = [sender draggingPasteboard];
1323 if ([[pPasteBoard types] containsObject:NSFilenamesPboardType])
1324 return NSDragOperationGeneric;
1326 return NSDragOperationNone;
1329- (BOOL) performDragOperation: (
id<NSDraggingInfo>) sender
1331 NSPasteboard* pPasteBoard = [sender draggingPasteboard];
1333 if ([[pPasteBoard types] containsObject:NSFilenamesPboardType])
1335 NSArray* pFiles = [pPasteBoard propertyListForType:NSFilenamesPboardType];
1336 NSPoint point = [sender draggingLocation];
1337 NSPoint relativePoint = [
self convertPoint: point fromView:nil];
1339 const float scale = mGraphics->GetDrawScale();
1340 const float x = relativePoint.x / scale;
1341 const float y = relativePoint.y / scale;
1342 if ([pFiles count] == 1)
1344 NSString* pFirstFile = [pFiles firstObject];
1345 mGraphics->OnDrop([pFirstFile UTF8String], x, y);
1347 else if ([pFiles count] > 1)
1349 std::vector<const char*> paths([pFiles count]);
1350 for (
auto i = 0; i < [pFiles count]; i++)
1352 NSString* pFile = [pFiles objectAtIndex: i];
1353 paths[i] = [pFile UTF8String];
1355 mGraphics->OnDropMultiple(paths, x, y);
1361- (NSDragOperation)draggingSession:(NSDraggingSession*) session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
1363 return NSDragOperationCopy;
1366#if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1367- (void) frameDidChange:(NSNotification*) pNotification
1369 CGFloat scale = [[
self window] backingScaleFactor];
1371 [(CAMetalLayer*)[
self layer] setDrawableSize:CGSizeMake(self.frame.size.width * scale,
1372 self.frame.size.height * scale)];
1375#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1376- (void) frameDidChange:(NSNotification*) pNotification
1378 [
self activateGLContext];
1419- (void) activateGLContext
1421#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1422 [[
self openGLContext] makeCurrentContext];
1425#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1426 if (mEGLDisplay != EGL_NO_DISPLAY && mEGLContext != EGL_NO_CONTEXT)
1427 eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
1431- (void) deactivateGLContext
1433#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1434 [NSOpenGLContext clearCurrentContext];
1437#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1438 if (mEGLDisplay != EGL_NO_DISPLAY)
1439 eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1445#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1446 [[
self openGLContext] flushBuffer];
1449#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1450 if (mEGLDisplay != EGL_NO_DISPLAY && mEGLSurface != EGL_NO_SURFACE)
1451 eglSwapBuffers(mEGLDisplay, mEGLSurface);
1455#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1456- (EGLDisplay) getEGLDisplay
1461- (EGLSurface) getEGLSurface
1466- (EGLContext) getEGLContext
This file contains the base IControl implementation, along with some base classes for specific types ...
IPlug logging a.k.a tracing functionality.
IGraphics platform class for macOS.
EParamType Type() const
Get the parameter's type.
Used to manage a list of rectangular areas and optimize them for drawing to the screen.
Used to group mouse coordinates with mouse modifier information.
Used to manage color data, independent of draw class/platform.
Used for key press info, such as ASCII representation, virtual key (mapped to win32 codes) and modifi...
Used to manage mouse modifiers i.e.
Used to manage a rectangular area, independent of draw class/platform.
IText is used to manage font and text/text entry style for a piece of text on the UI,...