iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
IGraphicsMac_view.mm
1/*
2 ==============================================================================
3
4 This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5
6 See LICENSE.txt for more info.
7
8 ==============================================================================
9*/
10
11#import <QuartzCore/QuartzCore.h>
12
13#if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
14#import <Metal/Metal.h>
15#endif
16
17#include "wdlutf8.h"
18
19#import "IGraphicsMac_view.h"
20#include "IControl.h"
21#include "IPlugParameter.h"
22#include "IPlugLogger.h"
23
24using namespace iplug;
25using namespace igraphics;
26
27static int MacKeyCodeToVK(int code)
28{
29 switch (code)
30 {
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;
76 }
77 return kVK_NONE;
78}
79
80static int MacKeyEventToVK(NSEvent* pEvent, int& flag)
81{
82 int code = kVK_NONE;
83
84 const NSInteger mod = [pEvent modifierFlags];
85
86 if (mod & NSShiftKeyMask) flag |= kFSHIFT;
87 if (mod & NSCommandKeyMask) flag |= kFCONTROL;
88 if (mod & NSAlternateKeyMask) flag |= kFALT;
89 if ((mod & NSControlKeyMask) /*&& !IsRightClickEmulateEnabled()*/) flag |= kFLWIN;
90
91 int rawcode = [pEvent keyCode];
92
93 code = MacKeyCodeToVK(rawcode);
94 if (code == kVK_NONE)
95 {
96 NSString *str = NULL;
97
98 if (!str || ![str length]) str = [pEvent charactersIgnoringModifiers];
99
100 if (!str || ![str length])
101 {
102 if (!code)
103 {
104 code = 1024 + rawcode; // raw code
105 flag |= kFVIRTKEY;
106 }
107 }
108 else
109 {
110 code = [str characterAtIndex:0];
111 if (code >= NSF1FunctionKey && code <= NSF24FunctionKey)
112 {
113 flag |= kFVIRTKEY;
114 code += kVK_F1 - NSF1FunctionKey;
115 }
116 else
117 {
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;
121 }
122 }
123 }
124 else
125 {
126 flag |= kFVIRTKEY;
127 if (code == 8) code = '\b';
128 }
129
130 if (!(flag & kFVIRTKEY)) flag &= ~kFSHIFT;
131
132 return code;
133}
134
135#ifndef IGRAPHICS_MENU_RCVR
136#warning The iPlug2 Obj-C namespace is not customized. Did you forget to include IPlugOBJCPrefix.pch?
137#endif
138
139@implementation IGRAPHICS_MENU_RCVR
140
141- (NSMenuItem*) menuItem
142{
143 return nsMenuItem;
144}
145
146- (void) onMenuSelection:(id) sender
147{
148 nsMenuItem = sender;
149}
150
151@end
152
153@implementation IGRAPHICS_MENU
154
155- (id) initWithIPopupMenuAndReceiver: (IPopupMenu*) pMenu : (NSView*) pView
156{
157 [self initWithTitle: @""];
158
159 NSMenuItem* nsMenuItem = nil;
160 NSMutableString* nsMenuItemTitle = nil;
161
162 [self setAutoenablesItems:NO];
163
164 int numItems = pMenu->NItems();
165
166 for (int i = 0; i < numItems; ++i)
167 {
168 IPopupMenu::Item* pMenuItem = pMenu->GetItem(i);
169
170 nsMenuItemTitle = [[[NSMutableString alloc] initWithUTF8String:pMenuItem->GetText()] autorelease];
171
172 if (pMenu->GetPrefix())
173 {
174 NSString* prefixString = 0;
175
176 switch (pMenu->GetPrefix())
177 {
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;
182 }
183
184 [nsMenuItemTitle insertString:prefixString atIndex:0];
185 }
186
187 if (pMenuItem->GetIsSeparator())
188 {
189 [self addItem:[NSMenuItem separatorItem]];
190 }
191 else if (pMenuItem->GetSubmenu())
192 {
193 nsMenuItem = [self addItemWithTitle:nsMenuItemTitle action:nil keyEquivalent:@""];
194 NSMenu* subMenu = [[IGRAPHICS_MENU alloc] initWithIPopupMenuAndReceiver:pMenuItem->GetSubmenu() :pView];
195 [self setSubmenu: subMenu forItem:nsMenuItem];
196 [subMenu release];
197 }
198 else
199 {
200 nsMenuItem = [self addItemWithTitle:nsMenuItemTitle action:@selector(onMenuSelection:) keyEquivalent:@""];
201
202 [nsMenuItem setTarget:pView];
203 }
204
205 if (nsMenuItem && !pMenuItem->GetIsSeparator())
206 {
207 [nsMenuItem setIndentationLevel:pMenuItem->GetIsTitle() ? 1 : 0 ];
208 [nsMenuItem setEnabled:pMenuItem->GetEnabled() ? YES : NO];
209 [nsMenuItem setState:pMenuItem->GetChecked() ? NSOnState : NSOffState];
210 }
211 }
212
213 mIPopupMenu = pMenu;
214
215 return self;
216}
217
218- (IPopupMenu*) iPopupMenu
219{
220 return mIPopupMenu;
221}
222
223@end
224
225@implementation IGRAPHICS_TEXTFIELD
226
227- (bool) becomeFirstResponder;
228{
229 bool success = [super becomeFirstResponder];
230 if (success)
231 {
232 NSTextView *textField = (NSTextView*) [self currentEditor];
233 if( [textField respondsToSelector: @selector(setInsertionPointColor:)] )
234 [textField setInsertionPointColor: [self textColor]];
235 }
236 return success;
237}
238
239@end
240
241// IGRAPHICS_TEXTFIELDCELL based on...
242
243// https://red-sweater.com/blog/148/what-a-difference-a-cell-makes
244
245// This source code is provided to you compliments of Red Sweater Software under the license as described below. NOTE: This is the MIT License.
246//
247// Copyright (c) 2006 Red Sweater Software
248//
249// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
250//
251// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
252//
253// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
254
255@implementation IGRAPHICS_TEXTFIELDCELL
256
257- (NSRect) drawingRectForBounds: (NSRect) inRect
258{
259 // Get the parent's idea of where we should draw
260 NSRect outRect = [super drawingRectForBounds:inRect];
261
262 // When the text field is being
263 // edited or selected, we have to turn off the magic because it screws up
264 // the configuration of the field editor. We sneak around this by
265 // intercepting selectWithFrame and editWithFrame and sneaking a
266 // reduced, centered rect in at the last minute.
267 if (mIsEditingOrSelecting == NO)
268 {
269 // Get our ideal size for current text
270 NSSize textSize = [self cellSize];
271
272 // Center that in the proposed rect
273 float heightDelta = outRect.size.height - textSize.height;
274
275 outRect.size.height -= heightDelta;
276 outRect.origin.y += (heightDelta / 2);
277 }
278
279 return outRect;
280}
281
282- (void) selectWithFrame: (NSRect) aRect inView: (NSView*) controlView editor: (NSText*) textObj delegate: (id) anObject start: (NSInteger) selStart length: (NSInteger) selLength
283{
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;
288}
289
290- (void) editWithFrame: (NSRect) aRect inView: (NSView*) controlView editor: (NSText*) textObj delegate: (id) anObject event: (NSEvent*) theEvent
291{
292 aRect = [self drawingRectForBounds:aRect];
293 mIsEditingOrSelecting = YES;
294 [super editWithFrame:aRect inView:controlView editor:textObj delegate:anObject event:theEvent];
295 mIsEditingOrSelecting = NO;
296}
297@end
298
299
300@implementation IGRAPHICS_FORMATTER
301
302- (void) dealloc
303{
304 [filterCharacterSet release];
305 [super dealloc];
306}
307
308- (BOOL) isPartialStringValid:(NSString*) partialString newEditingString:(NSString**) newString errorDescription:(NSString**) error
309{
310 if (filterCharacterSet != nil)
311 {
312 int i = 0;
313 int len = (int) [partialString length];
314
315 for (i = 0; i < len; i++)
316 {
317 if (![filterCharacterSet characterIsMember:[partialString characterAtIndex:i]])
318 {
319 return NO;
320 }
321 }
322 }
323
324 if (maxLength)
325 {
326 if ([partialString length] > maxLength)
327 {
328 return NO;
329 }
330 }
331
332 if (maxValue && [partialString intValue] > maxValue)
333 {
334 return NO;
335 }
336
337 return YES;
338}
339
340- (void) setAcceptableCharacterSet: (NSCharacterSet*) inCharacterSet
341{
342 [inCharacterSet retain];
343 [filterCharacterSet release];
344 filterCharacterSet = inCharacterSet;
345}
346
347- (void) setMaximumLength: (int) inLength
348{
349 maxLength = inLength;
350}
351
352- (void) setMaximumValue: (int) inValue
353{
354 maxValue = inValue;
355}
356
357- (NSString*) stringForObjectValue: (id) anObject
358{
359 if ([anObject isKindOfClass:[NSString class]])
360 {
361 return anObject;
362 }
363
364 return nil;
365}
366
367- (BOOL) getObjectValue: (id*) anObject forString:(NSString*) string errorDescription: (NSString **) error
368{
369 if (anObject && string)
370 {
371 *anObject = [NSString stringWithString:string];
372 }
373
374 return YES;
375}
376@end
377
378#pragma mark -
379
380extern StaticStorage<CoreTextFontDescriptor> sFontDescriptorCache;
381
382@implementation IGRAPHICS_VIEW
383
384- (CALayer *)makeBackingLayer
385{
386 // CAMetalLayer is correct for both Metal and GLES - ANGLE uses Metal backend on macOS
387 // NB: this file is not ARC; the caller retains, so the layer must be autoreleased
388 // or it is stranded (with its drawable pool) every time the window closes
389#if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
390 return [[[CAMetalLayer alloc] init] autorelease];
391#else
392 return [[[CALayer alloc] init] autorelease];
393#endif
394}
395
396- (id) initWithIGraphics: (IGraphicsMac*) pGraphics
397{
398 TRACE
399
400 mGraphics = pGraphics;
401 NSRect r = NSMakeRect(0.f, 0.f, (float) pGraphics->WindowWidth(), (float) pGraphics->WindowHeight());
402 self = [super initWithFrame:r];
403
404 mMouseOutDuringDrag = false;
405
406 self.layer.frame = r;
407 self.wantsLayer = YES;
408 self.layer.opaque = YES;
409 self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
410 [self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
411
412 #if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
413 CAMetalLayer* mtlLayer = (CAMetalLayer*) self.layer;
414 [mtlLayer setPixelFormat:MTLPixelFormatBGRA8Unorm];
415 mtlLayer.device = [MTLCreateSystemDefaultDevice() autorelease]; // Create-rule +1, property assignment retains
416 mtlLayer.framebufferOnly = YES;
417 #elif defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
418 NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy;
419 #if defined IGRAPHICS_GL3
420 profile = (NSOpenGLPixelFormatAttribute)NSOpenGLProfileVersion3_2Core;
421 #endif
422 const NSOpenGLPixelFormatAttribute attrs[] = {
423 NSOpenGLPFAAccelerated,
424 NSOpenGLPFANoRecovery,
425 NSOpenGLPFADoubleBuffer,
426 NSOpenGLPFAAlphaSize, 8,
427 NSOpenGLPFAColorSize, 24,
428 NSOpenGLPFADepthSize, 0,
429 NSOpenGLPFAStencilSize, 8,
430 NSOpenGLPFAOpenGLProfile, profile,
431 (NSOpenGLPixelFormatAttribute) 0
432 };
433 NSOpenGLPixelFormat* pPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
434 NSOpenGLContext* pGLContext = [[NSOpenGLContext alloc] initWithFormat:pPixelFormat shareContext:nil];
435
436 #ifdef DEBUG
437 // CGLEnable([context CGLContextObj], kCGLCECrashOnRemovedFunctions); //SKIA_GL2 will crash
438 #endif
439
440 self.pixelFormat = pPixelFormat;
441 self.openGLContext = pGLContext;
442 self.wantsBestResolutionOpenGLSurface = YES;
443 #endif // IGRAPHICS_GL2 || defined IGRAPHICS_GL3
444
445 #if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
446 EGLDisplay display = eglGetPlatformDisplay(EGLenum(EGL_PLATFORM_ANGLE_ANGLE), 0, 0);
447
448 if (!display) {
449 DBGMSG("eglGetPlatformDisplay() returned error %i", eglGetError());
450 }
451
452 if (eglInitialize(display, nil, nil) == 0) {
453 DBGMSG("eglInitialize() returned error %i", eglGetError());
454 }
455
456 const EGLint configAttribs[9] = {
457 EGL_BLUE_SIZE, 8,
458 EGL_GREEN_SIZE, 8,
459 EGL_RED_SIZE, 8,
460 EGL_DEPTH_SIZE, 24,
461 EGL_NONE};
462 EGLint numConfigs = 0;
463 EGLConfig configs[1];
464
465 if (eglChooseConfig(display, configAttribs, configs, 1, &numConfigs) == 0) {
466 DBGMSG("eglChooseConfig() returned error %i", eglGetError());
467 }
468
469 if (!configs[0]) {
470 DBGMSG("Empty config returned in eglChooseConfig()");
471 }
472
473 #if defined IGRAPHICS_GLES2
474 const EGLint contextAttribs [5] = {
475 EGL_CONTEXT_MAJOR_VERSION, 2,
476 EGL_CONTEXT_MINOR_VERSION, 0,
477 EGL_NONE,
478 };
479 #elif defined IGRAPHICS_GLES3
480 const EGLint contextAttribs [5] = {
481 EGL_CONTEXT_MAJOR_VERSION, 3,
482 EGL_CONTEXT_MINOR_VERSION, 0,
483 EGL_NONE,
484 };
485 #endif
486
487 EGLContext context = eglCreateContext(display, configs[0], nullptr, contextAttribs);
488
489 if (!context) {
490 DBGMSG("eglCreateContext() returned error %d", eglGetError());
491 }
492
493 EGLSurface surface = eglCreateWindowSurface(display, configs[0], (__bridge EGLNativeWindowType) [self layer], nullptr);
494
495 if (!surface) {
496 DBGMSG("eglCreateWindowSurface() returned error %d", eglGetError());
497 }
498
499 mEGLSurface = surface;
500 mEGLDisplay = display;
501 mEGLContext = context;
502 #endif
503
504
505 #if !defined IGRAPHICS_GL2 && !defined IGRAPHICS_GL3
506 [self setTimer];
507 #endif
508
509 return self;
510}
511
512#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
513- (void) prepareOpenGL
514{
515 [super prepareOpenGL];
516
517 [self activateGLContext];
518
519 // Synchronize buffer swaps with vertical refresh rate
520 GLint swapInt = 1;
521 [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
522
523 [self setTimer];
524}
525#endif
526
527static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
528{
529 dispatch_source_t source = (dispatch_source_t) displayLinkContext;
530 dispatch_source_merge_data(source, 1);
531
532 return kCVReturnSuccess;
533}
534
535- (void) onTimer: (NSTimer*) pTimer
536{
537 [self render];
538}
539
540- (void) setTimer
541{
542#ifdef IGRAPHICS_CVDISPLAYLINK
543 mDisplaySource = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
544 dispatch_source_set_event_handler(mDisplaySource, ^(){
545 [self render];
546 });
547 dispatch_resume(mDisplaySource);
548
549 CVReturn cvReturn;
550
551 cvReturn = CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink);
552
553 assert(cvReturn == kCVReturnSuccess);
554
555 cvReturn = CVDisplayLinkSetOutputCallback(mDisplayLink, &displayLinkCallback, (void*) mDisplaySource);
556 assert(cvReturn == kCVReturnSuccess);
557
558 #if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
559 CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
560 CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
561 CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(mDisplayLink, cglContext, cglPixelFormat);
562 #endif
563
564 CGDirectDisplayID viewDisplayID =
565 (CGDirectDisplayID) [self.window.screen.deviceDescription[@"NSScreenNumber"] unsignedIntegerValue];;
566
567 cvReturn = CVDisplayLinkSetCurrentCGDisplay(mDisplayLink, viewDisplayID);
568
569 assert(cvReturn == kCVReturnSuccess);
570
571 CVDisplayLinkStart(mDisplayLink);
572#else
573 double sec = 1.0 / (double) mGraphics->FPS();
574 mTimer = [NSTimer timerWithTimeInterval:sec target:self selector:@selector(onTimer:) userInfo:nil repeats:YES];
575 [[NSRunLoop currentRunLoop] addTimer: mTimer forMode: (NSString*) kCFRunLoopCommonModes];
576#endif
577}
578
579- (void) killTimer
580{
581#ifdef IGRAPHICS_CVDISPLAYLINK
582 CVDisplayLinkStop(mDisplayLink);
583 dispatch_source_cancel(mDisplaySource);
584 CVDisplayLinkRelease(mDisplayLink);
585 mDisplayLink = nil;
586#else
587 [mTimer invalidate];
588 mTimer = nullptr;
589#endif
590}
591
592- (void) dealloc
593{
594 if([NSColorPanel sharedColorPanelExists])
595 [[NSColorPanel sharedColorPanel] close];
596
597 mColorPickerFunc = nullptr;
598 [mMoveCursor release];
599 [mTrackingArea release];
600 [[NSNotificationCenter defaultCenter] removeObserver:self];
601 [super dealloc];
602}
603
604
605- (BOOL) isOpaque
606{
607 return mGraphics ? YES : NO;
608}
609
610- (BOOL) isFlipped
611{
612 return YES;
613}
614
615- (void) viewDidChangeEffectiveAppearance
616{
617 if (@available(macOS 10.14, *)) {
618 BOOL isDarkMode = [[[self effectiveAppearance] name] isEqualToString: (NSAppearanceNameDarkAqua)];
619 mGraphics->OnAppearanceChanged(isDarkMode ? EUIAppearance::Dark : EUIAppearance::Light);
620 }
621}
622
623- (BOOL) acceptsFirstResponder
624{
625 return YES;
626}
627
628- (BOOL) acceptsFirstMouse: (NSEvent*) pEvent
629{
630 return YES;
631}
632
633- (void) viewDidMoveToWindow
634{
635 NSWindow* pWindow = [self window];
636
637 if (pWindow)
638 {
639 [pWindow makeFirstResponder: self];
640 [pWindow setAcceptsMouseMovedEvents: YES];
641
642 CGFloat newScale = [pWindow backingScaleFactor];
643
644 if (mGraphics)
645 mGraphics->SetScreenScale(newScale);
646
647 #if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
648 [[NSNotificationCenter defaultCenter] addObserver:self
649 selector:@selector(frameDidChange:)
650 name:NSViewFrameDidChangeNotification
651 object:self];
652 #endif
653
654 #if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
655 [[NSNotificationCenter defaultCenter] addObserver:self
656 selector:@selector(frameDidChange:)
657 name:NSViewGlobalFrameDidChangeNotification
658 object:self];
659 #endif
660
661// [[NSNotificationCenter defaultCenter] addObserver:self
662// selector:@selector(windowResized:) name:NSWindowDidEndLiveResizeNotification
663// object:pWindow];
664//
665// [[NSNotificationCenter defaultCenter] addObserver:self
666// selector:@selector(windowFullscreened:) name:NSWindowDidEnterFullScreenNotification
667// object:pWindow];
668//
669// [[NSNotificationCenter defaultCenter] addObserver:self
670// selector:@selector(windowFullscreened:) name:NSWindowDidExitFullScreenNotification
671// object:pWindow];
672 }
673}
674
675- (void) viewDidChangeBackingProperties:(NSNotification*) pNotification
676{
677 NSWindow* pWindow = [self window];
678
679 if (!pWindow)
680 return;
681
682 CGFloat newScale = [pWindow backingScaleFactor];
683
684 mGraphics->SetPlatformContext(nullptr);
685
686 if (newScale != mGraphics->GetScreenScale())
687 mGraphics->SetScreenScale(newScale);
688
689 self.layer.contentsScale = [[self window] backingScaleFactor];
690
691#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
692 self.layer.contentsScale = 1./newScale;
693#elif defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
694 [(CAMetalLayer*)[self layer] setDrawableSize:CGSizeMake(self.frame.size.width * newScale,
695 self.frame.size.height * newScale)];
696#endif
697}
698
699- (CGContextRef) getCGContextRef
700{
701 CGContextRef pCGC = [NSGraphicsContext currentContext].CGContext;
702 return [NSGraphicsContext graphicsContextWithCGContext: pCGC flipped: YES].CGContext;
703}
704
705// not called for layer backed views
706- (void) drawRect: (NSRect) bounds
707{
708 #if defined IGRAPHICS_CPU
709 if (mGraphics)
710 {
711 mGraphics->SetPlatformContext([self getCGContextRef]);
712
713 if (mGraphics->GetPlatformContext())
714 {
715 const NSRect *rects;
716 NSInteger numRects;
717 [self getRectsBeingDrawn:&rects count:&numRects];
718 IRECTList drawRects;
719
720 for (int i = 0; i < numRects; i++)
721 drawRects.Add(ToIRECT(mGraphics, &rects[i]));
722
723 mGraphics->Draw(drawRects);
724 }
725 }
726 #endif
727}
728
729- (void) render
730{
731 mDirtyRects.Clear();
732
733 if (mGraphics->IsDirty(mDirtyRects))
734 {
735 mGraphics->SetAllControlsClean();
736
737 #if defined IGRAPHICS_CPU
738 for (int i = 0; i < mDirtyRects.Size(); i++)
739 [self setNeedsDisplayInRect:ToNSRect(mGraphics, mDirtyRects.Get(i))];
740 #else
741 IGraphics::ScopedGLContext scopedGLCtx {mGraphics};
742 // so just draw on each frame, if something is dirty
743 mGraphics->Draw(mDirtyRects);
744 [self swapBuffers]; // No-op for Metal
745 #endif
746 }
747}
748
749- (void) getMouseXY: (NSEvent*) pEvent : (float&) x : (float&) y
750{
751 if (mGraphics)
752 {
753 NSPoint pt = [self convertPoint:[pEvent locationInWindow] fromView:nil];
754 x = pt.x / mGraphics->GetDrawScale();
755 y = pt.y / mGraphics->GetDrawScale();
756
757 mGraphics->DoCursorLock(x, y, mPrevX, mPrevY);
758 mGraphics->SetTabletInput(pEvent.subtype == NSTabletPointEventSubtype);
759 }
760}
761
762- (IMouseInfo) getMouseLeft: (NSEvent*) pEvent
763{
764 IMouseInfo info;
765 [self getMouseXY:pEvent : info.x : info.y];
766 int mods = (int) [pEvent modifierFlags];
767 info.ms = IMouseMod(true, (mods & NSCommandKeyMask), (mods & NSShiftKeyMask), (mods & NSControlKeyMask), (mods & NSAlternateKeyMask));
768
769 return info;
770}
771
772- (IMouseInfo) getMouseRight: (NSEvent*) pEvent
773{
774 IMouseInfo info;
775 [self getMouseXY:pEvent : info.x : info.y];
776 int mods = (int) [pEvent modifierFlags];
777 info.ms = IMouseMod(false, true, (mods & NSShiftKeyMask), (mods & NSControlKeyMask), (mods & NSAlternateKeyMask));
778
779 return info;
780}
781
782- (void) updateTrackingAreas
783{
784 [super updateTrackingAreas]; // This is needed to get mouseEntered and mouseExited
785
786 if (mTrackingArea != nil)
787 {
788 [self removeTrackingArea:mTrackingArea];
789 [mTrackingArea release];
790 }
791
792 int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingEnabledDuringMouseDrag);
793 mTrackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds] options:opts owner:self userInfo:nil];
794 [self addTrackingArea:mTrackingArea];
795}
796
797- (void) mouseEntered: (NSEvent*) pEvent
798{
799 mMouseOutDuringDrag = false;
800
801 if (mGraphics)
802 {
803 mGraphics->OnSetCursor();
804 }
805}
806
807- (void) mouseExited: (NSEvent*) pEvent
808{
809 if (mGraphics)
810 {
811 if (!mGraphics->ControlIsCaptured())
812 {
813 mGraphics->OnMouseOut();
814 }
815 else
816 {
817 mMouseOutDuringDrag = true;
818 }
819 }
820}
821
822- (void) mouseDown: (NSEvent*) pEvent
823{
824 IMouseInfo info = [self getMouseLeft:pEvent];
825 if (mGraphics)
826 {
827 if (([pEvent clickCount] - 1) % 2)
828 {
829 mGraphics->OnMouseDblClick(info.x, info.y, info.ms);
830 }
831 else
832 {
833 std::vector<IMouseInfo> list {info};
834 mGraphics->OnMouseDown(list);
835 }
836 }
837}
838
839- (void) mouseUp: (NSEvent*) pEvent
840{
841 IMouseInfo info = [self getMouseLeft:pEvent];
842 if (mGraphics)
843 {
844 std::vector<IMouseInfo> list {info};
845 mGraphics->OnMouseUp(list);
846
847 if (mMouseOutDuringDrag)
848 {
849 mGraphics->OnMouseOut();
850 mMouseOutDuringDrag = false;
851 }
852 }
853}
854
855- (void) mouseDragged: (NSEvent*) pEvent
856{
857 // Cache previous values before retrieving the new mouse position (which will update them)
858 float prevX = mPrevX;
859 float prevY = mPrevY;
860 IMouseInfo info = [self getMouseLeft:pEvent];
861 if (mGraphics && !mGraphics->IsInPlatformTextEntry())
862 {
863 info.dX = info.x - prevX;
864 info.dY = info.y - prevY;
865 std::vector<IMouseInfo> list {info};
866 mGraphics->OnMouseDrag(list);
867 }
868}
869
870- (void) rightMouseDown: (NSEvent*) pEvent
871{
872 IMouseInfo info = [self getMouseRight:pEvent];
873 if (mGraphics)
874 {
875 if (([pEvent clickCount] - 1) % 2)
876 {
877 mGraphics->OnMouseDblClick(info.x, info.y, info.ms);
878 }
879 else
880 {
881 std::vector<IMouseInfo> list {info};
882 mGraphics->OnMouseDown(list);
883 }
884 }
885}
886
887- (void) rightMouseUp: (NSEvent*) pEvent
888{
889 IMouseInfo info = [self getMouseRight:pEvent];
890 if (mGraphics)
891 {
892 std::vector<IMouseInfo> list {info};
893 mGraphics->OnMouseUp(list);
894 }
895}
896
897- (void) rightMouseDragged: (NSEvent*) pEvent
898{
899 // Cache previous values before retrieving the new mouse position (which will update them)
900 float prevX = mPrevX;
901 float prevY = mPrevY;
902 IMouseInfo info = [self getMouseRight:pEvent];
903
904 if (mGraphics && !mTextFieldView)
905 {
906 info.dX = info.x - prevX;
907 info.dY = info.y - prevY;
908 std::vector<IMouseInfo> list {info};
909 mGraphics->OnMouseDrag(list);
910 }
911}
912
913- (void) mouseMoved: (NSEvent*) pEvent
914{
915 IMouseInfo info = [self getMouseLeft:pEvent];
916 if (mGraphics)
917 mGraphics->OnMouseOver(info.x, info.y, info.ms);
918}
919
920- (void) keyDown: (NSEvent*) pEvent
921{
922 int flag = 0;
923 int code = MacKeyEventToVK(pEvent, flag);
924 NSString *s = [pEvent charactersIgnoringModifiers];
925
926 unichar c = 0;
927
928 if ([s length] == 1)
929 c = [s characterAtIndex:0];
930
931 if(!static_cast<bool>(flag & kFVIRTKEY))
932 {
933 code = kVK_NONE;
934 }
935
936 char utf8[5];
937 WDL_MakeUTFChar(utf8, c, 4);
938
939 IKeyPress keyPress {utf8, code, static_cast<bool>(flag & kFSHIFT),
940 static_cast<bool>(flag & kFCONTROL),
941 static_cast<bool>(flag & kFALT)};
942
943 bool handle = mGraphics->OnKeyDown(mPrevX, mPrevY, keyPress);
944
945 if (!handle)
946 {
947 [[self nextResponder] keyDown:pEvent];
948 }
949}
950
951- (void) keyUp: (NSEvent*) pEvent
952{
953 int flag = 0;
954 int code = MacKeyEventToVK(pEvent, flag);
955 NSString *s = [pEvent charactersIgnoringModifiers];
956
957 unichar c = 0;
958
959 if ([s length] == 1)
960 c = [s characterAtIndex:0];
961
962 if(!static_cast<bool>(flag & kFVIRTKEY))
963 {
964 code = kVK_NONE;
965 }
966
967 char utf8[5];
968 WDL_MakeUTFChar(utf8, c, 4);
969
970 IKeyPress keyPress {utf8, code, static_cast<bool>(flag & kFSHIFT),
971 static_cast<bool>(flag & kFCONTROL),
972 static_cast<bool>(flag & kFALT)};
973
974 bool handle = mGraphics->OnKeyUp(mPrevX, mPrevY, keyPress);
975
976 if (!handle)
977 {
978 [[self nextResponder] keyUp:pEvent];
979 }
980}
981
982- (void) scrollWheel: (NSEvent*) pEvent
983{
984 if (mTextFieldView) [self endUserInput ];
985 IMouseInfo info = [self getMouseLeft:pEvent];
986 float d = [pEvent deltaY];
987 if (mGraphics)
988 mGraphics->OnMouseWheel(info.x, info.y, info.ms, d);
989}
990
991static void MakeCursorFromName(NSCursor*& cursor, const char *name)
992{
993 // get paths and intialise images etc.
994 const char* basePath = "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors/";
995
996 NSString* imagePath = [NSString stringWithFormat:@"%s%s/cursor.pdf", basePath, name];
997 NSString* infoPath = [NSString stringWithFormat:@"file:%s%s/info.plist", basePath, name];
998 NSImage* fileImage = [[NSImage alloc] initByReferencingFile: imagePath];
999 NSImage *cursorImage = [[NSImage alloc] initWithSize:[fileImage size]];
1000 NSDictionary* info = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:infoPath]];
1001
1002 // get info from dictionary
1003 double hotX = [info[@"hotx-scaled"] doubleValue];
1004 double hotY = [info[@"hoty-scaled"] doubleValue];
1005 double blur = [info[@"blur"] doubleValue];
1006 double offsetX = [info[@"shadowoffsetx"] doubleValue];
1007 double offsetY = [info[@"shadowoffsety"] doubleValue];
1008 double red = [info[@"shadowcolor"][0] doubleValue];
1009 double green = [info[@"shadowcolor"][1] doubleValue];
1010 double blue = [info[@"shadowcolor"][2] doubleValue];
1011 double alpha = [info[@"shadowcolor"][3] doubleValue];
1012 CGColorRef shadowColor = CGColorCreateGenericRGB(red, green, blue, alpha);
1013
1014 for (int scale = 1; scale <= 4; scale++)
1015 {
1016 // scale
1017 NSAffineTransform* xform = [NSAffineTransform transform];
1018 [xform scaleBy:scale];
1019 id hints = @{ NSImageHintCTM: xform };
1020 CGImageRef rasterCGImage = [fileImage CGImageForProposedRect:NULL context:nil hints:hints];
1021
1022 // apply shadow
1023 size_t width = CGImageGetWidth(rasterCGImage);
1024 size_t height = CGImageGetHeight(rasterCGImage);
1025 CGSize offset = CGSize { static_cast<CGFloat>(offsetX * scale), static_cast<CGFloat>(offsetY * scale) };
1026 CGContextRef shadowContext = CGBitmapContextCreate(NULL, width, height, CGImageGetBitsPerComponent(rasterCGImage), 0, CGImageGetColorSpace(rasterCGImage), CGImageGetBitmapInfo(rasterCGImage));
1027 CGContextSetShadowWithColor(shadowContext, offset, blur * scale, shadowColor);
1028 CGContextDrawImage(shadowContext, CGRectMake(0, 0, width, height), rasterCGImage);
1029 CGImageRef shadowCGImage = CGBitmapContextCreateImage(shadowContext);
1030
1031 // add to cursor inmge
1032 NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithCGImage:shadowCGImage];
1033 [rep setSize:[fileImage size]];
1034 [cursorImage addRepresentation:rep];
1035
1036 // release
1037 [rep release];
1038 CGContextRelease(shadowContext);
1039 CGImageRelease(shadowCGImage);
1040 }
1041
1042 // create cursor
1043 cursor = [[NSCursor alloc] initWithImage:cursorImage hotSpot:NSMakePoint(hotX, hotY)];
1044
1045 // release
1046 [cursorImage release];
1047 [fileImage release];
1048 CGColorRelease(shadowColor);
1049}
1050
1051- (void) setMouseCursor: (ECursor) cursorType
1052{
1053 NSCursor* pCursor = nullptr;
1054
1055 bool helpCurrent = false;
1056 bool helpRequested = false;
1057
1058 switch (cursorType)
1059 {
1060 case ECursor::ARROW: pCursor = [NSCursor arrowCursor]; break;
1061 case ECursor::IBEAM: pCursor = [NSCursor IBeamCursor]; break;
1062 case ECursor::WAIT:
1063 if ([NSCursor respondsToSelector:@selector(busyButClickableCursor)])
1064 pCursor = [NSCursor performSelector:@selector(busyButClickableCursor)];
1065 break;
1066 case ECursor::CROSS: pCursor = [NSCursor crosshairCursor]; break;
1067 case ECursor::UPARROW:
1068 if ([NSCursor respondsToSelector:@selector(_windowResizeNorthCursor)])
1069 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthCursor)];
1070 else
1071 pCursor = [NSCursor resizeUpCursor];
1072 break;
1073 case ECursor::SIZENWSE:
1074 if ([NSCursor respondsToSelector:@selector(_windowResizeNorthWestSouthEastCursor)])
1075 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthWestSouthEastCursor)];
1076 break;
1077 case ECursor::SIZENESW:
1078 if ([NSCursor respondsToSelector:@selector(_windowResizeNorthEastSouthWestCursor)])
1079 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthEastSouthWestCursor)];
1080 break;
1081 case ECursor::SIZEWE:
1082 if ([NSCursor respondsToSelector:@selector(_windowResizeEastWestCursor)])
1083 pCursor = [NSCursor performSelector:@selector(_windowResizeEastWestCursor)];
1084 else
1085 pCursor = [NSCursor resizeLeftRightCursor];
1086 break;
1087 case ECursor::SIZENS:
1088 if ([NSCursor respondsToSelector:@selector(_windowResizeNorthSouthCursor)])
1089 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthSouthCursor)];
1090 else
1091 pCursor = [NSCursor resizeUpDownCursor];
1092 break;
1093 case ECursor::SIZEALL:
1094 {
1095 if (!mMoveCursor)
1096 MakeCursorFromName(mMoveCursor, "move");
1097 pCursor = mMoveCursor;
1098 break;
1099 }
1100 case ECursor::INO: pCursor = [NSCursor operationNotAllowedCursor]; break;
1101 case ECursor::HAND: pCursor = [NSCursor pointingHandCursor]; break;
1102 case ECursor::APPSTARTING:
1103 if ([NSCursor respondsToSelector:@selector(busyButClickableCursor)])
1104 pCursor = [NSCursor performSelector:@selector(busyButClickableCursor)];
1105 break;
1106 case ECursor::HELP:
1107 if ([NSCursor respondsToSelector:@selector(_helpCursor)])
1108 pCursor = [NSCursor performSelector:@selector(_helpCursor)];
1109 helpRequested = true;
1110 break;
1111 default: pCursor = [NSCursor arrowCursor]; break;
1112 }
1113
1114 if ([NSCursor respondsToSelector:@selector(helpCursorShown)])
1115 helpCurrent = [NSCursor performSelector:@selector(helpCursorShown)];
1116
1117 if (helpCurrent && !helpRequested)
1118 {
1119 // N.B. - suppress warnings for this call only
1120#pragma clang diagnostic push
1121#pragma clang diagnostic ignored "-Wobjc-method-access"
1122 [NSCursor _setHelpCursor : false];
1123#pragma clang diagnostic pop
1124 }
1125
1126 if (!pCursor)
1127 pCursor = [NSCursor arrowCursor];
1128
1129 [pCursor set];
1130}
1131
1132- (void) removeFromSuperview
1133{
1134 if (mTextFieldView)
1135 [self endUserInput ];
1136
1137 mGraphics->SetPlatformContext(nullptr);
1138
1139 //For some APIs (AUv2) this is where we know about the window being closed, close via delegate
1140 mGraphics->GetDelegate()->CloseWindow();
1141 [super removeFromSuperview];
1142}
1143
1144- (void) controlTextDidEndEditing: (NSNotification*) aNotification
1145{
1146 char* txt = (char*)[[mTextFieldView stringValue] UTF8String];
1147
1148 mGraphics->SetControlValueAfterTextEdit(txt);
1149 mGraphics->SetAllControlsDirty();
1150
1151 [self endUserInput ];
1152}
1153
1154- (IPopupMenu*) createPopupMenu: (IPopupMenu&) menu : (NSRect) bounds;
1155{
1156 IGRAPHICS_MENU_RCVR* pDummyView = [[[IGRAPHICS_MENU_RCVR alloc] initWithFrame:bounds] autorelease];
1157 NSMenu* pNSMenu = [[[IGRAPHICS_MENU alloc] initWithIPopupMenuAndReceiver:&menu : pDummyView] autorelease];
1158 NSPoint wp = {bounds.origin.x, bounds.origin.y + bounds.size.height + 4};
1159
1160 NSMenuItem* pSelectedItem = nil;
1161
1162 auto selectedItemIdx = menu.GetChosenItemIdx();
1163
1164 if (selectedItemIdx > -1)
1165 {
1166 pSelectedItem = [pNSMenu itemAtIndex:selectedItemIdx];
1167 }
1168
1169 if (pSelectedItem != nil)
1170 {
1171 wp = {bounds.origin.x, bounds.origin.y};
1172 }
1173
1174 [pNSMenu popUpMenuPositioningItem:pSelectedItem atLocation:wp inView:self];
1175
1176 NSMenuItem* pChosenItem = [pDummyView menuItem];
1177 NSMenu* pChosenMenu = [pChosenItem menu];
1178 IPopupMenu* pIPopupMenu = [(IGRAPHICS_MENU*) pChosenMenu iPopupMenu];
1179
1180 long chosenItemIdx = [pChosenMenu indexOfItem: pChosenItem];
1181
1182 if (chosenItemIdx > -1 && pIPopupMenu)
1183 {
1184 pIPopupMenu->SetChosenItemIdx((int) chosenItemIdx);
1185 return pIPopupMenu;
1186 }
1187 else
1188 return nullptr;
1189}
1190
1191- (void) createTextEntry: (int) paramIdx : (const IText&) text : (const char*) str : (int) length : (NSRect) areaRect;
1192{
1193 if (mTextFieldView)
1194 return;
1195
1196 mTextFieldView = [[IGRAPHICS_TEXTFIELD alloc] initWithFrame: areaRect];
1197
1198 if (text.mVAlign == EVAlign::Middle)
1199 {
1200 IGRAPHICS_TEXTFIELDCELL* pCell = [[IGRAPHICS_TEXTFIELDCELL alloc] initTextCell:@"textfield"];
1201 [mTextFieldView setCell: pCell];
1202 [mTextFieldView setEditable: TRUE];
1203 [mTextFieldView setDrawsBackground: TRUE];
1204 }
1205
1206 CoreTextFontDescriptor* CTFontDescriptor = CoreTextHelpers::GetCTFontDescriptor(text, sFontDescriptorCache);
1207 double ratio = CTFontDescriptor->GetEMRatio() * mGraphics->GetDrawScale();
1208 NSFontDescriptor* fontDescriptor = (NSFontDescriptor*) CTFontDescriptor->GetDescriptor();
1209 NSFont* font = [NSFont fontWithDescriptor: fontDescriptor size: text.mSize * ratio];
1210 [mTextFieldView setFont: font];
1211
1212 switch (text.mAlign)
1213 {
1214 case EAlign::Near:
1215 [mTextFieldView setAlignment: NSLeftTextAlignment];
1216 break;
1217 case EAlign::Center:
1218 [mTextFieldView setAlignment: NSCenterTextAlignment];
1219 break;
1220 case EAlign::Far:
1221 [mTextFieldView setAlignment: NSRightTextAlignment];
1222 break;
1223 default:
1224 break;
1225 }
1226
1227 const IParam* pParam = paramIdx > kNoParameter ? mGraphics->GetDelegate()->GetParam(paramIdx) : nullptr;
1228
1229 // set up formatter
1230 if (pParam)
1231 {
1232 NSMutableCharacterSet *characterSet = [[NSMutableCharacterSet alloc] init];
1233
1234 switch ( pParam->Type() )
1235 {
1236 case IParam::kTypeEnum:
1237 case IParam::kTypeInt:
1238 case IParam::kTypeBool:
1239 [characterSet addCharactersInString:@"0123456789-+"];
1240 break;
1241 case IParam::kTypeDouble:
1242 [characterSet addCharactersInString:@"0123456789.-+"];
1243 break;
1244 default:
1245 break;
1246 }
1247
1248 [mTextFieldView setFormatter:[[[IGRAPHICS_FORMATTER alloc] init] autorelease]];
1249 [[mTextFieldView formatter] setAcceptableCharacterSet:characterSet];
1250 [[mTextFieldView formatter] setMaximumLength:length];
1251 [characterSet release];
1252 }
1253
1254 [[mTextFieldView cell] setLineBreakMode: NSLineBreakByTruncatingTail];
1255 [mTextFieldView setAllowsEditingTextAttributes:NO];
1256 [mTextFieldView setTextColor:ToNSColor(text.mTextEntryFGColor)];
1257 [mTextFieldView setBackgroundColor:ToNSColor(text.mTextEntryBGColor)];
1258
1259 [mTextFieldView setStringValue: [NSString stringWithUTF8String:str]];
1260
1261#ifndef COCOA_TEXTENTRY_BORDERED
1262 [mTextFieldView setBordered: NO];
1263 [mTextFieldView setFocusRingType:NSFocusRingTypeNone];
1264#endif
1265
1266 [mTextFieldView setDelegate: self];
1267
1268 [self addSubview: mTextFieldView];
1269 NSWindow* pWindow = [self window];
1270 [pWindow makeKeyAndOrderFront:nil];
1271 [pWindow makeFirstResponder: mTextFieldView];
1272}
1273
1274- (void) endUserInput
1275{
1276 [mTextFieldView setDelegate: nil];
1277 [mTextFieldView removeFromSuperview];
1278
1279 NSWindow* pWindow = [self window];
1280 [pWindow makeFirstResponder: self];
1281
1282 mTextFieldView = nullptr;
1283 mGraphics->ClearInTextEntryControl();
1284}
1285
1286- (BOOL) promptForColor: (IColor&) color : (IColorPickerHandlerFunc) func;
1287{
1288 NSColorPanel* colorPanel = [NSColorPanel sharedColorPanel];
1289 mColorPickerFunc = func;
1290
1291 [colorPanel setTarget:self];
1292 [colorPanel setShowsAlpha: TRUE];
1293 [colorPanel setAction:@selector(onColorPicked:)];
1294 [colorPanel setColor:ToNSColor(color)];
1295 [colorPanel orderFront:nil];
1296
1297 return colorPanel != nil;
1298}
1299
1300- (void) onColorPicked: (NSColorPanel*) pColorPanel
1301{
1302 mColorPickerFunc(FromNSColor([pColorPanel color]));
1303}
1304
1305- (NSString*) view: (NSView*) pView stringForToolTip: (NSToolTipTag) tag point: (NSPoint) point userData: (void*) pData
1306{
1307 int c = mGraphics ? GetMouseOver(mGraphics) : -1;
1308 if (c < 0) return @"";
1309
1310 const char* tooltip = mGraphics->GetControl(c)->GetTooltip();
1311 return CStringHasContents(tooltip) ? [NSString stringWithUTF8String:tooltip] : @"";
1312}
1313
1314- (void) registerToolTip: (IRECT&) bounds
1315{
1316 [self addToolTipRect: ToNSRect(mGraphics, bounds) owner: self userData: nil];
1317}
1318
1319- (NSDragOperation) draggingEntered: (id<NSDraggingInfo>) sender
1320{
1321 NSPasteboard *pPasteBoard = [sender draggingPasteboard];
1322
1323 if ([[pPasteBoard types] containsObject:NSFilenamesPboardType])
1324 return NSDragOperationGeneric;
1325 else
1326 return NSDragOperationNone;
1327}
1328
1329- (BOOL) performDragOperation: (id<NSDraggingInfo>) sender
1330{
1331 NSPasteboard* pPasteBoard = [sender draggingPasteboard];
1332
1333 if ([[pPasteBoard types] containsObject:NSFilenamesPboardType])
1334 {
1335 NSArray* pFiles = [pPasteBoard propertyListForType:NSFilenamesPboardType];
1336 NSPoint point = [sender draggingLocation];
1337 NSPoint relativePoint = [self convertPoint: point fromView:nil];
1338
1339 const float scale = mGraphics->GetDrawScale();
1340 const float x = relativePoint.x / scale;
1341 const float y = relativePoint.y / scale;
1342 if ([pFiles count] == 1)
1343 {
1344 NSString* pFirstFile = [pFiles firstObject];
1345 mGraphics->OnDrop([pFirstFile UTF8String], x, y);
1346 }
1347 else if ([pFiles count] > 1)
1348 {
1349 std::vector<const char*> paths([pFiles count]);
1350 for (auto i = 0; i < [pFiles count]; i++)
1351 {
1352 NSString* pFile = [pFiles objectAtIndex: i];
1353 paths[i] = [pFile UTF8String];
1354 }
1355 mGraphics->OnDropMultiple(paths, x, y);
1356 }
1357 }
1358 return YES;
1359}
1360
1361- (NSDragOperation)draggingSession:(NSDraggingSession*) session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
1362{
1363 return NSDragOperationCopy;
1364}
1365
1366#if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1367- (void) frameDidChange:(NSNotification*) pNotification
1368{
1369 CGFloat scale = [[self window] backingScaleFactor];
1370
1371 [(CAMetalLayer*)[self layer] setDrawableSize:CGSizeMake(self.frame.size.width * scale,
1372 self.frame.size.height * scale)];
1373}
1374#endif
1375#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1376- (void) frameDidChange:(NSNotification*) pNotification
1377{
1378 [self activateGLContext];
1379}
1380#endif
1381
1382//- (void) windowResized: (NSNotification*) notification;
1383//{
1384// if(!mGraphics)
1385// return;
1386//
1387// NSSize windowSize = [[self window] frame].size;
1388// NSRect viewFrameInWindowCoords = [self convertRect: [self bounds] toView: nil];
1389//
1390// float width = windowSize.width - viewFrameInWindowCoords.origin.x;
1391// float height = windowSize.height - viewFrameInWindowCoords.origin.y;
1392//
1393// float scaleX = width / mGraphics->Width();
1394// float scaleY = height / mGraphics->Height();
1395//
1396// if(mGraphics->GetUIResizerMode() == EUIResizerMode::Scale)
1397// mGraphics->Resize(width, height, mGraphics->GetDrawScale());
1398// else // EUIResizerMode::Size
1399// mGraphics->Resize(mGraphics->Width(), mGraphics->Height(), Clip(std::min(scaleX, scaleY), 0.1f, 10.f));
1400//}
1401//
1402//- (void) windowFullscreened: (NSNotification*) pNotification;
1403//{
1404// NSSize windowSize = [[self window] frame].size;
1405// NSRect viewFrameInWindowCoords = [self convertRect: [self bounds] toView: nil];
1406//
1407// float width = windowSize.width - viewFrameInWindowCoords.origin.x;
1408// float height = windowSize.height - viewFrameInWindowCoords.origin.y;
1409//
1410// float scaleX = width / mGraphics->Width();
1411// float scaleY = height / mGraphics->Height();
1412//
1413// if(mGraphics->GetUIResizerMode() == EUIResizerMode::Scale)
1414// mGraphics->Resize(width, height, mGraphics->GetDrawScale());
1415// else // EUIResizerMode::Size
1416// mGraphics->Resize(mGraphics->Width(), mGraphics->Height(), Clip(std::min(scaleX, scaleY), 0.1f, 10.f));
1417//}
1418
1419- (void) activateGLContext
1420{
1421#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1422 [[self openGLContext] makeCurrentContext];
1423#endif
1424
1425#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1426 if (mEGLDisplay != EGL_NO_DISPLAY && mEGLContext != EGL_NO_CONTEXT)
1427 eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
1428#endif
1429}
1430
1431- (void) deactivateGLContext
1432{
1433#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1434 [NSOpenGLContext clearCurrentContext];
1435#endif
1436
1437#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1438 if (mEGLDisplay != EGL_NO_DISPLAY)
1439 eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1440#endif
1441}
1442
1443- (void) swapBuffers
1444{
1445#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1446 [[self openGLContext] flushBuffer];
1447#endif
1448
1449#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1450 if (mEGLDisplay != EGL_NO_DISPLAY && mEGLSurface != EGL_NO_SURFACE)
1451 eglSwapBuffers(mEGLDisplay, mEGLSurface);
1452#endif
1453}
1454
1455#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1456- (EGLDisplay) getEGLDisplay
1457{
1458 return mEGLDisplay;
1459}
1460
1461- (EGLSurface) getEGLSurface
1462{
1463 return mEGLSurface;
1464}
1465
1466- (EGLContext) getEGLContext
1467{
1468 return mEGLContext;
1469}
1470#endif
1471
1472@end
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.
Definition: IGraphicsMac.h:24
IPlug's parameter class.
EParamType Type() const
Get the parameter's type.
A class to specify an item of a pop up menu.
A class for setting the contents of a pop up menu.
Used to manage a list of rectangular areas and optimize them for drawing to the screen.
Used to 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...
Definition: IPlugStructs.h:615
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,...