11#import <QuartzCore/QuartzCore.h>
13#if defined IGRAPHICS_METAL
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
388 mGraphics = pGraphics;
389 NSRect r = NSMakeRect(0.f, 0.f, (
float) pGraphics->WindowWidth(), (
float) pGraphics->WindowHeight());
390 self = [
super initWithFrame:r];
392 mMouseOutDuringDrag =
false;
394 self.wantsLayer = YES;
395 self.layer.opaque = YES;
396 self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
398 [
self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
400 #if defined IGRAPHICS_METAL
401 self.layer = [CAMetalLayer new];
402 [(CAMetalLayer*)[
self layer] setPixelFormat:MTLPixelFormatBGRA8Unorm];
403 ((CAMetalLayer*) self.layer).device = MTLCreateSystemDefaultDevice();
405 #elif defined IGRAPHICS_GL
406 NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy;
407 #if defined IGRAPHICS_GL3
408 profile = (NSOpenGLPixelFormatAttribute)NSOpenGLProfileVersion3_2Core;
410 const NSOpenGLPixelFormatAttribute attrs[] = {
411 NSOpenGLPFAAccelerated,
412 NSOpenGLPFANoRecovery,
413 NSOpenGLPFADoubleBuffer,
414 NSOpenGLPFAAlphaSize, 8,
415 NSOpenGLPFAColorSize, 24,
416 NSOpenGLPFADepthSize, 0,
417 NSOpenGLPFAStencilSize, 8,
418 NSOpenGLPFAOpenGLProfile, profile,
419 (NSOpenGLPixelFormatAttribute) 0
421 NSOpenGLPixelFormat* pPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
422 NSOpenGLContext* pGLContext = [[NSOpenGLContext alloc] initWithFormat:pPixelFormat shareContext:nil];
428 self.pixelFormat = pPixelFormat;
429 self.openGLContext = pGLContext;
430 self.wantsBestResolutionOpenGLSurface = YES;
433 #if !defined IGRAPHICS_GL
441- (void) prepareOpenGL
443 [
super prepareOpenGL];
445 [
self activateGLContext];
449 [[
self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
455static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink,
const CVTimeStamp* now,
const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut,
void* displayLinkContext)
457 dispatch_source_t source = (dispatch_source_t) displayLinkContext;
458 dispatch_source_merge_data(source, 1);
460 return kCVReturnSuccess;
463- (void) onTimer: (NSTimer*) pTimer
470#ifdef IGRAPHICS_CVDISPLAYLINK
471 mDisplaySource = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
472 dispatch_source_set_event_handler(mDisplaySource, ^(){
475 dispatch_resume(mDisplaySource);
479 cvReturn = CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink);
481 assert(cvReturn == kCVReturnSuccess);
483 cvReturn = CVDisplayLinkSetOutputCallback(mDisplayLink, &displayLinkCallback, (
void*) mDisplaySource);
484 assert(cvReturn == kCVReturnSuccess);
487 CGLContextObj cglContext = [[
self openGLContext] CGLContextObj];
488 CGLPixelFormatObj cglPixelFormat = [[
self pixelFormat] CGLPixelFormatObj];
489 CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(mDisplayLink, cglContext, cglPixelFormat);
492 CGDirectDisplayID viewDisplayID =
493 (CGDirectDisplayID) [self.window.screen.deviceDescription[
@"NSScreenNumber"] unsignedIntegerValue];;
495 cvReturn = CVDisplayLinkSetCurrentCGDisplay(mDisplayLink, viewDisplayID);
497 assert(cvReturn == kCVReturnSuccess);
499 CVDisplayLinkStart(mDisplayLink);
501 double sec = 1.0 / (double) mGraphics->FPS();
502 mTimer = [NSTimer timerWithTimeInterval:sec target:self selector:@selector(onTimer:) userInfo:nil repeats:YES];
503 [[NSRunLoop currentRunLoop] addTimer: mTimer forMode: (NSString*) kCFRunLoopCommonModes];
509#ifdef IGRAPHICS_CVDISPLAYLINK
510 CVDisplayLinkStop(mDisplayLink);
511 dispatch_source_cancel(mDisplaySource);
512 CVDisplayLinkRelease(mDisplayLink);
522 if([NSColorPanel sharedColorPanelExists])
523 [[NSColorPanel sharedColorPanel] close];
525 mColorPickerFunc =
nullptr;
526 [mMoveCursor release];
527 [mTrackingArea release];
528 [[NSNotificationCenter defaultCenter] removeObserver:self];
535 return mGraphics ? YES : NO;
543- (void) viewDidChangeEffectiveAppearance
545 if (@available(macOS 10.14, *)) {
546 BOOL isDarkMode = [[[
self effectiveAppearance] name] isEqualToString: (NSAppearanceNameDarkAqua)];
547 mGraphics->OnAppearanceChanged(isDarkMode ? EUIAppearance::Dark : EUIAppearance::Light);
551- (BOOL) acceptsFirstResponder
556- (BOOL) acceptsFirstMouse: (NSEvent*) pEvent
561- (void) viewDidMoveToWindow
563 NSWindow* pWindow = [
self window];
567 [pWindow makeFirstResponder: self];
568 [pWindow setAcceptsMouseMovedEvents: YES];
570 CGFloat newScale = [pWindow backingScaleFactor];
573 mGraphics->SetScreenScale(newScale);
575 #ifdef IGRAPHICS_METAL
576 [[NSNotificationCenter defaultCenter] addObserver:self
577 selector:@selector(frameDidChange:)
578 name:NSViewFrameDidChangeNotification
582 [[NSNotificationCenter defaultCenter] addObserver:self
583 selector:@selector(frameDidChange:)
584 name:NSViewGlobalFrameDidChangeNotification
602- (void) viewDidChangeBackingProperties:(NSNotification*) pNotification
604 NSWindow* pWindow = [
self window];
609 CGFloat newScale = [pWindow backingScaleFactor];
611 mGraphics->SetPlatformContext(
nullptr);
613 if (newScale != mGraphics->GetScreenScale())
614 mGraphics->SetScreenScale(newScale);
616#if defined IGRAPHICS_GL
617 self.layer.contentsScale = 1./newScale;
618#elif defined IGRAPHICS_METAL
619 [(CAMetalLayer*)[
self layer] setDrawableSize:CGSizeMake(self.frame.size.width * newScale,
620 self.frame.size.height * newScale)];
624- (CGContextRef) getCGContextRef
626 CGContextRef pCGC = [NSGraphicsContext currentContext].CGContext;
627 return [NSGraphicsContext graphicsContextWithCGContext: pCGC flipped: YES].CGContext;
631- (void) drawRect: (NSRect) bounds
633 #if !defined IGRAPHICS_GL && !defined IGRAPHICS_METAL
636 mGraphics->SetPlatformContext([self getCGContextRef]);
638 if (mGraphics->GetPlatformContext())
642 [
self getRectsBeingDrawn:&rects count:&numRects];
645 for (
int i = 0; i < numRects; i++)
646 drawRects.Add(ToIRECT(mGraphics, &rects[i]));
648 mGraphics->Draw(drawRects);
658 if (mGraphics->IsDirty(mDirtyRects))
660 mGraphics->SetAllControlsClean();
662 #if defined IGRAPHICS_CPU
663 for (int i = 0; i < mDirtyRects.Size(); i++)
664 [self setNeedsDisplayInRect:ToNSRect(mGraphics, mDirtyRects.Get(i))];
666 IGraphics::ScopedGLContext scopedGLCtx {mGraphics};
668 mGraphics->Draw(mDirtyRects);
674- (void) getMouseXY: (NSEvent*) pEvent : (
float&) x : (
float&) y
678 NSPoint pt = [
self convertPoint:[pEvent locationInWindow] fromView:nil];
679 x = pt.x / mGraphics->GetDrawScale();
680 y = pt.y / mGraphics->GetDrawScale();
682 mGraphics->DoCursorLock(x, y, mPrevX, mPrevY);
683 mGraphics->SetTabletInput(pEvent.subtype == NSTabletPointEventSubtype);
690 [
self getMouseXY:pEvent : info.x : info.y];
691 int mods = (int) [pEvent modifierFlags];
692 info.ms =
IMouseMod(
true, (mods & NSCommandKeyMask), (mods & NSShiftKeyMask), (mods & NSControlKeyMask), (mods & NSAlternateKeyMask));
697- (
IMouseInfo) getMouseRight: (NSEvent*) pEvent
700 [
self getMouseXY:pEvent : info.x : info.y];
701 int mods = (int) [pEvent modifierFlags];
702 info.ms =
IMouseMod(
false,
true, (mods & NSShiftKeyMask), (mods & NSControlKeyMask), (mods & NSAlternateKeyMask));
707- (void) updateTrackingAreas
709 [
super updateTrackingAreas];
711 if (mTrackingArea != nil)
713 [
self removeTrackingArea:mTrackingArea];
714 [mTrackingArea release];
717 int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingEnabledDuringMouseDrag);
718 mTrackingArea = [ [NSTrackingArea alloc] initWithRect:[
self bounds] options:opts owner:self userInfo:nil];
719 [
self addTrackingArea:mTrackingArea];
722- (void) mouseEntered: (NSEvent*) pEvent
724 mMouseOutDuringDrag =
false;
728 mGraphics->OnSetCursor();
732- (void) mouseExited: (NSEvent*) pEvent
736 if (!mGraphics->ControlIsCaptured())
738 mGraphics->OnMouseOut();
742 mMouseOutDuringDrag =
true;
747- (void) mouseDown: (NSEvent*) pEvent
752 if (([pEvent clickCount] - 1) % 2)
754 mGraphics->OnMouseDblClick(info.x, info.y, info.ms);
758 std::vector<IMouseInfo> list {info};
759 mGraphics->OnMouseDown(list);
764- (void) mouseUp: (NSEvent*) pEvent
769 std::vector<IMouseInfo> list {info};
770 mGraphics->OnMouseUp(list);
772 if (mMouseOutDuringDrag)
774 mGraphics->OnMouseOut();
775 mMouseOutDuringDrag =
false;
780- (void) mouseDragged: (NSEvent*) pEvent
783 float prevX = mPrevX;
784 float prevY = mPrevY;
786 if (mGraphics && !mGraphics->IsInPlatformTextEntry())
788 info.dX = info.x - prevX;
789 info.dY = info.y - prevY;
790 std::vector<IMouseInfo> list {info};
791 mGraphics->OnMouseDrag(list);
795- (void) rightMouseDown: (NSEvent*) pEvent
797 IMouseInfo info = [
self getMouseRight:pEvent];
800 if (([pEvent clickCount] - 1) % 2)
802 mGraphics->OnMouseDblClick(info.x, info.y, info.ms);
806 std::vector<IMouseInfo> list {info};
807 mGraphics->OnMouseDown(list);
812- (void) rightMouseUp: (NSEvent*) pEvent
814 IMouseInfo info = [
self getMouseRight:pEvent];
817 std::vector<IMouseInfo> list {info};
818 mGraphics->OnMouseUp(list);
822- (void) rightMouseDragged: (NSEvent*) pEvent
825 float prevX = mPrevX;
826 float prevY = mPrevY;
827 IMouseInfo info = [
self getMouseRight:pEvent];
829 if (mGraphics && !mTextFieldView)
831 info.dX = info.x - prevX;
832 info.dY = info.y - prevY;
833 std::vector<IMouseInfo> list {info};
834 mGraphics->OnMouseDrag(list);
838- (void) mouseMoved: (NSEvent*) pEvent
842 mGraphics->OnMouseOver(info.x, info.y, info.ms);
845- (void) keyDown: (NSEvent*) pEvent
848 int code = MacKeyEventToVK(pEvent, flag);
849 NSString *s = [pEvent charactersIgnoringModifiers];
854 c = [s characterAtIndex:0];
856 if(!
static_cast<bool>(flag & kFVIRTKEY))
862 WDL_MakeUTFChar(utf8, c, 4);
864 IKeyPress keyPress {utf8, code,
static_cast<bool>(flag & kFSHIFT),
865 static_cast<bool>(flag & kFCONTROL),
866 static_cast<bool>(flag & kFALT)};
868 bool handle = mGraphics->OnKeyDown(mPrevX, mPrevY, keyPress);
872 [[
self nextResponder] keyDown:pEvent];
876- (void) keyUp: (NSEvent*) pEvent
879 int code = MacKeyEventToVK(pEvent, flag);
880 NSString *s = [pEvent charactersIgnoringModifiers];
885 c = [s characterAtIndex:0];
887 if(!
static_cast<bool>(flag & kFVIRTKEY))
893 WDL_MakeUTFChar(utf8, c, 4);
895 IKeyPress keyPress {utf8, code,
static_cast<bool>(flag & kFSHIFT),
896 static_cast<bool>(flag & kFCONTROL),
897 static_cast<bool>(flag & kFALT)};
899 bool handle = mGraphics->OnKeyUp(mPrevX, mPrevY, keyPress);
903 [[
self nextResponder] keyUp:pEvent];
907- (void) scrollWheel: (NSEvent*) pEvent
909 if (mTextFieldView) [
self endUserInput ];
911 float d = [pEvent deltaY];
913 mGraphics->OnMouseWheel(info.x, info.y, info.ms, d);
916static void MakeCursorFromName(NSCursor*& cursor,
const char *name)
919 const char* basePath =
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors/";
921 NSString* imagePath = [NSString stringWithFormat:@"%s%s/cursor.pdf", basePath, name];
922 NSString* infoPath = [NSString stringWithFormat:@"file:%s%s/info.plist", basePath, name];
923 NSImage* fileImage = [[NSImage alloc] initByReferencingFile: imagePath];
924 NSImage *cursorImage = [[NSImage alloc] initWithSize:[fileImage size]];
925 NSDictionary* info = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:infoPath]];
928 double hotX = [info[@"hotx-scaled"] doubleValue];
929 double hotY = [info[@"hoty-scaled"] doubleValue];
930 double blur = [info[@"blur"] doubleValue];
931 double offsetX = [info[@"shadowoffsetx"] doubleValue];
932 double offsetY = [info[@"shadowoffsety"] doubleValue];
933 double red = [info[@"shadowcolor"][0] doubleValue];
934 double green = [info[@"shadowcolor"][1] doubleValue];
935 double blue = [info[@"shadowcolor"][2] doubleValue];
936 double alpha = [info[@"shadowcolor"][3] doubleValue];
937 CGColorRef shadowColor = CGColorCreateGenericRGB(red, green, blue, alpha);
939 for (
int scale = 1; scale <= 4; scale++)
942 NSAffineTransform* xform = [NSAffineTransform transform];
943 [xform scaleBy:scale];
944 id hints = @{ NSImageHintCTM: xform };
945 CGImageRef rasterCGImage = [fileImage CGImageForProposedRect:NULL context:nil hints:hints];
948 size_t width = CGImageGetWidth(rasterCGImage);
949 size_t height = CGImageGetHeight(rasterCGImage);
950 CGSize offset = CGSize {
static_cast<CGFloat
>(offsetX * scale),
static_cast<CGFloat
>(offsetY * scale) };
951 CGContextRef shadowContext = CGBitmapContextCreate(NULL, width, height, CGImageGetBitsPerComponent(rasterCGImage), 0, CGImageGetColorSpace(rasterCGImage), CGImageGetBitmapInfo(rasterCGImage));
952 CGContextSetShadowWithColor(shadowContext, offset, blur * scale, shadowColor);
953 CGContextDrawImage(shadowContext, CGRectMake(0, 0, width, height), rasterCGImage);
954 CGImageRef shadowCGImage = CGBitmapContextCreateImage(shadowContext);
957 NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithCGImage:shadowCGImage];
958 [rep setSize:[fileImage size]];
959 [cursorImage addRepresentation:rep];
963 CGContextRelease(shadowContext);
964 CGImageRelease(shadowCGImage);
968 cursor = [[NSCursor alloc] initWithImage:cursorImage hotSpot:NSMakePoint(hotX, hotY)];
971 [cursorImage release];
973 CGColorRelease(shadowColor);
976- (void) setMouseCursor: (ECursor) cursorType
978 NSCursor* pCursor =
nullptr;
980 bool helpCurrent =
false;
981 bool helpRequested =
false;
985 case ECursor::ARROW: pCursor = [NSCursor arrowCursor];
break;
986 case ECursor::IBEAM: pCursor = [NSCursor IBeamCursor];
break;
988 if ([NSCursor respondsToSelector:
@selector(busyButClickableCursor)])
989 pCursor = [NSCursor performSelector:@selector(busyButClickableCursor)];
991 case ECursor::CROSS: pCursor = [NSCursor crosshairCursor];
break;
992 case ECursor::UPARROW:
993 if ([NSCursor respondsToSelector:
@selector(_windowResizeNorthCursor)])
994 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthCursor)];
996 pCursor = [NSCursor resizeUpCursor];
998 case ECursor::SIZENWSE:
999 if ([NSCursor respondsToSelector:
@selector(_windowResizeNorthWestSouthEastCursor)])
1000 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthWestSouthEastCursor)];
1002 case ECursor::SIZENESW:
1003 if ([NSCursor respondsToSelector:
@selector(_windowResizeNorthEastSouthWestCursor)])
1004 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthEastSouthWestCursor)];
1006 case ECursor::SIZEWE:
1007 if ([NSCursor respondsToSelector:
@selector(_windowResizeEastWestCursor)])
1008 pCursor = [NSCursor performSelector:@selector(_windowResizeEastWestCursor)];
1010 pCursor = [NSCursor resizeLeftRightCursor];
1012 case ECursor::SIZENS:
1013 if ([NSCursor respondsToSelector:
@selector(_windowResizeNorthSouthCursor)])
1014 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthSouthCursor)];
1016 pCursor = [NSCursor resizeUpDownCursor];
1018 case ECursor::SIZEALL:
1021 MakeCursorFromName(mMoveCursor,
"move");
1022 pCursor = mMoveCursor;
1025 case ECursor::INO: pCursor = [NSCursor operationNotAllowedCursor];
break;
1026 case ECursor::HAND: pCursor = [NSCursor pointingHandCursor];
break;
1027 case ECursor::APPSTARTING:
1028 if ([NSCursor respondsToSelector:
@selector(busyButClickableCursor)])
1029 pCursor = [NSCursor performSelector:@selector(busyButClickableCursor)];
1032 if ([NSCursor respondsToSelector:
@selector(_helpCursor)])
1033 pCursor = [NSCursor performSelector:@selector(_helpCursor)];
1034 helpRequested =
true;
1036 default: pCursor = [NSCursor arrowCursor];
break;
1039 if ([NSCursor respondsToSelector:
@selector(helpCursorShown)])
1040 helpCurrent = [NSCursor performSelector:@selector(helpCursorShown)];
1042 if (helpCurrent && !helpRequested)
1045#pragma clang diagnostic push
1046#pragma clang diagnostic ignored "-Wobjc-method-access"
1047 [NSCursor _setHelpCursor : false];
1048#pragma clang diagnostic pop
1052 pCursor = [NSCursor arrowCursor];
1057- (void) removeFromSuperview
1060 [
self endUserInput ];
1062 mGraphics->SetPlatformContext(
nullptr);
1065 mGraphics->GetDelegate()->CloseWindow();
1066 [
super removeFromSuperview];
1069- (void) controlTextDidEndEditing: (NSNotification*) aNotification
1071 char* txt = (
char*)[[mTextFieldView stringValue] UTF8String];
1073 mGraphics->SetControlValueAfterTextEdit(txt);
1074 mGraphics->SetAllControlsDirty();
1076 [
self endUserInput ];
1081 IGRAPHICS_MENU_RCVR* pDummyView = [[[IGRAPHICS_MENU_RCVR alloc] initWithFrame:bounds] autorelease];
1082 NSMenu* pNSMenu = [[[IGRAPHICS_MENU alloc] initWithIPopupMenuAndReceiver:&menu : pDummyView] autorelease];
1083 NSPoint wp = {bounds.origin.x, bounds.origin.y + bounds.size.height + 4};
1085 NSMenuItem* pSelectedItem = nil;
1087 auto selectedItemIdx = menu.GetChosenItemIdx();
1089 if (selectedItemIdx > -1)
1091 pSelectedItem = [pNSMenu itemAtIndex:selectedItemIdx];
1094 if (pSelectedItem != nil)
1096 wp = {bounds.origin.x, bounds.origin.y};
1099 [pNSMenu popUpMenuPositioningItem:pSelectedItem atLocation:wp inView:self];
1101 NSMenuItem* pChosenItem = [pDummyView menuItem];
1102 NSMenu* pChosenMenu = [pChosenItem menu];
1103 IPopupMenu* pIPopupMenu = [(IGRAPHICS_MENU*) pChosenMenu iPopupMenu];
1105 long chosenItemIdx = [pChosenMenu indexOfItem: pChosenItem];
1107 if (chosenItemIdx > -1 && pIPopupMenu)
1109 pIPopupMenu->SetChosenItemIdx((
int) chosenItemIdx);
1116- (void) createTextEntry: (
int) paramIdx : (const
IText&) text : (const
char*) str : (
int) length : (NSRect) areaRect;
1121 mTextFieldView = [[IGRAPHICS_TEXTFIELD alloc] initWithFrame: areaRect];
1123 if (text.mVAlign == EVAlign::Middle)
1125 IGRAPHICS_TEXTFIELDCELL* pCell = [[IGRAPHICS_TEXTFIELDCELL alloc] initTextCell:@"textfield"];
1126 [mTextFieldView setCell: pCell];
1127 [mTextFieldView setEditable: TRUE];
1128 [mTextFieldView setDrawsBackground: TRUE];
1131 CoreTextFontDescriptor* CTFontDescriptor = CoreTextHelpers::GetCTFontDescriptor(text, sFontDescriptorCache);
1132 double ratio = CTFontDescriptor->GetEMRatio() * mGraphics->GetDrawScale();
1133 NSFontDescriptor* fontDescriptor = (NSFontDescriptor*) CTFontDescriptor->GetDescriptor();
1134 NSFont* font = [NSFont fontWithDescriptor: fontDescriptor size: text.mSize * ratio];
1135 [mTextFieldView setFont: font];
1137 switch (text.mAlign)
1140 [mTextFieldView setAlignment: NSLeftTextAlignment];
1142 case EAlign::Center:
1143 [mTextFieldView setAlignment: NSCenterTextAlignment];
1146 [mTextFieldView setAlignment: NSRightTextAlignment];
1152 const IParam* pParam = paramIdx > kNoParameter ? mGraphics->GetDelegate()->GetParam(paramIdx) :
nullptr;
1157 NSMutableCharacterSet *characterSet = [[NSMutableCharacterSet alloc] init];
1159 switch ( pParam->
Type() )
1161 case IParam::kTypeEnum:
1162 case IParam::kTypeInt:
1163 case IParam::kTypeBool:
1164 [characterSet addCharactersInString:@"0123456789-+"];
1166 case IParam::kTypeDouble:
1167 [characterSet addCharactersInString:@"0123456789.-+"];
1173 [mTextFieldView setFormatter:[[[IGRAPHICS_FORMATTER alloc] init] autorelease]];
1174 [[mTextFieldView formatter] setAcceptableCharacterSet:characterSet];
1175 [[mTextFieldView formatter] setMaximumLength:length];
1176 [characterSet release];
1179 [[mTextFieldView cell] setLineBreakMode: NSLineBreakByTruncatingTail];
1180 [mTextFieldView setAllowsEditingTextAttributes:NO];
1181 [mTextFieldView setTextColor:ToNSColor(text.mTextEntryFGColor)];
1182 [mTextFieldView setBackgroundColor:ToNSColor(text.mTextEntryBGColor)];
1184 [mTextFieldView setStringValue: [NSString stringWithUTF8String:str]];
1186#ifndef COCOA_TEXTENTRY_BORDERED
1187 [mTextFieldView setBordered: NO];
1188 [mTextFieldView setFocusRingType:NSFocusRingTypeNone];
1191 [mTextFieldView setDelegate: self];
1193 [
self addSubview: mTextFieldView];
1194 NSWindow* pWindow = [
self window];
1195 [pWindow makeKeyAndOrderFront:nil];
1196 [pWindow makeFirstResponder: mTextFieldView];
1199- (void) endUserInput
1201 [mTextFieldView setDelegate: nil];
1202 [mTextFieldView removeFromSuperview];
1204 NSWindow* pWindow = [
self window];
1205 [pWindow makeFirstResponder: self];
1207 mTextFieldView =
nullptr;
1208 mGraphics->ClearInTextEntryControl();
1211- (BOOL) promptForColor: (
IColor&) color : (IColorPickerHandlerFunc) func;
1213 NSColorPanel* colorPanel = [NSColorPanel sharedColorPanel];
1214 mColorPickerFunc = func;
1216 [colorPanel setTarget:self];
1217 [colorPanel setShowsAlpha: TRUE];
1218 [colorPanel setAction:@selector(onColorPicked:)];
1219 [colorPanel setColor:ToNSColor(color)];
1220 [colorPanel orderFront:nil];
1222 return colorPanel != nil;
1225- (void) onColorPicked: (NSColorPanel*) pColorPanel
1227 mColorPickerFunc(FromNSColor([pColorPanel color]));
1230- (NSString*) view: (NSView*) pView stringForToolTip: (NSToolTipTag) tag point: (NSPoint) point userData: (
void*) pData
1232 int c = mGraphics ? GetMouseOver(mGraphics) : -1;
1233 if (c < 0)
return @"";
1235 const char* tooltip = mGraphics->GetControl(c)->GetTooltip();
1236 return CStringHasContents(tooltip) ? [NSString stringWithUTF8String:tooltip] :
@"";
1239- (void) registerToolTip: (
IRECT&) bounds
1241 [
self addToolTipRect: ToNSRect(mGraphics, bounds) owner: self userData: nil];
1244- (NSDragOperation) draggingEntered: (
id<NSDraggingInfo>) sender
1246 NSPasteboard *pPasteBoard = [sender draggingPasteboard];
1248 if ([[pPasteBoard types] containsObject:NSFilenamesPboardType])
1249 return NSDragOperationGeneric;
1251 return NSDragOperationNone;
1254- (BOOL) performDragOperation: (
id<NSDraggingInfo>) sender
1256 NSPasteboard* pPasteBoard = [sender draggingPasteboard];
1258 if ([[pPasteBoard types] containsObject:NSFilenamesPboardType])
1260 NSArray* pFiles = [pPasteBoard propertyListForType:NSFilenamesPboardType];
1261 NSPoint point = [sender draggingLocation];
1262 NSPoint relativePoint = [
self convertPoint: point fromView:nil];
1264 const float scale = mGraphics->GetDrawScale();
1265 const float x = relativePoint.x / scale;
1266 const float y = relativePoint.y / scale;
1267 if ([pFiles count] == 1)
1269 NSString* pFirstFile = [pFiles firstObject];
1270 mGraphics->OnDrop([pFirstFile UTF8String], x, y);
1272 else if ([pFiles count] > 1)
1274 std::vector<const char*> paths([pFiles count]);
1275 for (
auto i = 0; i < [pFiles count]; i++)
1277 NSString* pFile = [pFiles objectAtIndex: i];
1278 paths[i] = [pFile UTF8String];
1280 mGraphics->OnDropMultiple(paths, x, y);
1286- (NSDragOperation)draggingSession:(NSDraggingSession*) session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
1288 return NSDragOperationCopy;
1291#ifdef IGRAPHICS_METAL
1292- (void) frameDidChange:(NSNotification*) pNotification
1294 CGFloat scale = [[
self window] backingScaleFactor];
1296 [(CAMetalLayer*)[
self layer] setDrawableSize:CGSizeMake(self.frame.size.width * scale,
1297 self.frame.size.height * scale)];
1300#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1301- (void) frameDidChange:(NSNotification*) pNotification
1303 [
self activateGLContext];
1344- (void) activateGLContext
1346#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1347 [[
self openGLContext] makeCurrentContext];
1352- (void) deactivateGLContext
1354#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1355 [NSOpenGLContext clearCurrentContext];
1362#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1363 [[
self openGLContext] flushBuffer];
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,...