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@implementation IGRAPHICS_MENU_RCVR
137- (NSMenuItem*) menuItem
142- (void) onMenuSelection:(
id) sender
149@implementation IGRAPHICS_MENU
151- (id) initWithIPopupMenuAndReceiver: (
IPopupMenu*) pMenu : (NSView*) pView
153 [
self initWithTitle: @""];
155 NSMenuItem* nsMenuItem = nil;
156 NSMutableString* nsMenuItemTitle = nil;
158 [
self setAutoenablesItems:NO];
160 int numItems = pMenu->NItems();
162 for (
int i = 0; i < numItems; ++i)
166 nsMenuItemTitle = [[[NSMutableString alloc] initWithCString:pMenuItem->GetText() encoding:NSUTF8StringEncoding] autorelease];
168 if (pMenu->GetPrefix())
170 NSString* prefixString = 0;
172 switch (pMenu->GetPrefix())
174 case 0: prefixString = [NSString stringWithUTF8String:
""]; break;
175 case 1: prefixString = [NSString stringWithFormat:
@"%1d: ", i+1]; break;
176 case 2: prefixString = [NSString stringWithFormat:
@"%02d: ", i+1]; break;
177 case 3: prefixString = [NSString stringWithFormat:
@"%03d: ", i+1]; break;
180 [nsMenuItemTitle insertString:prefixString atIndex:0];
183 if (pMenuItem->GetIsSeparator())
185 [self addItem:[NSMenuItem separatorItem]];
187 else if (pMenuItem->GetSubmenu())
189 nsMenuItem = [self addItemWithTitle:nsMenuItemTitle action:nil keyEquivalent:
@""];
190 NSMenu* subMenu = [[IGRAPHICS_MENU alloc] initWithIPopupMenuAndReceiver:pMenuItem->GetSubmenu() :pView];
191 [self setSubmenu: subMenu forItem:nsMenuItem];
196 nsMenuItem = [self addItemWithTitle:nsMenuItemTitle action:@selector(onMenuSelection:) keyEquivalent:
@""];
198 [nsMenuItem setTarget:pView];
201 if (nsMenuItem && !pMenuItem->GetIsSeparator())
203 [nsMenuItem setIndentationLevel:pMenuItem->GetIsTitle() ? 1 : 0 ];
204 [nsMenuItem setEnabled:pMenuItem->GetEnabled() ? YES : NO];
205 [nsMenuItem setState:pMenuItem->GetChecked() ? NSOnState : NSOffState];
221@implementation IGRAPHICS_TEXTFIELD
223- (bool) becomeFirstResponder;
225 bool success = [
super becomeFirstResponder];
228 NSTextView *textField = (NSTextView*) [self currentEditor];
229 if( [textField respondsToSelector:
@selector(setInsertionPointColor:)] )
230 [textField setInsertionPointColor: [
self textColor]];
251@implementation IGRAPHICS_TEXTFIELDCELL
253- (NSRect) drawingRectForBounds: (NSRect) inRect
256 NSRect outRect = [
super drawingRectForBounds:inRect];
263 if (mIsEditingOrSelecting == NO)
266 NSSize textSize = [
self cellSize];
269 float heightDelta = outRect.size.height - textSize.height;
271 outRect.origin.x += 3;
272 outRect.size.width -= 6;
276 outRect.size.height -= heightDelta;
277 outRect.origin.y += (heightDelta / 2);
284- (void) selectWithFrame: (NSRect) aRect inView: (NSView*) controlView editor: (NSText*) textObj delegate: (
id) anObject start: (NSInteger) selStart length: (NSInteger) selLength
286 aRect = [
self drawingRectForBounds:aRect];
287 mIsEditingOrSelecting = YES;
288 [
super selectWithFrame:aRect inView:controlView editor:textObj delegate:anObject start:selStart length:selLength];
289 mIsEditingOrSelecting = NO;
292- (void) editWithFrame: (NSRect) aRect inView: (NSView*) controlView editor: (NSText*) textObj delegate: (
id) anObject event: (NSEvent*) theEvent
294 aRect = [
self drawingRectForBounds:aRect];
295 mIsEditingOrSelecting = YES;
296 [
super editWithFrame:aRect inView:controlView editor:textObj delegate:anObject event:theEvent];
297 mIsEditingOrSelecting = NO;
302@implementation IGRAPHICS_FORMATTER
306 [filterCharacterSet release];
310- (BOOL) isPartialStringValid:(NSString*) partialString newEditingString:(NSString**) newString errorDescription:(NSString**) error
312 if (filterCharacterSet != nil)
315 int len = (int) [partialString length];
317 for (i = 0; i < len; i++)
319 if (![filterCharacterSet characterIsMember:[partialString characterAtIndex:i]])
328 if ([partialString length] > maxLength)
334 if (maxValue && [partialString intValue] > maxValue)
342- (void) setAcceptableCharacterSet: (NSCharacterSet*) inCharacterSet
344 [inCharacterSet retain];
345 [filterCharacterSet release];
346 filterCharacterSet = inCharacterSet;
349- (void) setMaximumLength: (
int) inLength
351 maxLength = inLength;
354- (void) setMaximumValue: (
int) inValue
359- (NSString*) stringForObjectValue: (
id) anObject
361 if ([anObject isKindOfClass:[NSString
class]])
369- (BOOL) getObjectValue: (
id*) anObject forString:(NSString*) string errorDescription: (NSString **) error
371 if (anObject &&
string)
373 *anObject = [NSString stringWithString:string];
382extern StaticStorage<CoreTextFontDescriptor> sFontDescriptorCache;
384@implementation IGRAPHICS_VIEW
390 mGraphics = pGraphics;
391 NSRect r = NSMakeRect(0.f, 0.f, (
float) pGraphics->WindowWidth(), (
float) pGraphics->WindowHeight());
392 self = [
super initWithFrame:r];
394 mMouseOutDuringDrag =
false;
396 self.wantsLayer = YES;
397 self.layer.opaque = YES;
398 self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
400 [
self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
402 #if defined IGRAPHICS_METAL
403 self.layer = [CAMetalLayer new];
404 [(CAMetalLayer*)[
self layer] setPixelFormat:MTLPixelFormatBGRA8Unorm];
405 ((CAMetalLayer*) self.layer).device = MTLCreateSystemDefaultDevice();
407 #elif defined IGRAPHICS_GL
408 NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy;
409 #if defined IGRAPHICS_GL3
410 profile = (NSOpenGLPixelFormatAttribute)NSOpenGLProfileVersion3_2Core;
412 const NSOpenGLPixelFormatAttribute attrs[] = {
413 NSOpenGLPFAAccelerated,
414 NSOpenGLPFANoRecovery,
415 NSOpenGLPFADoubleBuffer,
416 NSOpenGLPFAAlphaSize, 8,
417 NSOpenGLPFAColorSize, 24,
418 NSOpenGLPFADepthSize, 0,
419 NSOpenGLPFAStencilSize, 8,
420 NSOpenGLPFAOpenGLProfile, profile,
421 (NSOpenGLPixelFormatAttribute) 0
423 NSOpenGLPixelFormat* pPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
424 NSOpenGLContext* pGLContext = [[NSOpenGLContext alloc] initWithFormat:pPixelFormat shareContext:nil];
430 self.pixelFormat = pPixelFormat;
431 self.openGLContext = pGLContext;
432 self.wantsBestResolutionOpenGLSurface = YES;
435 #if !defined IGRAPHICS_GL
443- (void) prepareOpenGL
445 [
super prepareOpenGL];
447 [[
self openGLContext] makeCurrentContext];
451 [[
self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
457static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink,
const CVTimeStamp* now,
const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut,
void* displayLinkContext)
459 dispatch_source_t source = (dispatch_source_t) displayLinkContext;
460 dispatch_source_merge_data(source, 1);
462 return kCVReturnSuccess;
465- (void) onTimer: (NSTimer*) pTimer
472#ifdef IGRAPHICS_CVDISPLAYLINK
473 mDisplaySource = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
474 dispatch_source_set_event_handler(mDisplaySource, ^(){
477 dispatch_resume(mDisplaySource);
481 cvReturn = CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink);
483 assert(cvReturn == kCVReturnSuccess);
485 cvReturn = CVDisplayLinkSetOutputCallback(mDisplayLink, &displayLinkCallback, (
void*) mDisplaySource);
486 assert(cvReturn == kCVReturnSuccess);
489 CGLContextObj cglContext = [[
self openGLContext] CGLContextObj];
490 CGLPixelFormatObj cglPixelFormat = [[
self pixelFormat] CGLPixelFormatObj];
491 CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(mDisplayLink, cglContext, cglPixelFormat);
494 CGDirectDisplayID viewDisplayID =
495 (CGDirectDisplayID) [self.window.screen.deviceDescription[
@"NSScreenNumber"] unsignedIntegerValue];;
497 cvReturn = CVDisplayLinkSetCurrentCGDisplay(mDisplayLink, viewDisplayID);
499 assert(cvReturn == kCVReturnSuccess);
501 CVDisplayLinkStart(mDisplayLink);
503 double sec = 1.0 / (double) mGraphics->FPS();
504 mTimer = [NSTimer timerWithTimeInterval:sec target:self selector:@selector(onTimer:) userInfo:nil repeats:YES];
505 [[NSRunLoop currentRunLoop] addTimer: mTimer forMode: (NSString*) kCFRunLoopCommonModes];
511#ifdef IGRAPHICS_CVDISPLAYLINK
512 CVDisplayLinkStop(mDisplayLink);
513 dispatch_source_cancel(mDisplaySource);
514 CVDisplayLinkRelease(mDisplayLink);
524 if([NSColorPanel sharedColorPanelExists])
525 [[NSColorPanel sharedColorPanel] close];
527 mColorPickerFunc =
nullptr;
528 [mMoveCursor release];
529 [mTrackingArea release];
530 [[NSNotificationCenter defaultCenter] removeObserver:self];
537 return mGraphics ? YES : NO;
545- (void) viewDidChangeEffectiveAppearance
547 if (@available(macOS 10.14, *)) {
548 BOOL isDarkMode = [[[
self effectiveAppearance] name] isEqualToString: (NSAppearanceNameDarkAqua)];
549 mGraphics->OnAppearanceChanged(isDarkMode ? EUIAppearance::Dark : EUIAppearance::Light);
553- (BOOL) acceptsFirstResponder
558- (BOOL) acceptsFirstMouse: (NSEvent*) pEvent
563- (void) viewDidMoveToWindow
565 NSWindow* pWindow = [
self window];
569 [pWindow makeFirstResponder: self];
570 [pWindow setAcceptsMouseMovedEvents: YES];
572 CGFloat newScale = [pWindow backingScaleFactor];
575 mGraphics->SetScreenScale(newScale);
577 #ifdef IGRAPHICS_METAL
578 [[NSNotificationCenter defaultCenter] addObserver:self
579 selector:@selector(frameDidChange:)
580 name:NSViewFrameDidChangeNotification
598- (void) viewDidChangeBackingProperties:(NSNotification*) pNotification
600 NSWindow* pWindow = [
self window];
605 CGFloat newScale = [pWindow backingScaleFactor];
607 mGraphics->SetPlatformContext(
nullptr);
609 if (newScale != mGraphics->GetScreenScale())
610 mGraphics->SetScreenScale(newScale);
612#if defined IGRAPHICS_GL
613 self.layer.contentsScale = 1./newScale;
614#elif defined IGRAPHICS_METAL
615 [(CAMetalLayer*)[
self layer] setDrawableSize:CGSizeMake(self.frame.size.width * newScale,
616 self.frame.size.height * newScale)];
620- (CGContextRef) getCGContextRef
622 CGContextRef pCGC = [NSGraphicsContext currentContext].CGContext;
623 return [NSGraphicsContext graphicsContextWithCGContext: pCGC flipped: YES].CGContext;
627- (void) drawRect: (NSRect) bounds
629 #if !defined IGRAPHICS_GL && !defined IGRAPHICS_METAL
632 mGraphics->SetPlatformContext([self getCGContextRef]);
634 if (mGraphics->GetPlatformContext())
638 [
self getRectsBeingDrawn:&rects count:&numRects];
641 for (
int i = 0; i < numRects; i++)
642 drawRects.Add(ToIRECT(mGraphics, &rects[i]));
644 mGraphics->Draw(drawRects);
657 if (mGraphics->IsDirty(mDirtyRects))
659 mGraphics->SetAllControlsClean();
661 #if !defined IGRAPHICS_GL && !defined IGRAPHICS_METAL
662 for (int i = 0; i < mDirtyRects.Size(); i++)
663 [self setNeedsDisplayInRect:ToNSRect(mGraphics, mDirtyRects.Get(i))];
666 [[self openGLContext] makeCurrentContext];
669 mGraphics->Draw(mDirtyRects);
672 [[self openGLContext] flushBuffer];
677- (void) getMouseXY: (NSEvent*) pEvent : (
float&) x : (
float&) y
681 NSPoint pt = [
self convertPoint:[pEvent locationInWindow] fromView:nil];
682 x = pt.x / mGraphics->GetDrawScale();
683 y = pt.y / mGraphics->GetDrawScale();
685 mGraphics->DoCursorLock(x, y, mPrevX, mPrevY);
686 mGraphics->SetTabletInput(pEvent.subtype == NSTabletPointEventSubtype);
693 [
self getMouseXY:pEvent : info.x : info.y];
694 int mods = (int) [pEvent modifierFlags];
695 info.ms =
IMouseMod(
true, (mods & NSCommandKeyMask), (mods & NSShiftKeyMask), (mods & NSControlKeyMask), (mods & NSAlternateKeyMask));
700- (
IMouseInfo) getMouseRight: (NSEvent*) pEvent
703 [
self getMouseXY:pEvent : info.x : info.y];
704 int mods = (int) [pEvent modifierFlags];
705 info.ms =
IMouseMod(
false,
true, (mods & NSShiftKeyMask), (mods & NSControlKeyMask), (mods & NSAlternateKeyMask));
710- (void) updateTrackingAreas
712 [
super updateTrackingAreas];
714 if (mTrackingArea != nil)
716 [
self removeTrackingArea:mTrackingArea];
717 [mTrackingArea release];
720 int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingEnabledDuringMouseDrag);
721 mTrackingArea = [ [NSTrackingArea alloc] initWithRect:[
self bounds] options:opts owner:self userInfo:nil];
722 [
self addTrackingArea:mTrackingArea];
725- (void) mouseEntered: (NSEvent*) pEvent
727 mMouseOutDuringDrag =
false;
731 mGraphics->OnSetCursor();
735- (void) mouseExited: (NSEvent*) pEvent
739 if (!mGraphics->ControlIsCaptured())
741 mGraphics->OnMouseOut();
745 mMouseOutDuringDrag =
true;
750- (void) mouseDown: (NSEvent*) pEvent
755 if (([pEvent clickCount] - 1) % 2)
757 mGraphics->OnMouseDblClick(info.x, info.y, info.ms);
761 std::vector<IMouseInfo> list {info};
762 mGraphics->OnMouseDown(list);
767- (void) mouseUp: (NSEvent*) pEvent
772 std::vector<IMouseInfo> list {info};
773 mGraphics->OnMouseUp(list);
775 if (mMouseOutDuringDrag)
777 mGraphics->OnMouseOut();
778 mMouseOutDuringDrag =
false;
783- (void) mouseDragged: (NSEvent*) pEvent
786 float prevX = mPrevX;
787 float prevY = mPrevY;
789 if (mGraphics && !mGraphics->IsInPlatformTextEntry())
791 info.dX = info.x - prevX;
792 info.dY = info.y - prevY;
793 std::vector<IMouseInfo> list {info};
794 mGraphics->OnMouseDrag(list);
798- (void) rightMouseDown: (NSEvent*) pEvent
800 IMouseInfo info = [
self getMouseRight:pEvent];
803 if (([pEvent clickCount] - 1) % 2)
805 mGraphics->OnMouseDblClick(info.x, info.y, info.ms);
809 std::vector<IMouseInfo> list {info};
810 mGraphics->OnMouseDown(list);
815- (void) rightMouseUp: (NSEvent*) pEvent
817 IMouseInfo info = [
self getMouseRight:pEvent];
820 std::vector<IMouseInfo> list {info};
821 mGraphics->OnMouseUp(list);
825- (void) rightMouseDragged: (NSEvent*) pEvent
828 float prevX = mPrevX;
829 float prevY = mPrevY;
830 IMouseInfo info = [
self getMouseRight:pEvent];
832 if (mGraphics && !mTextFieldView)
834 info.dX = info.x - prevX;
835 info.dY = info.y - prevY;
836 std::vector<IMouseInfo> list {info};
837 mGraphics->OnMouseDrag(list);
841- (void) mouseMoved: (NSEvent*) pEvent
845 mGraphics->OnMouseOver(info.x, info.y, info.ms);
848- (void) keyDown: (NSEvent*) pEvent
851 int code = MacKeyEventToVK(pEvent, flag);
852 NSString *s = [pEvent charactersIgnoringModifiers];
857 c = [s characterAtIndex:0];
859 if(!
static_cast<bool>(flag & kFVIRTKEY))
865 WDL_MakeUTFChar(utf8, c, 4);
867 IKeyPress keyPress {utf8, code,
static_cast<bool>(flag & kFSHIFT),
868 static_cast<bool>(flag & kFCONTROL),
869 static_cast<bool>(flag & kFALT)};
871 bool handle = mGraphics->OnKeyDown(mPrevX, mPrevY, keyPress);
875 [[
self nextResponder] keyDown:pEvent];
879- (void) keyUp: (NSEvent*) pEvent
882 int code = MacKeyEventToVK(pEvent, flag);
883 NSString *s = [pEvent charactersIgnoringModifiers];
888 c = [s characterAtIndex:0];
890 if(!
static_cast<bool>(flag & kFVIRTKEY))
896 WDL_MakeUTFChar(utf8, c, 4);
898 IKeyPress keyPress {utf8, code,
static_cast<bool>(flag & kFSHIFT),
899 static_cast<bool>(flag & kFCONTROL),
900 static_cast<bool>(flag & kFALT)};
902 bool handle = mGraphics->OnKeyUp(mPrevX, mPrevY, keyPress);
906 [[
self nextResponder] keyUp:pEvent];
910- (void) scrollWheel: (NSEvent*) pEvent
912 if (mTextFieldView) [
self endUserInput ];
914 float d = [pEvent deltaY];
916 mGraphics->OnMouseWheel(info.x, info.y, info.ms, d);
919static void MakeCursorFromName(NSCursor*& cursor,
const char *name)
922 const char* basePath =
"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors/";
924 NSString* imagePath = [NSString stringWithFormat:@"%s%s/cursor.pdf", basePath, name];
925 NSString* infoPath = [NSString stringWithFormat:@"file:%s%s/info.plist", basePath, name];
926 NSImage* fileImage = [[NSImage alloc] initByReferencingFile: imagePath];
927 NSImage *cursorImage = [[NSImage alloc] initWithSize:[fileImage size]];
928 NSDictionary* info = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:infoPath]];
931 double hotX = [info[@"hotx-scaled"] doubleValue];
932 double hotY = [info[@"hoty-scaled"] doubleValue];
933 double blur = [info[@"blur"] doubleValue];
934 double offsetX = [info[@"shadowoffsetx"] doubleValue];
935 double offsetY = [info[@"shadowoffsety"] doubleValue];
936 double red = [info[@"shadowcolor"][0] doubleValue];
937 double green = [info[@"shadowcolor"][1] doubleValue];
938 double blue = [info[@"shadowcolor"][2] doubleValue];
939 double alpha = [info[@"shadowcolor"][3] doubleValue];
940 CGColorRef shadowColor = CGColorCreateGenericRGB(red, green, blue, alpha);
942 for (
int scale = 1; scale <= 4; scale++)
945 NSAffineTransform* xform = [NSAffineTransform transform];
946 [xform scaleBy:scale];
947 id hints = @{ NSImageHintCTM: xform };
948 CGImageRef rasterCGImage = [fileImage CGImageForProposedRect:NULL context:nil hints:hints];
951 size_t width = CGImageGetWidth(rasterCGImage);
952 size_t height = CGImageGetHeight(rasterCGImage);
953 CGSize offset = CGSize {
static_cast<CGFloat
>(offsetX * scale),
static_cast<CGFloat
>(offsetY * scale) };
954 CGContextRef shadowContext = CGBitmapContextCreate(NULL, width, height, CGImageGetBitsPerComponent(rasterCGImage), 0, CGImageGetColorSpace(rasterCGImage), CGImageGetBitmapInfo(rasterCGImage));
955 CGContextSetShadowWithColor(shadowContext, offset, blur * scale, shadowColor);
956 CGContextDrawImage(shadowContext, CGRectMake(0, 0, width, height), rasterCGImage);
957 CGImageRef shadowCGImage = CGBitmapContextCreateImage(shadowContext);
960 NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithCGImage:shadowCGImage];
961 [rep setSize:[fileImage size]];
962 [cursorImage addRepresentation:rep];
966 CGContextRelease(shadowContext);
967 CGImageRelease(shadowCGImage);
971 cursor = [[NSCursor alloc] initWithImage:cursorImage hotSpot:NSMakePoint(hotX, hotY)];
974 [cursorImage release];
976 CGColorRelease(shadowColor);
979- (void) setMouseCursor: (ECursor) cursorType
981 NSCursor* pCursor =
nullptr;
983 bool helpCurrent =
false;
984 bool helpRequested =
false;
988 case ECursor::ARROW: pCursor = [NSCursor arrowCursor];
break;
989 case ECursor::IBEAM: pCursor = [NSCursor IBeamCursor];
break;
991 if ([NSCursor respondsToSelector:
@selector(busyButClickableCursor)])
992 pCursor = [NSCursor performSelector:@selector(busyButClickableCursor)];
994 case ECursor::CROSS: pCursor = [NSCursor crosshairCursor];
break;
995 case ECursor::UPARROW:
996 if ([NSCursor respondsToSelector:
@selector(_windowResizeNorthCursor)])
997 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthCursor)];
999 pCursor = [NSCursor resizeUpCursor];
1001 case ECursor::SIZENWSE:
1002 if ([NSCursor respondsToSelector:
@selector(_windowResizeNorthWestSouthEastCursor)])
1003 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthWestSouthEastCursor)];
1005 case ECursor::SIZENESW:
1006 if ([NSCursor respondsToSelector:
@selector(_windowResizeNorthEastSouthWestCursor)])
1007 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthEastSouthWestCursor)];
1009 case ECursor::SIZEWE:
1010 if ([NSCursor respondsToSelector:
@selector(_windowResizeEastWestCursor)])
1011 pCursor = [NSCursor performSelector:@selector(_windowResizeEastWestCursor)];
1013 pCursor = [NSCursor resizeLeftRightCursor];
1015 case ECursor::SIZENS:
1016 if ([NSCursor respondsToSelector:
@selector(_windowResizeNorthSouthCursor)])
1017 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthSouthCursor)];
1019 pCursor = [NSCursor resizeUpDownCursor];
1021 case ECursor::SIZEALL:
1024 MakeCursorFromName(mMoveCursor,
"move");
1025 pCursor = mMoveCursor;
1028 case ECursor::INO: pCursor = [NSCursor operationNotAllowedCursor];
break;
1029 case ECursor::HAND: pCursor = [NSCursor pointingHandCursor];
break;
1030 case ECursor::APPSTARTING:
1031 if ([NSCursor respondsToSelector:
@selector(busyButClickableCursor)])
1032 pCursor = [NSCursor performSelector:@selector(busyButClickableCursor)];
1035 if ([NSCursor respondsToSelector:
@selector(_helpCursor)])
1036 pCursor = [NSCursor performSelector:@selector(_helpCursor)];
1037 helpRequested =
true;
1039 default: pCursor = [NSCursor arrowCursor];
break;
1042 if ([NSCursor respondsToSelector:
@selector(helpCursorShown)])
1043 helpCurrent = [NSCursor performSelector:@selector(helpCursorShown)];
1045 if (helpCurrent && !helpRequested)
1048#pragma clang diagnostic push
1049#pragma clang diagnostic ignored "-Wobjc-method-access"
1050 [NSCursor _setHelpCursor : false];
1051#pragma clang diagnostic pop
1055 pCursor = [NSCursor arrowCursor];
1060- (void) removeFromSuperview
1063 [
self endUserInput ];
1065 mGraphics->SetPlatformContext(
nullptr);
1068 mGraphics->GetDelegate()->CloseWindow();
1069 [
super removeFromSuperview];
1072- (void) controlTextDidEndEditing: (NSNotification*) aNotification
1074 char* txt = (
char*)[[mTextFieldView stringValue] UTF8String];
1076 mGraphics->SetControlValueAfterTextEdit(txt);
1077 mGraphics->SetAllControlsDirty();
1079 [
self endUserInput ];
1084 IGRAPHICS_MENU_RCVR* pDummyView = [[[IGRAPHICS_MENU_RCVR alloc] initWithFrame:bounds] autorelease];
1085 NSMenu* pNSMenu = [[[IGRAPHICS_MENU alloc] initWithIPopupMenuAndReceiver:&menu : pDummyView] autorelease];
1086 NSPoint wp = {bounds.origin.x, bounds.origin.y + bounds.size.height + 4};
1088 NSMenuItem* pSelectedItem = nil;
1090 auto selectedItemIdx = menu.GetChosenItemIdx();
1092 if (selectedItemIdx > -1)
1094 pSelectedItem = [pNSMenu itemAtIndex:selectedItemIdx];
1097 if (pSelectedItem != nil)
1099 wp = {bounds.origin.x, bounds.origin.y};
1102 [pNSMenu popUpMenuPositioningItem:pSelectedItem atLocation:wp inView:self];
1104 NSMenuItem* pChosenItem = [pDummyView menuItem];
1105 NSMenu* pChosenMenu = [pChosenItem menu];
1106 IPopupMenu* pIPopupMenu = [(IGRAPHICS_MENU*) pChosenMenu iPopupMenu];
1108 long chosenItemIdx = [pChosenMenu indexOfItem: pChosenItem];
1110 if (chosenItemIdx > -1 && pIPopupMenu)
1112 pIPopupMenu->SetChosenItemIdx((
int) chosenItemIdx);
1119- (void) createTextEntry: (
int) paramIdx : (const
IText&) text : (const
char*) str : (
int) length : (NSRect) areaRect;
1124 mTextFieldView = [[IGRAPHICS_TEXTFIELD alloc] initWithFrame: areaRect];
1126 if (text.mVAlign == EVAlign::Middle)
1128 IGRAPHICS_TEXTFIELDCELL* pCell = [[IGRAPHICS_TEXTFIELDCELL alloc] initTextCell:@"textfield"];
1129 [mTextFieldView setCell: pCell];
1130 [mTextFieldView setEditable: TRUE];
1131 [mTextFieldView setDrawsBackground: TRUE];
1134 CoreTextFontDescriptor* CTFontDescriptor = CoreTextHelpers::GetCTFontDescriptor(text, sFontDescriptorCache);
1135 double ratio = CTFontDescriptor->GetEMRatio() * mGraphics->GetDrawScale();
1136 NSFontDescriptor* fontDescriptor = (NSFontDescriptor*) CTFontDescriptor->GetDescriptor();
1137 NSFont* font = [NSFont fontWithDescriptor: fontDescriptor size: text.mSize * ratio];
1138 [mTextFieldView setFont: font];
1140 switch (text.mAlign)
1143 [mTextFieldView setAlignment: NSLeftTextAlignment];
1145 case EAlign::Center:
1146 [mTextFieldView setAlignment: NSCenterTextAlignment];
1149 [mTextFieldView setAlignment: NSRightTextAlignment];
1155 const IParam* pParam = paramIdx > kNoParameter ? mGraphics->GetDelegate()->GetParam(paramIdx) :
nullptr;
1160 NSMutableCharacterSet *characterSet = [[NSMutableCharacterSet alloc] init];
1162 switch ( pParam->
Type() )
1164 case IParam::kTypeEnum:
1165 case IParam::kTypeInt:
1166 case IParam::kTypeBool:
1167 [characterSet addCharactersInString:@"0123456789-+"];
1169 case IParam::kTypeDouble:
1170 [characterSet addCharactersInString:@"0123456789.-+"];
1176 [mTextFieldView setFormatter:[[[IGRAPHICS_FORMATTER alloc] init] autorelease]];
1177 [[mTextFieldView formatter] setAcceptableCharacterSet:characterSet];
1178 [[mTextFieldView formatter] setMaximumLength:length];
1179 [characterSet release];
1182 [[mTextFieldView cell] setLineBreakMode: NSLineBreakByTruncatingTail];
1183 [mTextFieldView setAllowsEditingTextAttributes:NO];
1184 [mTextFieldView setTextColor:ToNSColor(text.mTextEntryFGColor)];
1185 [mTextFieldView setBackgroundColor:ToNSColor(text.mTextEntryBGColor)];
1187 [mTextFieldView setStringValue: [NSString stringWithCString:str encoding:NSUTF8StringEncoding]];
1189#ifndef COCOA_TEXTENTRY_BORDERED
1190 [mTextFieldView setBordered: NO];
1191 [mTextFieldView setFocusRingType:NSFocusRingTypeNone];
1194 [mTextFieldView setDelegate: self];
1196 [
self addSubview: mTextFieldView];
1197 NSWindow* pWindow = [
self window];
1198 [pWindow makeKeyAndOrderFront:nil];
1199 [pWindow makeFirstResponder: mTextFieldView];
1202- (void) endUserInput
1204 [mTextFieldView setDelegate: nil];
1205 [mTextFieldView removeFromSuperview];
1207 NSWindow* pWindow = [
self window];
1208 [pWindow makeFirstResponder: self];
1210 mTextFieldView =
nullptr;
1211 mGraphics->ClearInTextEntryControl();
1214- (BOOL) promptForColor: (
IColor&) color : (IColorPickerHandlerFunc) func;
1216 NSColorPanel* colorPanel = [NSColorPanel sharedColorPanel];
1217 mColorPickerFunc = func;
1219 [colorPanel setTarget:self];
1220 [colorPanel setShowsAlpha: TRUE];
1221 [colorPanel setAction:@selector(onColorPicked:)];
1222 [colorPanel setColor:ToNSColor(color)];
1223 [colorPanel orderFront:nil];
1225 return colorPanel != nil;
1228- (void) onColorPicked: (NSColorPanel*) pColorPanel
1230 mColorPickerFunc(FromNSColor([pColorPanel color]));
1233- (NSString*) view: (NSView*) pView stringForToolTip: (NSToolTipTag) tag point: (NSPoint) point userData: (
void*) pData
1235 int c = mGraphics ? GetMouseOver(mGraphics) : -1;
1236 if (c < 0)
return @"";
1238 const char* tooltip = mGraphics->GetControl(c)->GetTooltip();
1239 return CStringHasContents(tooltip) ? [NSString stringWithCString:tooltip encoding:NSUTF8StringEncoding] :
@"";
1242- (void) registerToolTip: (
IRECT&) bounds
1244 [
self addToolTipRect: ToNSRect(mGraphics, bounds) owner: self userData: nil];
1247- (NSDragOperation) draggingEntered: (
id<NSDraggingInfo>) sender
1249 NSPasteboard *pPasteBoard = [sender draggingPasteboard];
1251 if ([[pPasteBoard types] containsObject:NSFilenamesPboardType])
1252 return NSDragOperationGeneric;
1254 return NSDragOperationNone;
1257- (BOOL) performDragOperation: (
id<NSDraggingInfo>) sender
1259 NSPasteboard* pPasteBoard = [sender draggingPasteboard];
1261 if ([[pPasteBoard types] containsObject:NSFilenamesPboardType])
1263 NSArray* pFiles = [pPasteBoard propertyListForType:NSFilenamesPboardType];
1264 NSPoint point = [sender draggingLocation];
1265 NSPoint relativePoint = [
self convertPoint: point fromView:nil];
1267 float x = relativePoint.x;
1268 float y = relativePoint.y;
1269 if ([pFiles count] == 1)
1271 NSString* pFirstFile = [pFiles firstObject];
1272 mGraphics->OnDrop([pFirstFile UTF8String], x, y);
1274 else if ([pFiles count] > 1)
1276 std::vector<const char*> paths([pFiles count]);
1277 for (
auto i = 0; i < [pFiles count]; i++)
1279 NSString* pFile = [pFiles objectAtIndex: i];
1280 paths[i] = [pFile UTF8String];
1282 mGraphics->OnDropMultiple(paths, x, y);
1288- (NSDragOperation)draggingSession:(NSDraggingSession*) session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
1290 return NSDragOperationCopy;
1293#ifdef IGRAPHICS_METAL
1294- (void) frameDidChange:(NSNotification*) pNotification
1296 CGFloat scale = [[
self window] backingScaleFactor];
1298 [(CAMetalLayer*)[
self layer] setDrawableSize:CGSizeMake(self.frame.size.width * scale,
1299 self.frame.size.height * scale)];
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.
void Clear()
Set all fields of this IRECT to 0.
IText is used to manage font and text/text entry style for a piece of text on the UI,...