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#if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
388 return [[CAMetalLayer alloc] init];
389#else
390 return [[CALayer alloc] init];
391#endif
392}
393
394- (id) initWithIGraphics: (IGraphicsMac*) pGraphics
395{
396 TRACE
397
398 mGraphics = pGraphics;
399 NSRect r = NSMakeRect(0.f, 0.f, (float) pGraphics->WindowWidth(), (float) pGraphics->WindowHeight());
400 self = [super initWithFrame:r];
401
402 mMouseOutDuringDrag = false;
403
404 self.layer.frame = r;
405 self.wantsLayer = YES;
406 self.layer.opaque = YES;
407 self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
408 [self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
409
410 #if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
411 CAMetalLayer* mtlLayer = (CAMetalLayer*) self.layer;
412 [mtlLayer setPixelFormat:MTLPixelFormatBGRA8Unorm];
413 mtlLayer.device = MTLCreateSystemDefaultDevice();
414 mtlLayer.framebufferOnly = YES;
415 #elif defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
416 NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy;
417 #if defined IGRAPHICS_GL3
418 profile = (NSOpenGLPixelFormatAttribute)NSOpenGLProfileVersion3_2Core;
419 #endif
420 const NSOpenGLPixelFormatAttribute attrs[] = {
421 NSOpenGLPFAAccelerated,
422 NSOpenGLPFANoRecovery,
423 NSOpenGLPFADoubleBuffer,
424 NSOpenGLPFAAlphaSize, 8,
425 NSOpenGLPFAColorSize, 24,
426 NSOpenGLPFADepthSize, 0,
427 NSOpenGLPFAStencilSize, 8,
428 NSOpenGLPFAOpenGLProfile, profile,
429 (NSOpenGLPixelFormatAttribute) 0
430 };
431 NSOpenGLPixelFormat* pPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
432 NSOpenGLContext* pGLContext = [[NSOpenGLContext alloc] initWithFormat:pPixelFormat shareContext:nil];
433
434 #ifdef DEBUG
435 // CGLEnable([context CGLContextObj], kCGLCECrashOnRemovedFunctions); //SKIA_GL2 will crash
436 #endif
437
438 self.pixelFormat = pPixelFormat;
439 self.openGLContext = pGLContext;
440 self.wantsBestResolutionOpenGLSurface = YES;
441 #endif // IGRAPHICS_GL2 || defined IGRAPHICS_GL3
442
443 #if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
444 EGLDisplay display = eglGetPlatformDisplay(EGLenum(EGL_PLATFORM_ANGLE_ANGLE), 0, 0);
445
446 if (!display) {
447 DBGMSG("eglGetPlatformDisplay() returned error %i", eglGetError());
448 }
449
450 if (eglInitialize(display, nil, nil) == 0) {
451 DBGMSG("eglInitialize() returned error %i", eglGetError());
452 }
453
454 const EGLint configAttribs[9] = {
455 EGL_BLUE_SIZE, 8,
456 EGL_GREEN_SIZE, 8,
457 EGL_RED_SIZE, 8,
458 EGL_DEPTH_SIZE, 24,
459 EGL_NONE};
460 EGLint numConfigs = 0;
461 EGLConfig configs[1];
462
463 if (eglChooseConfig(display, configAttribs, configs, 1, &numConfigs) == 0) {
464 DBGMSG("eglChooseConfig() returned error %i", eglGetError());
465 }
466
467 if (!configs[0]) {
468 DBGMSG("Empty config returned in eglChooseConfig()");
469 }
470
471 #if defined IGRAPHICS_GLES2
472 const EGLint contextAttribs [5] = {
473 EGL_CONTEXT_MAJOR_VERSION, 2,
474 EGL_CONTEXT_MINOR_VERSION, 0,
475 EGL_NONE,
476 };
477 #elif defined IGRAPHICS_GLES3
478 const EGLint contextAttribs [5] = {
479 EGL_CONTEXT_MAJOR_VERSION, 3,
480 EGL_CONTEXT_MINOR_VERSION, 0,
481 EGL_NONE,
482 };
483 #endif
484
485 EGLContext context = eglCreateContext(display, configs[0], nullptr, contextAttribs);
486
487 if (!context) {
488 DBGMSG("eglCreateContext() returned error %d", eglGetError());
489 }
490
491 EGLSurface surface = eglCreateWindowSurface(display, configs[0], (__bridge EGLNativeWindowType) [self layer], nullptr);
492
493 if (!surface) {
494 DBGMSG("eglCreateWindowSurface() returned error %d", eglGetError());
495 }
496
497 mEGLSurface = surface;
498 mEGLDisplay = display;
499 mEGLContext = context;
500 #endif
501
502
503 #if !defined IGRAPHICS_GL2 && !defined IGRAPHICS_GL3
504 [self setTimer];
505 #endif
506
507 return self;
508}
509
510#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
511- (void) prepareOpenGL
512{
513 [super prepareOpenGL];
514
515 [self activateGLContext];
516
517 // Synchronize buffer swaps with vertical refresh rate
518 GLint swapInt = 1;
519 [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
520
521 [self setTimer];
522}
523#endif
524
525static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
526{
527 dispatch_source_t source = (dispatch_source_t) displayLinkContext;
528 dispatch_source_merge_data(source, 1);
529
530 return kCVReturnSuccess;
531}
532
533- (void) onTimer: (NSTimer*) pTimer
534{
535 [self render];
536}
537
538- (void) setTimer
539{
540#ifdef IGRAPHICS_CVDISPLAYLINK
541 mDisplaySource = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
542 dispatch_source_set_event_handler(mDisplaySource, ^(){
543 [self render];
544 });
545 dispatch_resume(mDisplaySource);
546
547 CVReturn cvReturn;
548
549 cvReturn = CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink);
550
551 assert(cvReturn == kCVReturnSuccess);
552
553 cvReturn = CVDisplayLinkSetOutputCallback(mDisplayLink, &displayLinkCallback, (void*) mDisplaySource);
554 assert(cvReturn == kCVReturnSuccess);
555
556 #if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
557 CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
558 CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
559 CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(mDisplayLink, cglContext, cglPixelFormat);
560 #endif
561
562 CGDirectDisplayID viewDisplayID =
563 (CGDirectDisplayID) [self.window.screen.deviceDescription[@"NSScreenNumber"] unsignedIntegerValue];;
564
565 cvReturn = CVDisplayLinkSetCurrentCGDisplay(mDisplayLink, viewDisplayID);
566
567 assert(cvReturn == kCVReturnSuccess);
568
569 CVDisplayLinkStart(mDisplayLink);
570#else
571 double sec = 1.0 / (double) mGraphics->FPS();
572 mTimer = [NSTimer timerWithTimeInterval:sec target:self selector:@selector(onTimer:) userInfo:nil repeats:YES];
573 [[NSRunLoop currentRunLoop] addTimer: mTimer forMode: (NSString*) kCFRunLoopCommonModes];
574#endif
575}
576
577- (void) killTimer
578{
579#ifdef IGRAPHICS_CVDISPLAYLINK
580 CVDisplayLinkStop(mDisplayLink);
581 dispatch_source_cancel(mDisplaySource);
582 CVDisplayLinkRelease(mDisplayLink);
583 mDisplayLink = nil;
584#else
585 [mTimer invalidate];
586 mTimer = nullptr;
587#endif
588}
589
590- (void) dealloc
591{
592 if([NSColorPanel sharedColorPanelExists])
593 [[NSColorPanel sharedColorPanel] close];
594
595 mColorPickerFunc = nullptr;
596 [mMoveCursor release];
597 [mTrackingArea release];
598 [[NSNotificationCenter defaultCenter] removeObserver:self];
599 [super dealloc];
600}
601
602
603- (BOOL) isOpaque
604{
605 return mGraphics ? YES : NO;
606}
607
608- (BOOL) isFlipped
609{
610 return YES;
611}
612
613- (void) viewDidChangeEffectiveAppearance
614{
615 if (@available(macOS 10.14, *)) {
616 BOOL isDarkMode = [[[self effectiveAppearance] name] isEqualToString: (NSAppearanceNameDarkAqua)];
617 mGraphics->OnAppearanceChanged(isDarkMode ? EUIAppearance::Dark : EUIAppearance::Light);
618 }
619}
620
621- (BOOL) acceptsFirstResponder
622{
623 return YES;
624}
625
626- (BOOL) acceptsFirstMouse: (NSEvent*) pEvent
627{
628 return YES;
629}
630
631- (void) viewDidMoveToWindow
632{
633 NSWindow* pWindow = [self window];
634
635 if (pWindow)
636 {
637 [pWindow makeFirstResponder: self];
638 [pWindow setAcceptsMouseMovedEvents: YES];
639
640 CGFloat newScale = [pWindow backingScaleFactor];
641
642 if (mGraphics)
643 mGraphics->SetScreenScale(newScale);
644
645 #if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
646 [[NSNotificationCenter defaultCenter] addObserver:self
647 selector:@selector(frameDidChange:)
648 name:NSViewFrameDidChangeNotification
649 object:self];
650 #endif
651
652 #if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
653 [[NSNotificationCenter defaultCenter] addObserver:self
654 selector:@selector(frameDidChange:)
655 name:NSViewGlobalFrameDidChangeNotification
656 object:self];
657 #endif
658
659// [[NSNotificationCenter defaultCenter] addObserver:self
660// selector:@selector(windowResized:) name:NSWindowDidEndLiveResizeNotification
661// object:pWindow];
662//
663// [[NSNotificationCenter defaultCenter] addObserver:self
664// selector:@selector(windowFullscreened:) name:NSWindowDidEnterFullScreenNotification
665// object:pWindow];
666//
667// [[NSNotificationCenter defaultCenter] addObserver:self
668// selector:@selector(windowFullscreened:) name:NSWindowDidExitFullScreenNotification
669// object:pWindow];
670 }
671}
672
673- (void) viewDidChangeBackingProperties:(NSNotification*) pNotification
674{
675 NSWindow* pWindow = [self window];
676
677 if (!pWindow)
678 return;
679
680 CGFloat newScale = [pWindow backingScaleFactor];
681
682 mGraphics->SetPlatformContext(nullptr);
683
684 if (newScale != mGraphics->GetScreenScale())
685 mGraphics->SetScreenScale(newScale);
686
687 self.layer.contentsScale = [[self window] backingScaleFactor];
688
689#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
690 self.layer.contentsScale = 1./newScale;
691#elif defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
692 [(CAMetalLayer*)[self layer] setDrawableSize:CGSizeMake(self.frame.size.width * newScale,
693 self.frame.size.height * newScale)];
694#endif
695}
696
697- (CGContextRef) getCGContextRef
698{
699 CGContextRef pCGC = [NSGraphicsContext currentContext].CGContext;
700 return [NSGraphicsContext graphicsContextWithCGContext: pCGC flipped: YES].CGContext;
701}
702
703// not called for layer backed views
704- (void) drawRect: (NSRect) bounds
705{
706 #if defined IGRAPHICS_CPU
707 if (mGraphics)
708 {
709 mGraphics->SetPlatformContext([self getCGContextRef]);
710
711 if (mGraphics->GetPlatformContext())
712 {
713 const NSRect *rects;
714 NSInteger numRects;
715 [self getRectsBeingDrawn:&rects count:&numRects];
716 IRECTList drawRects;
717
718 for (int i = 0; i < numRects; i++)
719 drawRects.Add(ToIRECT(mGraphics, &rects[i]));
720
721 mGraphics->Draw(drawRects);
722 }
723 }
724 #endif
725}
726
727- (void) render
728{
729 mDirtyRects.Clear();
730
731 if (mGraphics->IsDirty(mDirtyRects))
732 {
733 mGraphics->SetAllControlsClean();
734
735 #if defined IGRAPHICS_CPU
736 for (int i = 0; i < mDirtyRects.Size(); i++)
737 [self setNeedsDisplayInRect:ToNSRect(mGraphics, mDirtyRects.Get(i))];
738 #else
739 IGraphics::ScopedGLContext scopedGLCtx {mGraphics};
740 // so just draw on each frame, if something is dirty
741 mGraphics->Draw(mDirtyRects);
742 [self swapBuffers]; // No-op for Metal
743 #endif
744 }
745}
746
747- (void) getMouseXY: (NSEvent*) pEvent : (float&) x : (float&) y
748{
749 if (mGraphics)
750 {
751 NSPoint pt = [self convertPoint:[pEvent locationInWindow] fromView:nil];
752 x = pt.x / mGraphics->GetDrawScale();
753 y = pt.y / mGraphics->GetDrawScale();
754
755 mGraphics->DoCursorLock(x, y, mPrevX, mPrevY);
756 mGraphics->SetTabletInput(pEvent.subtype == NSTabletPointEventSubtype);
757 }
758}
759
760- (IMouseInfo) getMouseLeft: (NSEvent*) pEvent
761{
762 IMouseInfo info;
763 [self getMouseXY:pEvent : info.x : info.y];
764 int mods = (int) [pEvent modifierFlags];
765 info.ms = IMouseMod(true, (mods & NSCommandKeyMask), (mods & NSShiftKeyMask), (mods & NSControlKeyMask), (mods & NSAlternateKeyMask));
766
767 return info;
768}
769
770- (IMouseInfo) getMouseRight: (NSEvent*) pEvent
771{
772 IMouseInfo info;
773 [self getMouseXY:pEvent : info.x : info.y];
774 int mods = (int) [pEvent modifierFlags];
775 info.ms = IMouseMod(false, true, (mods & NSShiftKeyMask), (mods & NSControlKeyMask), (mods & NSAlternateKeyMask));
776
777 return info;
778}
779
780- (void) updateTrackingAreas
781{
782 [super updateTrackingAreas]; // This is needed to get mouseEntered and mouseExited
783
784 if (mTrackingArea != nil)
785 {
786 [self removeTrackingArea:mTrackingArea];
787 [mTrackingArea release];
788 }
789
790 int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingEnabledDuringMouseDrag);
791 mTrackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds] options:opts owner:self userInfo:nil];
792 [self addTrackingArea:mTrackingArea];
793}
794
795- (void) mouseEntered: (NSEvent*) pEvent
796{
797 mMouseOutDuringDrag = false;
798
799 if (mGraphics)
800 {
801 mGraphics->OnSetCursor();
802 }
803}
804
805- (void) mouseExited: (NSEvent*) pEvent
806{
807 if (mGraphics)
808 {
809 if (!mGraphics->ControlIsCaptured())
810 {
811 mGraphics->OnMouseOut();
812 }
813 else
814 {
815 mMouseOutDuringDrag = true;
816 }
817 }
818}
819
820- (void) mouseDown: (NSEvent*) pEvent
821{
822 IMouseInfo info = [self getMouseLeft:pEvent];
823 if (mGraphics)
824 {
825 if (([pEvent clickCount] - 1) % 2)
826 {
827 mGraphics->OnMouseDblClick(info.x, info.y, info.ms);
828 }
829 else
830 {
831 std::vector<IMouseInfo> list {info};
832 mGraphics->OnMouseDown(list);
833 }
834 }
835}
836
837- (void) mouseUp: (NSEvent*) pEvent
838{
839 IMouseInfo info = [self getMouseLeft:pEvent];
840 if (mGraphics)
841 {
842 std::vector<IMouseInfo> list {info};
843 mGraphics->OnMouseUp(list);
844
845 if (mMouseOutDuringDrag)
846 {
847 mGraphics->OnMouseOut();
848 mMouseOutDuringDrag = false;
849 }
850 }
851}
852
853- (void) mouseDragged: (NSEvent*) pEvent
854{
855 // Cache previous values before retrieving the new mouse position (which will update them)
856 float prevX = mPrevX;
857 float prevY = mPrevY;
858 IMouseInfo info = [self getMouseLeft:pEvent];
859 if (mGraphics && !mGraphics->IsInPlatformTextEntry())
860 {
861 info.dX = info.x - prevX;
862 info.dY = info.y - prevY;
863 std::vector<IMouseInfo> list {info};
864 mGraphics->OnMouseDrag(list);
865 }
866}
867
868- (void) rightMouseDown: (NSEvent*) pEvent
869{
870 IMouseInfo info = [self getMouseRight:pEvent];
871 if (mGraphics)
872 {
873 if (([pEvent clickCount] - 1) % 2)
874 {
875 mGraphics->OnMouseDblClick(info.x, info.y, info.ms);
876 }
877 else
878 {
879 std::vector<IMouseInfo> list {info};
880 mGraphics->OnMouseDown(list);
881 }
882 }
883}
884
885- (void) rightMouseUp: (NSEvent*) pEvent
886{
887 IMouseInfo info = [self getMouseRight:pEvent];
888 if (mGraphics)
889 {
890 std::vector<IMouseInfo> list {info};
891 mGraphics->OnMouseUp(list);
892 }
893}
894
895- (void) rightMouseDragged: (NSEvent*) pEvent
896{
897 // Cache previous values before retrieving the new mouse position (which will update them)
898 float prevX = mPrevX;
899 float prevY = mPrevY;
900 IMouseInfo info = [self getMouseRight:pEvent];
901
902 if (mGraphics && !mTextFieldView)
903 {
904 info.dX = info.x - prevX;
905 info.dY = info.y - prevY;
906 std::vector<IMouseInfo> list {info};
907 mGraphics->OnMouseDrag(list);
908 }
909}
910
911- (void) mouseMoved: (NSEvent*) pEvent
912{
913 IMouseInfo info = [self getMouseLeft:pEvent];
914 if (mGraphics)
915 mGraphics->OnMouseOver(info.x, info.y, info.ms);
916}
917
918- (void) keyDown: (NSEvent*) pEvent
919{
920 int flag = 0;
921 int code = MacKeyEventToVK(pEvent, flag);
922 NSString *s = [pEvent charactersIgnoringModifiers];
923
924 unichar c = 0;
925
926 if ([s length] == 1)
927 c = [s characterAtIndex:0];
928
929 if(!static_cast<bool>(flag & kFVIRTKEY))
930 {
931 code = kVK_NONE;
932 }
933
934 char utf8[5];
935 WDL_MakeUTFChar(utf8, c, 4);
936
937 IKeyPress keyPress {utf8, code, static_cast<bool>(flag & kFSHIFT),
938 static_cast<bool>(flag & kFCONTROL),
939 static_cast<bool>(flag & kFALT)};
940
941 bool handle = mGraphics->OnKeyDown(mPrevX, mPrevY, keyPress);
942
943 if (!handle)
944 {
945 [[self nextResponder] keyDown:pEvent];
946 }
947}
948
949- (void) keyUp: (NSEvent*) pEvent
950{
951 int flag = 0;
952 int code = MacKeyEventToVK(pEvent, flag);
953 NSString *s = [pEvent charactersIgnoringModifiers];
954
955 unichar c = 0;
956
957 if ([s length] == 1)
958 c = [s characterAtIndex:0];
959
960 if(!static_cast<bool>(flag & kFVIRTKEY))
961 {
962 code = kVK_NONE;
963 }
964
965 char utf8[5];
966 WDL_MakeUTFChar(utf8, c, 4);
967
968 IKeyPress keyPress {utf8, code, static_cast<bool>(flag & kFSHIFT),
969 static_cast<bool>(flag & kFCONTROL),
970 static_cast<bool>(flag & kFALT)};
971
972 bool handle = mGraphics->OnKeyUp(mPrevX, mPrevY, keyPress);
973
974 if (!handle)
975 {
976 [[self nextResponder] keyUp:pEvent];
977 }
978}
979
980- (void) scrollWheel: (NSEvent*) pEvent
981{
982 if (mTextFieldView) [self endUserInput ];
983 IMouseInfo info = [self getMouseLeft:pEvent];
984 float d = [pEvent deltaY];
985 if (mGraphics)
986 mGraphics->OnMouseWheel(info.x, info.y, info.ms, d);
987}
988
989static void MakeCursorFromName(NSCursor*& cursor, const char *name)
990{
991 // get paths and intialise images etc.
992 const char* basePath = "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors/";
993
994 NSString* imagePath = [NSString stringWithFormat:@"%s%s/cursor.pdf", basePath, name];
995 NSString* infoPath = [NSString stringWithFormat:@"file:%s%s/info.plist", basePath, name];
996 NSImage* fileImage = [[NSImage alloc] initByReferencingFile: imagePath];
997 NSImage *cursorImage = [[NSImage alloc] initWithSize:[fileImage size]];
998 NSDictionary* info = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:infoPath]];
999
1000 // get info from dictionary
1001 double hotX = [info[@"hotx-scaled"] doubleValue];
1002 double hotY = [info[@"hoty-scaled"] doubleValue];
1003 double blur = [info[@"blur"] doubleValue];
1004 double offsetX = [info[@"shadowoffsetx"] doubleValue];
1005 double offsetY = [info[@"shadowoffsety"] doubleValue];
1006 double red = [info[@"shadowcolor"][0] doubleValue];
1007 double green = [info[@"shadowcolor"][1] doubleValue];
1008 double blue = [info[@"shadowcolor"][2] doubleValue];
1009 double alpha = [info[@"shadowcolor"][3] doubleValue];
1010 CGColorRef shadowColor = CGColorCreateGenericRGB(red, green, blue, alpha);
1011
1012 for (int scale = 1; scale <= 4; scale++)
1013 {
1014 // scale
1015 NSAffineTransform* xform = [NSAffineTransform transform];
1016 [xform scaleBy:scale];
1017 id hints = @{ NSImageHintCTM: xform };
1018 CGImageRef rasterCGImage = [fileImage CGImageForProposedRect:NULL context:nil hints:hints];
1019
1020 // apply shadow
1021 size_t width = CGImageGetWidth(rasterCGImage);
1022 size_t height = CGImageGetHeight(rasterCGImage);
1023 CGSize offset = CGSize { static_cast<CGFloat>(offsetX * scale), static_cast<CGFloat>(offsetY * scale) };
1024 CGContextRef shadowContext = CGBitmapContextCreate(NULL, width, height, CGImageGetBitsPerComponent(rasterCGImage), 0, CGImageGetColorSpace(rasterCGImage), CGImageGetBitmapInfo(rasterCGImage));
1025 CGContextSetShadowWithColor(shadowContext, offset, blur * scale, shadowColor);
1026 CGContextDrawImage(shadowContext, CGRectMake(0, 0, width, height), rasterCGImage);
1027 CGImageRef shadowCGImage = CGBitmapContextCreateImage(shadowContext);
1028
1029 // add to cursor inmge
1030 NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithCGImage:shadowCGImage];
1031 [rep setSize:[fileImage size]];
1032 [cursorImage addRepresentation:rep];
1033
1034 // release
1035 [rep release];
1036 CGContextRelease(shadowContext);
1037 CGImageRelease(shadowCGImage);
1038 }
1039
1040 // create cursor
1041 cursor = [[NSCursor alloc] initWithImage:cursorImage hotSpot:NSMakePoint(hotX, hotY)];
1042
1043 // release
1044 [cursorImage release];
1045 [fileImage release];
1046 CGColorRelease(shadowColor);
1047}
1048
1049- (void) setMouseCursor: (ECursor) cursorType
1050{
1051 NSCursor* pCursor = nullptr;
1052
1053 bool helpCurrent = false;
1054 bool helpRequested = false;
1055
1056 switch (cursorType)
1057 {
1058 case ECursor::ARROW: pCursor = [NSCursor arrowCursor]; break;
1059 case ECursor::IBEAM: pCursor = [NSCursor IBeamCursor]; break;
1060 case ECursor::WAIT:
1061 if ([NSCursor respondsToSelector:@selector(busyButClickableCursor)])
1062 pCursor = [NSCursor performSelector:@selector(busyButClickableCursor)];
1063 break;
1064 case ECursor::CROSS: pCursor = [NSCursor crosshairCursor]; break;
1065 case ECursor::UPARROW:
1066 if ([NSCursor respondsToSelector:@selector(_windowResizeNorthCursor)])
1067 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthCursor)];
1068 else
1069 pCursor = [NSCursor resizeUpCursor];
1070 break;
1071 case ECursor::SIZENWSE:
1072 if ([NSCursor respondsToSelector:@selector(_windowResizeNorthWestSouthEastCursor)])
1073 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthWestSouthEastCursor)];
1074 break;
1075 case ECursor::SIZENESW:
1076 if ([NSCursor respondsToSelector:@selector(_windowResizeNorthEastSouthWestCursor)])
1077 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthEastSouthWestCursor)];
1078 break;
1079 case ECursor::SIZEWE:
1080 if ([NSCursor respondsToSelector:@selector(_windowResizeEastWestCursor)])
1081 pCursor = [NSCursor performSelector:@selector(_windowResizeEastWestCursor)];
1082 else
1083 pCursor = [NSCursor resizeLeftRightCursor];
1084 break;
1085 case ECursor::SIZENS:
1086 if ([NSCursor respondsToSelector:@selector(_windowResizeNorthSouthCursor)])
1087 pCursor = [NSCursor performSelector:@selector(_windowResizeNorthSouthCursor)];
1088 else
1089 pCursor = [NSCursor resizeUpDownCursor];
1090 break;
1091 case ECursor::SIZEALL:
1092 {
1093 if (!mMoveCursor)
1094 MakeCursorFromName(mMoveCursor, "move");
1095 pCursor = mMoveCursor;
1096 break;
1097 }
1098 case ECursor::INO: pCursor = [NSCursor operationNotAllowedCursor]; break;
1099 case ECursor::HAND: pCursor = [NSCursor pointingHandCursor]; break;
1100 case ECursor::APPSTARTING:
1101 if ([NSCursor respondsToSelector:@selector(busyButClickableCursor)])
1102 pCursor = [NSCursor performSelector:@selector(busyButClickableCursor)];
1103 break;
1104 case ECursor::HELP:
1105 if ([NSCursor respondsToSelector:@selector(_helpCursor)])
1106 pCursor = [NSCursor performSelector:@selector(_helpCursor)];
1107 helpRequested = true;
1108 break;
1109 default: pCursor = [NSCursor arrowCursor]; break;
1110 }
1111
1112 if ([NSCursor respondsToSelector:@selector(helpCursorShown)])
1113 helpCurrent = [NSCursor performSelector:@selector(helpCursorShown)];
1114
1115 if (helpCurrent && !helpRequested)
1116 {
1117 // N.B. - suppress warnings for this call only
1118#pragma clang diagnostic push
1119#pragma clang diagnostic ignored "-Wobjc-method-access"
1120 [NSCursor _setHelpCursor : false];
1121#pragma clang diagnostic pop
1122 }
1123
1124 if (!pCursor)
1125 pCursor = [NSCursor arrowCursor];
1126
1127 [pCursor set];
1128}
1129
1130- (void) removeFromSuperview
1131{
1132 if (mTextFieldView)
1133 [self endUserInput ];
1134
1135 mGraphics->SetPlatformContext(nullptr);
1136
1137 //For some APIs (AUv2) this is where we know about the window being closed, close via delegate
1138 mGraphics->GetDelegate()->CloseWindow();
1139 [super removeFromSuperview];
1140}
1141
1142- (void) controlTextDidEndEditing: (NSNotification*) aNotification
1143{
1144 char* txt = (char*)[[mTextFieldView stringValue] UTF8String];
1145
1146 mGraphics->SetControlValueAfterTextEdit(txt);
1147 mGraphics->SetAllControlsDirty();
1148
1149 [self endUserInput ];
1150}
1151
1152- (IPopupMenu*) createPopupMenu: (IPopupMenu&) menu : (NSRect) bounds;
1153{
1154 IGRAPHICS_MENU_RCVR* pDummyView = [[[IGRAPHICS_MENU_RCVR alloc] initWithFrame:bounds] autorelease];
1155 NSMenu* pNSMenu = [[[IGRAPHICS_MENU alloc] initWithIPopupMenuAndReceiver:&menu : pDummyView] autorelease];
1156 NSPoint wp = {bounds.origin.x, bounds.origin.y + bounds.size.height + 4};
1157
1158 NSMenuItem* pSelectedItem = nil;
1159
1160 auto selectedItemIdx = menu.GetChosenItemIdx();
1161
1162 if (selectedItemIdx > -1)
1163 {
1164 pSelectedItem = [pNSMenu itemAtIndex:selectedItemIdx];
1165 }
1166
1167 if (pSelectedItem != nil)
1168 {
1169 wp = {bounds.origin.x, bounds.origin.y};
1170 }
1171
1172 [pNSMenu popUpMenuPositioningItem:pSelectedItem atLocation:wp inView:self];
1173
1174 NSMenuItem* pChosenItem = [pDummyView menuItem];
1175 NSMenu* pChosenMenu = [pChosenItem menu];
1176 IPopupMenu* pIPopupMenu = [(IGRAPHICS_MENU*) pChosenMenu iPopupMenu];
1177
1178 long chosenItemIdx = [pChosenMenu indexOfItem: pChosenItem];
1179
1180 if (chosenItemIdx > -1 && pIPopupMenu)
1181 {
1182 pIPopupMenu->SetChosenItemIdx((int) chosenItemIdx);
1183 return pIPopupMenu;
1184 }
1185 else
1186 return nullptr;
1187}
1188
1189- (void) createTextEntry: (int) paramIdx : (const IText&) text : (const char*) str : (int) length : (NSRect) areaRect;
1190{
1191 if (mTextFieldView)
1192 return;
1193
1194 mTextFieldView = [[IGRAPHICS_TEXTFIELD alloc] initWithFrame: areaRect];
1195
1196 if (text.mVAlign == EVAlign::Middle)
1197 {
1198 IGRAPHICS_TEXTFIELDCELL* pCell = [[IGRAPHICS_TEXTFIELDCELL alloc] initTextCell:@"textfield"];
1199 [mTextFieldView setCell: pCell];
1200 [mTextFieldView setEditable: TRUE];
1201 [mTextFieldView setDrawsBackground: TRUE];
1202 }
1203
1204 CoreTextFontDescriptor* CTFontDescriptor = CoreTextHelpers::GetCTFontDescriptor(text, sFontDescriptorCache);
1205 double ratio = CTFontDescriptor->GetEMRatio() * mGraphics->GetDrawScale();
1206 NSFontDescriptor* fontDescriptor = (NSFontDescriptor*) CTFontDescriptor->GetDescriptor();
1207 NSFont* font = [NSFont fontWithDescriptor: fontDescriptor size: text.mSize * ratio];
1208 [mTextFieldView setFont: font];
1209
1210 switch (text.mAlign)
1211 {
1212 case EAlign::Near:
1213 [mTextFieldView setAlignment: NSLeftTextAlignment];
1214 break;
1215 case EAlign::Center:
1216 [mTextFieldView setAlignment: NSCenterTextAlignment];
1217 break;
1218 case EAlign::Far:
1219 [mTextFieldView setAlignment: NSRightTextAlignment];
1220 break;
1221 default:
1222 break;
1223 }
1224
1225 const IParam* pParam = paramIdx > kNoParameter ? mGraphics->GetDelegate()->GetParam(paramIdx) : nullptr;
1226
1227 // set up formatter
1228 if (pParam)
1229 {
1230 NSMutableCharacterSet *characterSet = [[NSMutableCharacterSet alloc] init];
1231
1232 switch ( pParam->Type() )
1233 {
1234 case IParam::kTypeEnum:
1235 case IParam::kTypeInt:
1236 case IParam::kTypeBool:
1237 [characterSet addCharactersInString:@"0123456789-+"];
1238 break;
1239 case IParam::kTypeDouble:
1240 [characterSet addCharactersInString:@"0123456789.-+"];
1241 break;
1242 default:
1243 break;
1244 }
1245
1246 [mTextFieldView setFormatter:[[[IGRAPHICS_FORMATTER alloc] init] autorelease]];
1247 [[mTextFieldView formatter] setAcceptableCharacterSet:characterSet];
1248 [[mTextFieldView formatter] setMaximumLength:length];
1249 [characterSet release];
1250 }
1251
1252 [[mTextFieldView cell] setLineBreakMode: NSLineBreakByTruncatingTail];
1253 [mTextFieldView setAllowsEditingTextAttributes:NO];
1254 [mTextFieldView setTextColor:ToNSColor(text.mTextEntryFGColor)];
1255 [mTextFieldView setBackgroundColor:ToNSColor(text.mTextEntryBGColor)];
1256
1257 [mTextFieldView setStringValue: [NSString stringWithUTF8String:str]];
1258
1259#ifndef COCOA_TEXTENTRY_BORDERED
1260 [mTextFieldView setBordered: NO];
1261 [mTextFieldView setFocusRingType:NSFocusRingTypeNone];
1262#endif
1263
1264 [mTextFieldView setDelegate: self];
1265
1266 [self addSubview: mTextFieldView];
1267 NSWindow* pWindow = [self window];
1268 [pWindow makeKeyAndOrderFront:nil];
1269 [pWindow makeFirstResponder: mTextFieldView];
1270}
1271
1272- (void) endUserInput
1273{
1274 [mTextFieldView setDelegate: nil];
1275 [mTextFieldView removeFromSuperview];
1276
1277 NSWindow* pWindow = [self window];
1278 [pWindow makeFirstResponder: self];
1279
1280 mTextFieldView = nullptr;
1281 mGraphics->ClearInTextEntryControl();
1282}
1283
1284- (BOOL) promptForColor: (IColor&) color : (IColorPickerHandlerFunc) func;
1285{
1286 NSColorPanel* colorPanel = [NSColorPanel sharedColorPanel];
1287 mColorPickerFunc = func;
1288
1289 [colorPanel setTarget:self];
1290 [colorPanel setShowsAlpha: TRUE];
1291 [colorPanel setAction:@selector(onColorPicked:)];
1292 [colorPanel setColor:ToNSColor(color)];
1293 [colorPanel orderFront:nil];
1294
1295 return colorPanel != nil;
1296}
1297
1298- (void) onColorPicked: (NSColorPanel*) pColorPanel
1299{
1300 mColorPickerFunc(FromNSColor([pColorPanel color]));
1301}
1302
1303- (NSString*) view: (NSView*) pView stringForToolTip: (NSToolTipTag) tag point: (NSPoint) point userData: (void*) pData
1304{
1305 int c = mGraphics ? GetMouseOver(mGraphics) : -1;
1306 if (c < 0) return @"";
1307
1308 const char* tooltip = mGraphics->GetControl(c)->GetTooltip();
1309 return CStringHasContents(tooltip) ? [NSString stringWithUTF8String:tooltip] : @"";
1310}
1311
1312- (void) registerToolTip: (IRECT&) bounds
1313{
1314 [self addToolTipRect: ToNSRect(mGraphics, bounds) owner: self userData: nil];
1315}
1316
1317- (NSDragOperation) draggingEntered: (id<NSDraggingInfo>) sender
1318{
1319 NSPasteboard *pPasteBoard = [sender draggingPasteboard];
1320
1321 if ([[pPasteBoard types] containsObject:NSFilenamesPboardType])
1322 return NSDragOperationGeneric;
1323 else
1324 return NSDragOperationNone;
1325}
1326
1327- (BOOL) performDragOperation: (id<NSDraggingInfo>) sender
1328{
1329 NSPasteboard* pPasteBoard = [sender draggingPasteboard];
1330
1331 if ([[pPasteBoard types] containsObject:NSFilenamesPboardType])
1332 {
1333 NSArray* pFiles = [pPasteBoard propertyListForType:NSFilenamesPboardType];
1334 NSPoint point = [sender draggingLocation];
1335 NSPoint relativePoint = [self convertPoint: point fromView:nil];
1336
1337 const float scale = mGraphics->GetDrawScale();
1338 const float x = relativePoint.x / scale;
1339 const float y = relativePoint.y / scale;
1340 if ([pFiles count] == 1)
1341 {
1342 NSString* pFirstFile = [pFiles firstObject];
1343 mGraphics->OnDrop([pFirstFile UTF8String], x, y);
1344 }
1345 else if ([pFiles count] > 1)
1346 {
1347 std::vector<const char*> paths([pFiles count]);
1348 for (auto i = 0; i < [pFiles count]; i++)
1349 {
1350 NSString* pFile = [pFiles objectAtIndex: i];
1351 paths[i] = [pFile UTF8String];
1352 }
1353 mGraphics->OnDropMultiple(paths, x, y);
1354 }
1355 }
1356 return YES;
1357}
1358
1359- (NSDragOperation)draggingSession:(NSDraggingSession*) session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
1360{
1361 return NSDragOperationCopy;
1362}
1363
1364#if defined IGRAPHICS_METAL || defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1365- (void) frameDidChange:(NSNotification*) pNotification
1366{
1367 CGFloat scale = [[self window] backingScaleFactor];
1368
1369 [(CAMetalLayer*)[self layer] setDrawableSize:CGSizeMake(self.frame.size.width * scale,
1370 self.frame.size.height * scale)];
1371}
1372#endif
1373#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1374- (void) frameDidChange:(NSNotification*) pNotification
1375{
1376 [self activateGLContext];
1377}
1378#endif
1379
1380//- (void) windowResized: (NSNotification*) notification;
1381//{
1382// if(!mGraphics)
1383// return;
1384//
1385// NSSize windowSize = [[self window] frame].size;
1386// NSRect viewFrameInWindowCoords = [self convertRect: [self bounds] toView: nil];
1387//
1388// float width = windowSize.width - viewFrameInWindowCoords.origin.x;
1389// float height = windowSize.height - viewFrameInWindowCoords.origin.y;
1390//
1391// float scaleX = width / mGraphics->Width();
1392// float scaleY = height / mGraphics->Height();
1393//
1394// if(mGraphics->GetUIResizerMode() == EUIResizerMode::Scale)
1395// mGraphics->Resize(width, height, mGraphics->GetDrawScale());
1396// else // EUIResizerMode::Size
1397// mGraphics->Resize(mGraphics->Width(), mGraphics->Height(), Clip(std::min(scaleX, scaleY), 0.1f, 10.f));
1398//}
1399//
1400//- (void) windowFullscreened: (NSNotification*) pNotification;
1401//{
1402// NSSize windowSize = [[self window] frame].size;
1403// NSRect viewFrameInWindowCoords = [self convertRect: [self bounds] toView: nil];
1404//
1405// float width = windowSize.width - viewFrameInWindowCoords.origin.x;
1406// float height = windowSize.height - viewFrameInWindowCoords.origin.y;
1407//
1408// float scaleX = width / mGraphics->Width();
1409// float scaleY = height / mGraphics->Height();
1410//
1411// if(mGraphics->GetUIResizerMode() == EUIResizerMode::Scale)
1412// mGraphics->Resize(width, height, mGraphics->GetDrawScale());
1413// else // EUIResizerMode::Size
1414// mGraphics->Resize(mGraphics->Width(), mGraphics->Height(), Clip(std::min(scaleX, scaleY), 0.1f, 10.f));
1415//}
1416
1417- (void) activateGLContext
1418{
1419#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1420 [[self openGLContext] makeCurrentContext];
1421#endif
1422
1423#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1424 if (mEGLDisplay != EGL_NO_DISPLAY && mEGLContext != EGL_NO_CONTEXT)
1425 eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
1426#endif
1427}
1428
1429- (void) deactivateGLContext
1430{
1431#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1432 [NSOpenGLContext clearCurrentContext];
1433#endif
1434
1435#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1436 if (mEGLDisplay != EGL_NO_DISPLAY)
1437 eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1438#endif
1439}
1440
1441- (void) swapBuffers
1442{
1443#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
1444 [[self openGLContext] flushBuffer];
1445#endif
1446
1447#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1448 if (mEGLDisplay != EGL_NO_DISPLAY && mEGLSurface != EGL_NO_SURFACE)
1449 eglSwapBuffers(mEGLDisplay, mEGLSurface);
1450#endif
1451}
1452
1453#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
1454- (EGLDisplay) getEGLDisplay
1455{
1456 return mEGLDisplay;
1457}
1458
1459- (EGLSurface) getEGLSurface
1460{
1461 return mEGLSurface;
1462}
1463
1464- (EGLContext) getEGLContext
1465{
1466 return mEGLContext;
1467}
1468#endif
1469
1470@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,...