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
387#if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
388 return [[CAMetalLayer alloc] init];
390 return [[CALayer alloc] init];
398 mGraphics = pGraphics;
399 NSRect r = NSMakeRect(0.f, 0.f, (
float) pGraphics->WindowWidth(), (
float) pGraphics->WindowHeight());
400 self = [
super initWithFrame:r];
402 mMouseOutDuringDrag =
false;
404 self.layer.frame = r;
405 self.wantsLayer = YES;
406 self.layer.opaque = YES;
407 self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
408 [
self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
410 #if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
411 CAMetalLayer* mtlLayer = (CAMetalLayer*) self.layer;
412 [mtlLayer setPixelFormat:MTLPixelFormatBGRA8Unorm];
413 mtlLayer.device = MTLCreateSystemDefaultDevice();
414 mtlLayer.framebufferOnly = YES;
415 #elif defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
416 NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy;
417 #if defined IGRAPHICS_GL3
418 profile = (NSOpenGLPixelFormatAttribute)NSOpenGLProfileVersion3_2Core;
420 const NSOpenGLPixelFormatAttribute attrs[] = {
421 NSOpenGLPFAAccelerated,
422 NSOpenGLPFANoRecovery,
423 NSOpenGLPFADoubleBuffer,
424 NSOpenGLPFAAlphaSize, 8,
425 NSOpenGLPFAColorSize, 24,
426 NSOpenGLPFADepthSize, 0,
427 NSOpenGLPFAStencilSize, 8,
428 NSOpenGLPFAOpenGLProfile, profile,
429 (NSOpenGLPixelFormatAttribute) 0
431 NSOpenGLPixelFormat* pPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
432 NSOpenGLContext* pGLContext = [[NSOpenGLContext alloc] initWithFormat:pPixelFormat shareContext:nil];
438 self.pixelFormat = pPixelFormat;
439 self.openGLContext = pGLContext;
440 self.wantsBestResolutionOpenGLSurface = YES;
443 #if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
444 EGLDisplay display = eglGetPlatformDisplay(EGLenum(EGL_PLATFORM_ANGLE_ANGLE), 0, 0);
447 DBGMSG(
"eglGetPlatformDisplay() returned error %i", eglGetError());
450 if (eglInitialize(display, nil, nil) == 0) {
451 DBGMSG(
"eglInitialize() returned error %i", eglGetError());
454 const EGLint configAttribs[9] = {
460 EGLint numConfigs = 0;
461 EGLConfig configs[1];
463 if (eglChooseConfig(display, configAttribs, configs, 1, &numConfigs) == 0) {
464 DBGMSG(
"eglChooseConfig() returned error %i", eglGetError());
468 DBGMSG(
"Empty config returned in eglChooseConfig()");
471 #if defined IGRAPHICS_GLES2
472 const EGLint contextAttribs [5] = {
473 EGL_CONTEXT_MAJOR_VERSION, 2,
474 EGL_CONTEXT_MINOR_VERSION, 0,
477 #elif defined IGRAPHICS_GLES3
478 const EGLint contextAttribs [5] = {
479 EGL_CONTEXT_MAJOR_VERSION, 3,
480 EGL_CONTEXT_MINOR_VERSION, 0,
485 EGLContext context = eglCreateContext(display, configs[0],
nullptr, contextAttribs);
488 DBGMSG(
"eglCreateContext() returned error %d", eglGetError());
491 EGLSurface surface = eglCreateWindowSurface(display, configs[0], (__bridge EGLNativeWindowType) [self layer],
nullptr);
494 DBGMSG(
"eglCreateWindowSurface() returned error %d", eglGetError());
497 mEGLSurface = surface;
498 mEGLDisplay = display;
499 mEGLContext = context;
503 #if !defined IGRAPHICS_GL2 && !defined IGRAPHICS_GL3
510#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
511- (void) prepareOpenGL
513 [
super prepareOpenGL];
515 [
self activateGLContext];
519 [[
self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
525static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink,
const CVTimeStamp* now,
const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut,
void* displayLinkContext)
527 dispatch_source_t source = (dispatch_source_t) displayLinkContext;
528 dispatch_source_merge_data(source, 1);
530 return kCVReturnSuccess;
533- (void) onTimer: (NSTimer*) pTimer
540#ifdef IGRAPHICS_CVDISPLAYLINK
541 mDisplaySource = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
542 dispatch_source_set_event_handler(mDisplaySource, ^(){
545 dispatch_resume(mDisplaySource);
549 cvReturn = CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink);
551 assert(cvReturn == kCVReturnSuccess);
553 cvReturn = CVDisplayLinkSetOutputCallback(mDisplayLink, &displayLinkCallback, (
void*) mDisplaySource);
554 assert(cvReturn == kCVReturnSuccess);
556 #if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
557 CGLContextObj cglContext = [[
self openGLContext] CGLContextObj];
558 CGLPixelFormatObj cglPixelFormat = [[
self pixelFormat] CGLPixelFormatObj];
559 CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(mDisplayLink, cglContext, cglPixelFormat);
562 CGDirectDisplayID viewDisplayID =
563 (CGDirectDisplayID) [self.window.screen.deviceDescription[
@"NSScreenNumber"] unsignedIntegerValue];;
565 cvReturn = CVDisplayLinkSetCurrentCGDisplay(mDisplayLink, viewDisplayID);
567 assert(cvReturn == kCVReturnSuccess);
569 CVDisplayLinkStart(mDisplayLink);
571 double sec = 1.0 / (double) mGraphics->FPS();
572 mTimer = [NSTimer timerWithTimeInterval:sec target:self selector:@selector(onTimer:) userInfo:nil repeats:YES];
573 [[NSRunLoop currentRunLoop] addTimer: mTimer forMode: (NSString*) kCFRunLoopCommonModes];
579#ifdef IGRAPHICS_CVDISPLAYLINK
580 CVDisplayLinkStop(mDisplayLink);
581 dispatch_source_cancel(mDisplaySource);
582 CVDisplayLinkRelease(mDisplayLink);
592 if([NSColorPanel sharedColorPanelExists])
593 [[NSColorPanel sharedColorPanel] close];
595 mColorPickerFunc =
nullptr;
596 [mMoveCursor release];
597 [mTrackingArea release];
598 [[NSNotificationCenter defaultCenter] removeObserver:self];
605 return mGraphics ? YES : NO;
613- (void) viewDidChangeEffectiveAppearance
615 if (@available(macOS 10.14, *)) {
616 BOOL isDarkMode = [[[
self effectiveAppearance] name] isEqualToString: (NSAppearanceNameDarkAqua)];
617 mGraphics->OnAppearanceChanged(isDarkMode ? EUIAppearance::Dark : EUIAppearance::Light);
621- (BOOL) acceptsFirstResponder
626- (BOOL) acceptsFirstMouse: (NSEvent*) pEvent
631- (void) viewDidMoveToWindow
633 NSWindow* pWindow = [
self window];
637 [pWindow makeFirstResponder: self];
638 [pWindow setAcceptsMouseMovedEvents: YES];
640 CGFloat newScale = [pWindow backingScaleFactor];
643 mGraphics->SetScreenScale(newScale);
645 #if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
646 [[NSNotificationCenter defaultCenter] addObserver:self
647 selector:@selector(frameDidChange:)
648 name:NSViewFrameDidChangeNotification
652 #if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
653 [[NSNotificationCenter defaultCenter] addObserver:self
654 selector:@selector(frameDidChange:)
655 name:NSViewGlobalFrameDidChangeNotification
673- (void) viewDidChangeBackingProperties:(NSNotification*) pNotification
675 NSWindow* pWindow = [
self window];
680 CGFloat newScale = [pWindow backingScaleFactor];
682 mGraphics->SetPlatformContext(
nullptr);
684 if (newScale != mGraphics->GetScreenScale())
685 mGraphics->SetScreenScale(newScale);
687 self.layer.contentsScale = [[
self window] backingScaleFactor];
689#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
690 self.layer.contentsScale = 1./newScale;
691#elif defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
692 [(CAMetalLayer*)[
self layer] setDrawableSize:CGSizeMake(self.frame.size.width * newScale,
693 self.frame.size.height * newScale)];
697- (CGContextRef) getCGContextRef
699 CGContextRef pCGC = [NSGraphicsContext currentContext].CGContext;
700 return [NSGraphicsContext graphicsContextWithCGContext: pCGC flipped: YES].CGContext;
704- (void) drawRect: (NSRect) bounds
706 #if defined IGRAPHICS_CPU
709 mGraphics->SetPlatformContext([self getCGContextRef]);
711 if (mGraphics->GetPlatformContext())
715 [
self getRectsBeingDrawn:&rects count:&numRects];
718 for (
int i = 0; i < numRects; i++)
719 drawRects.Add(ToIRECT(mGraphics, &rects[i]));
721 mGraphics->Draw(drawRects);
731 if (mGraphics->IsDirty(mDirtyRects))
733 mGraphics->SetAllControlsClean();
735 #if defined IGRAPHICS_CPU
736 for (int i = 0; i < mDirtyRects.Size(); i++)
737 [self setNeedsDisplayInRect:ToNSRect(mGraphics, mDirtyRects.Get(i))];
739 IGraphics::ScopedGLContext scopedGLCtx {mGraphics};
741 mGraphics->Draw(mDirtyRects);
747- (void) getMouseXY: (NSEvent*) pEvent : (
float&) x : (
float&) y
751 NSPoint pt = [
self convertPoint:[pEvent locationInWindow] fromView:nil];
752 x = pt.x / mGraphics->GetDrawScale();
753 y = pt.y / mGraphics->GetDrawScale();
755 mGraphics->DoCursorLock(x, y, mPrevX, mPrevY);
756 mGraphics->SetTabletInput(pEvent.subtype == NSTabletPointEventSubtype);
763 [
self getMouseXY:pEvent : info.x : info.y];
764 int mods = (int) [pEvent modifierFlags];
765 info.ms =
IMouseMod(
true, (mods & NSCommandKeyMask), (mods & NSShiftKeyMask), (mods & NSControlKeyMask), (mods & NSAlternateKeyMask));
770- (
IMouseInfo) getMouseRight: (NSEvent*) pEvent
773 [
self getMouseXY:pEvent : info.x : info.y];
774 int mods = (int) [pEvent modifierFlags];
775 info.ms =
IMouseMod(
false,
true, (mods & NSShiftKeyMask), (mods & NSControlKeyMask), (mods & NSAlternateKeyMask));
780- (void) updateTrackingAreas
782 [
super updateTrackingAreas];
784 if (mTrackingArea != nil)
786 [
self removeTrackingArea:mTrackingArea];
787 [mTrackingArea release];
790 int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingEnabledDuringMouseDrag);
791 mTrackingArea = [ [NSTrackingArea alloc] initWithRect:[
self bounds] options:opts owner:self userInfo:nil];
792 [
self addTrackingArea:mTrackingArea];
795- (void) mouseEntered: (NSEvent*) pEvent
797 mMouseOutDuringDrag =
false;
801 mGraphics->OnSetCursor();
805- (void) mouseExited: (NSEvent*) pEvent
809 if (!mGraphics->ControlIsCaptured())
811 mGraphics->OnMouseOut();
815 mMouseOutDuringDrag =
true;
820- (void) mouseDown: (NSEvent*) pEvent
825 if (([pEvent clickCount] - 1) % 2)
827 mGraphics->OnMouseDblClick(info.x, info.y, info.ms);
831 std::vector<IMouseInfo> list {info};
832 mGraphics->OnMouseDown(list);
837- (void) mouseUp: (NSEvent*) pEvent
842 std::vector<IMouseInfo> list {info};
843 mGraphics->OnMouseUp(list);
845 if (mMouseOutDuringDrag)
847 mGraphics->OnMouseOut();
848 mMouseOutDuringDrag =
false;
853- (void) mouseDragged: (NSEvent*) pEvent
856 float prevX = mPrevX;
857 float prevY = mPrevY;
859 if (mGraphics && !mGraphics->IsInPlatformTextEntry())
861 info.dX = info.x - prevX;
862 info.dY = info.y - prevY;
863 std::vector<IMouseInfo> list {info};
864 mGraphics->OnMouseDrag(list);
868- (void) rightMouseDown: (NSEvent*) pEvent
870 IMouseInfo info = [
self getMouseRight:pEvent];
873 if (([pEvent clickCount] - 1) % 2)
875 mGraphics->OnMouseDblClick(info.x, info.y, info.ms);
879 std::vector<IMouseInfo> list {info};
880 mGraphics->OnMouseDown(list);
885- (void) rightMouseUp: (NSEvent*) pEvent
887 IMouseInfo info = [
self getMouseRight:pEvent];
890 std::vector<IMouseInfo> list {info};
891 mGraphics->OnMouseUp(list);
895- (void) rightMouseDragged: (NSEvent*) pEvent
898 float prevX = mPrevX;
899 float prevY = mPrevY;
900 IMouseInfo info = [
self getMouseRight:pEvent];
902 if (mGraphics && !mTextFieldView)
904 info.dX = info.x - prevX;
905 info.dY = info.y - prevY;
906 std::vector<IMouseInfo> list {info};
907 mGraphics->OnMouseDrag(list);
911- (void) mouseMoved: (NSEvent*) pEvent
915 mGraphics->OnMouseOver(info.x, info.y, info.ms);
918- (void) keyDown: (NSEvent*) pEvent
921 int code = MacKeyEventToVK(pEvent, flag);
922 NSString *s = [pEvent charactersIgnoringModifiers];
927 c = [s characterAtIndex:0];
929 if(!
static_cast<bool>(flag & kFVIRTKEY))
935 WDL_MakeUTFChar(utf8, c, 4);
937 IKeyPress keyPress {utf8, code,
static_cast<bool>(flag & kFSHIFT),
938 static_cast<bool>(flag & kFCONTROL),
939 static_cast<bool>(flag & kFALT)};
941 bool handle = mGraphics->OnKeyDown(mPrevX, mPrevY, keyPress);
945 [[
self nextResponder] keyDown:pEvent];
949- (void) keyUp: (NSEvent*) pEvent
952 int code = MacKeyEventToVK(pEvent, flag);
953 NSString *s = [pEvent charactersIgnoringModifiers];
958 c = [s characterAtIndex:0];
960 if(!
static_cast<bool>(flag & kFVIRTKEY))
966 WDL_MakeUTFChar(utf8, c, 4);
968 IKeyPress keyPress {utf8, code,
static_cast<bool>(flag & kFSHIFT),
969 static_cast<bool>(flag & kFCONTROL),
970 static_cast<bool>(flag & kFALT)};
972 bool handle = mGraphics->OnKeyUp(mPrevX, mPrevY, keyPress);
976 [[
self nextResponder] keyUp:pEvent];
980- (void) scrollWheel: (NSEvent*) pEvent
982 if (mTextFieldView) [
self endUserInput ];
984 float d = [pEvent deltaY];
986 mGraphics->OnMouseWheel(info.x, info.y, info.ms, d);
989static void MakeCursorFromName(NSCursor*& cursor,
const char *name)
992 const char* basePath =
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors/";
994 NSString* imagePath = [NSString stringWithFormat:@"%s%s/cursor.pdf", basePath, name];
995 NSString* infoPath = [NSString stringWithFormat:@"file:%s%s/info.plist", basePath, name];
996 NSImage* fileImage = [[NSImage alloc] initByReferencingFile: imagePath];
997 NSImage *cursorImage = [[NSImage alloc] initWithSize:[fileImage size]];
998 NSDictionary* info = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:infoPath]];
1001 double hotX = [info[@"hotx-scaled"] doubleValue];
1002 double hotY = [info[@"hoty-scaled"] doubleValue];
1003 double blur = [info[@"blur"] doubleValue];
1004 double offsetX = [info[@"shadowoffsetx"] doubleValue];
1005 double offsetY = [info[@"shadowoffsety"] doubleValue];
1006 double red = [info[@"shadowcolor"][0] doubleValue];
1007 double green = [info[@"shadowcolor"][1] doubleValue];
1008 double blue = [info[@"shadowcolor"][2] doubleValue];
1009 double alpha = [info[@"shadowcolor"][3] doubleValue];
1010 CGColorRef shadowColor = CGColorCreateGenericRGB(red, green, blue, alpha);
1012 for (
int scale = 1; scale <= 4; scale++)
1015 NSAffineTransform* xform = [NSAffineTransform transform];
1016 [xform scaleBy:scale];
1017 id hints = @{ NSImageHintCTM: xform };
1018 CGImageRef rasterCGImage = [fileImage CGImageForProposedRect:NULL context:nil hints:hints];
1021 size_t width = CGImageGetWidth(rasterCGImage);
1022 size_t height = CGImageGetHeight(rasterCGImage);
1023 CGSize offset = CGSize {
static_cast<CGFloat
>(offsetX * scale),
static_cast<CGFloat
>(offsetY * scale) };
1024 CGContextRef shadowContext = CGBitmapContextCreate(NULL, width, height, CGImageGetBitsPerComponent(rasterCGImage), 0, CGImageGetColorSpace(rasterCGImage), CGImageGetBitmapInfo(rasterCGImage));
1025 CGContextSetShadowWithColor(shadowContext, offset, blur * scale, shadowColor);
1026 CGContextDrawImage(shadowContext, CGRectMake(0, 0, width, height), rasterCGImage);
1027 CGImageRef shadowCGImage = CGBitmapContextCreateImage(shadowContext);
1030 NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithCGImage:shadowCGImage];
1031 [rep setSize:[fileImage size]];
1032 [cursorImage addRepresentation:rep];
1036 CGContextRelease(shadowContext);
1037 CGImageRelease(shadowCGImage);
1041 cursor = [[NSCursor alloc] initWithImage:cursorImage hotSpot:NSMakePoint(hotX, hotY)];
1044 [cursorImage release];
1045 [fileImage release];
1046 CGColorRelease(shadowColor);
1049- (void) setMouseCursor: (ECursor) cursorType
1051 NSCursor* pCursor =
nullptr;
1053 bool helpCurrent =
false;
1054 bool helpRequested =
false;
1058 case ECursor::ARROW: pCursor = [NSCursor arrowCursor];
break;
1059 case ECursor::IBEAM: pCursor = [NSCursor IBeamCursor];
break;
1061 if ([NSCursor respondsToSelector:
@selector(busyButClickableCursor)])
1062 pCursor = [NSCursor performSelector:@selector(busyButClickableCursor)];
1064 case ECursor::CROSS: pCursor = [NSCursor crosshairCursor];
break;
1065 case ECursor::UPARROW:
1066 if ([NSCursor respondsToSelector:
@selector(_windowResizeNorthCursor)])
1067 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthCursor)];
1069 pCursor = [NSCursor resizeUpCursor];
1071 case ECursor::SIZENWSE:
1072 if ([NSCursor respondsToSelector:
@selector(_windowResizeNorthWestSouthEastCursor)])
1073 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthWestSouthEastCursor)];
1075 case ECursor::SIZENESW:
1076 if ([NSCursor respondsToSelector:
@selector(_windowResizeNorthEastSouthWestCursor)])
1077 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthEastSouthWestCursor)];
1079 case ECursor::SIZEWE:
1080 if ([NSCursor respondsToSelector:
@selector(_windowResizeEastWestCursor)])
1081 pCursor = [NSCursor performSelector:@selector(_windowResizeEastWestCursor)];
1083 pCursor = [NSCursor resizeLeftRightCursor];
1085 case ECursor::SIZENS:
1086 if ([NSCursor respondsToSelector:
@selector(_windowResizeNorthSouthCursor)])
1087 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthSouthCursor)];
1089 pCursor = [NSCursor resizeUpDownCursor];
1091 case ECursor::SIZEALL:
1094 MakeCursorFromName(mMoveCursor,
"move");
1095 pCursor = mMoveCursor;
1098 case ECursor::INO: pCursor = [NSCursor operationNotAllowedCursor];
break;
1099 case ECursor::HAND: pCursor = [NSCursor pointingHandCursor];
break;
1100 case ECursor::APPSTARTING:
1101 if ([NSCursor respondsToSelector:
@selector(busyButClickableCursor)])
1102 pCursor = [NSCursor performSelector:@selector(busyButClickableCursor)];
1105 if ([NSCursor respondsToSelector:
@selector(_helpCursor)])
1106 pCursor = [NSCursor performSelector:@selector(_helpCursor)];
1107 helpRequested =
true;
1109 default: pCursor = [NSCursor arrowCursor];
break;
1112 if ([NSCursor respondsToSelector:
@selector(helpCursorShown)])
1113 helpCurrent = [NSCursor performSelector:@selector(helpCursorShown)];
1115 if (helpCurrent && !helpRequested)
1118#pragma clang diagnostic push
1119#pragma clang diagnostic ignored "-Wobjc-method-access"
1120 [NSCursor _setHelpCursor : false];
1121#pragma clang diagnostic pop
1125 pCursor = [NSCursor arrowCursor];
1130- (void) removeFromSuperview
1133 [
self endUserInput ];
1135 mGraphics->SetPlatformContext(
nullptr);
1138 mGraphics->GetDelegate()->CloseWindow();
1139 [
super removeFromSuperview];
1142- (void) controlTextDidEndEditing: (NSNotification*) aNotification
1144 char* txt = (
char*)[[mTextFieldView stringValue] UTF8String];
1146 mGraphics->SetControlValueAfterTextEdit(txt);
1147 mGraphics->SetAllControlsDirty();
1149 [
self endUserInput ];
1154 IGRAPHICS_MENU_RCVR* pDummyView = [[[IGRAPHICS_MENU_RCVR alloc] initWithFrame:bounds] autorelease];
1155 NSMenu* pNSMenu = [[[IGRAPHICS_MENU alloc] initWithIPopupMenuAndReceiver:&menu : pDummyView] autorelease];
1156 NSPoint wp = {bounds.origin.x, bounds.origin.y + bounds.size.height + 4};
1158 NSMenuItem* pSelectedItem = nil;
1160 auto selectedItemIdx = menu.GetChosenItemIdx();
1162 if (selectedItemIdx > -1)
1164 pSelectedItem = [pNSMenu itemAtIndex:selectedItemIdx];
1167 if (pSelectedItem != nil)
1169 wp = {bounds.origin.x, bounds.origin.y};
1172 [pNSMenu popUpMenuPositioningItem:pSelectedItem atLocation:wp inView:self];
1174 NSMenuItem* pChosenItem = [pDummyView menuItem];
1175 NSMenu* pChosenMenu = [pChosenItem menu];
1176 IPopupMenu* pIPopupMenu = [(IGRAPHICS_MENU*) pChosenMenu iPopupMenu];
1178 long chosenItemIdx = [pChosenMenu indexOfItem: pChosenItem];
1180 if (chosenItemIdx > -1 && pIPopupMenu)
1182 pIPopupMenu->SetChosenItemIdx((
int) chosenItemIdx);
1189- (void) createTextEntry: (
int) paramIdx : (const
IText&) text : (const
char*) str : (
int) length : (NSRect) areaRect;
1194 mTextFieldView = [[IGRAPHICS_TEXTFIELD alloc] initWithFrame: areaRect];
1196 if (text.mVAlign == EVAlign::Middle)
1198 IGRAPHICS_TEXTFIELDCELL* pCell = [[IGRAPHICS_TEXTFIELDCELL alloc] initTextCell:@"textfield"];
1199 [mTextFieldView setCell: pCell];
1200 [mTextFieldView setEditable: TRUE];
1201 [mTextFieldView setDrawsBackground: TRUE];
1204 CoreTextFontDescriptor* CTFontDescriptor = CoreTextHelpers::GetCTFontDescriptor(text, sFontDescriptorCache);
1205 double ratio = CTFontDescriptor->GetEMRatio() * mGraphics->GetDrawScale();
1206 NSFontDescriptor* fontDescriptor = (NSFontDescriptor*) CTFontDescriptor->GetDescriptor();
1207 NSFont* font = [NSFont fontWithDescriptor: fontDescriptor size: text.mSize * ratio];
1208 [mTextFieldView setFont: font];
1210 switch (text.mAlign)
1213 [mTextFieldView setAlignment: NSLeftTextAlignment];
1215 case EAlign::Center:
1216 [mTextFieldView setAlignment: NSCenterTextAlignment];
1219 [mTextFieldView setAlignment: NSRightTextAlignment];
1225 const IParam* pParam = paramIdx > kNoParameter ? mGraphics->GetDelegate()->GetParam(paramIdx) :
nullptr;
1230 NSMutableCharacterSet *characterSet = [[NSMutableCharacterSet alloc] init];
1232 switch ( pParam->
Type() )
1234 case IParam::kTypeEnum:
1235 case IParam::kTypeInt:
1236 case IParam::kTypeBool:
1237 [characterSet addCharactersInString:@"0123456789-+"];
1239 case IParam::kTypeDouble:
1240 [characterSet addCharactersInString:@"0123456789.-+"];
1246 [mTextFieldView setFormatter:[[[IGRAPHICS_FORMATTER alloc] init] autorelease]];
1247 [[mTextFieldView formatter] setAcceptableCharacterSet:characterSet];
1248 [[mTextFieldView formatter] setMaximumLength:length];
1249 [characterSet release];
1252 [[mTextFieldView cell] setLineBreakMode: NSLineBreakByTruncatingTail];
1253 [mTextFieldView setAllowsEditingTextAttributes:NO];
1254 [mTextFieldView setTextColor:ToNSColor(text.mTextEntryFGColor)];
1255 [mTextFieldView setBackgroundColor:ToNSColor(text.mTextEntryBGColor)];
1257 [mTextFieldView setStringValue: [NSString stringWithUTF8String:str]];
1259#ifndef COCOA_TEXTENTRY_BORDERED
1260 [mTextFieldView setBordered: NO];
1261 [mTextFieldView setFocusRingType:NSFocusRingTypeNone];
1264 [mTextFieldView setDelegate: self];
1266 [
self addSubview: mTextFieldView];
1267 NSWindow* pWindow = [
self window];
1268 [pWindow makeKeyAndOrderFront:nil];
1269 [pWindow makeFirstResponder: mTextFieldView];
1272- (void) endUserInput
1274 [mTextFieldView setDelegate: nil];
1275 [mTextFieldView removeFromSuperview];
1277 NSWindow* pWindow = [
self window];
1278 [pWindow makeFirstResponder: self];
1280 mTextFieldView =
nullptr;
1281 mGraphics->ClearInTextEntryControl();
1284- (BOOL) promptForColor: (
IColor&) color : (IColorPickerHandlerFunc) func;
1286 NSColorPanel* colorPanel = [NSColorPanel sharedColorPanel];
1287 mColorPickerFunc = func;
1289 [colorPanel setTarget:self];
1290 [colorPanel setShowsAlpha: TRUE];
1291 [colorPanel setAction:@selector(onColorPicked:)];
1292 [colorPanel setColor:ToNSColor(color)];
1293 [colorPanel orderFront:nil];
1295 return colorPanel != nil;
1298- (void) onColorPicked: (NSColorPanel*) pColorPanel
1300 mColorPickerFunc(FromNSColor([pColorPanel color]));
1303- (NSString*) view: (NSView*) pView stringForToolTip: (NSToolTipTag) tag point: (NSPoint) point userData: (
void*) pData
1305 int c = mGraphics ? GetMouseOver(mGraphics) : -1;
1306 if (c < 0)
return @"";
1308 const char* tooltip = mGraphics->GetControl(c)->GetTooltip();
1309 return CStringHasContents(tooltip) ? [NSString stringWithUTF8String:tooltip] :
@"";
1312- (void) registerToolTip: (
IRECT&) bounds
1314 [
self addToolTipRect: ToNSRect(mGraphics, bounds) owner: self userData: nil];
1317- (NSDragOperation) draggingEntered: (
id<NSDraggingInfo>) sender
1319 NSPasteboard *pPasteBoard = [sender draggingPasteboard];
1321 if ([[pPasteBoard types] containsObject:NSFilenamesPboardType])
1322 return NSDragOperationGeneric;
1324 return NSDragOperationNone;
1327- (BOOL) performDragOperation: (
id<NSDraggingInfo>) sender
1329 NSPasteboard* pPasteBoard = [sender draggingPasteboard];
1331 if ([[pPasteBoard types] containsObject:NSFilenamesPboardType])
1333 NSArray* pFiles = [pPasteBoard propertyListForType:NSFilenamesPboardType];
1334 NSPoint point = [sender draggingLocation];
1335 NSPoint relativePoint = [
self convertPoint: point fromView:nil];
1337 const float scale = mGraphics->GetDrawScale();
1338 const float x = relativePoint.x / scale;
1339 const float y = relativePoint.y / scale;
1340 if ([pFiles count] == 1)
1342 NSString* pFirstFile = [pFiles firstObject];
1343 mGraphics->OnDrop([pFirstFile UTF8String], x, y);
1345 else if ([pFiles count] > 1)
1347 std::vector<const char*> paths([pFiles count]);
1348 for (
auto i = 0; i < [pFiles count]; i++)
1350 NSString* pFile = [pFiles objectAtIndex: i];
1351 paths[i] = [pFile UTF8String];
1353 mGraphics->OnDropMultiple(paths, x, y);
1359- (NSDragOperation)draggingSession:(NSDraggingSession*) session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
1361 return NSDragOperationCopy;
1364#if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1365- (void) frameDidChange:(NSNotification*) pNotification
1367 CGFloat scale = [[
self window] backingScaleFactor];
1369 [(CAMetalLayer*)[
self layer] setDrawableSize:CGSizeMake(self.frame.size.width * scale,
1370 self.frame.size.height * scale)];
1373#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1374- (void) frameDidChange:(NSNotification*) pNotification
1376 [
self activateGLContext];
1417- (void) activateGLContext
1419#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1420 [[
self openGLContext] makeCurrentContext];
1423#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1424 if (mEGLDisplay != EGL_NO_DISPLAY && mEGLContext != EGL_NO_CONTEXT)
1425 eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
1429- (void) deactivateGLContext
1431#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1432 [NSOpenGLContext clearCurrentContext];
1435#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1436 if (mEGLDisplay != EGL_NO_DISPLAY)
1437 eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1443#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1444 [[
self openGLContext] flushBuffer];
1447#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1448 if (mEGLDisplay != EGL_NO_DISPLAY && mEGLSurface != EGL_NO_SURFACE)
1449 eglSwapBuffers(mEGLDisplay, mEGLSurface);
1453#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1454- (EGLDisplay) getEGLDisplay
1459- (EGLSurface) getEGLSurface
1464- (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,...