11#include "IGraphicsMac.h"
12#import "IGraphicsMac_view.h"
14#if defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
15#import <libEGL/libEGL.h>
21#pragma clang diagnostic ignored "-Wdeprecated-declarations"
24using namespace igraphics;
26static int GetSystemVersion()
31 if (NSAppKitVersionNumber >= 1266.0)
33 if (NSAppKitVersionNumber >= 1404.0)
41 Gestalt(gestaltSystemVersion,&a);
48StaticStorage<CoreTextFontDescriptor> sFontDescriptorCache;
52IGraphicsMac::IGraphicsMac(
IGEditorDelegate& dlg,
int w,
int h,
int fps,
float scale)
53: IGRAPHICS_DRAW_CLASS(dlg, w, h, fps, scale)
56 StaticStorage<CoreTextFontDescriptor>::Accessor storage(sFontDescriptorCache);
60IGraphicsMac::~IGraphicsMac()
62 StaticStorage<CoreTextFontDescriptor>::Accessor storage(sFontDescriptorCache);
68PlatformFontPtr IGraphicsMac::LoadPlatformFont(
const char* fontID,
const char* fileNameOrResID)
70 return CoreTextHelpers::LoadPlatformFont(fontID, fileNameOrResID, GetBundleID(), GetSharedResourcesSubPath());
73PlatformFontPtr IGraphicsMac::LoadPlatformFont(
const char* fontID,
const char* fontName, ETextStyle style)
75 return CoreTextHelpers::LoadPlatformFont(fontID, fontName, style);
78PlatformFontPtr IGraphicsMac::LoadPlatformFont(
const char* fontID,
void* pData,
int dataSize)
80 return CoreTextHelpers::LoadPlatformFont(fontID, pData, dataSize);
83void IGraphicsMac::CachePlatformFont(
const char* fontID,
const PlatformFontPtr& font)
85 CoreTextHelpers::CachePlatformFont(fontID, font, sFontDescriptorCache);
88float IGraphicsMac::MeasureText(
const IText& text,
const char* str,
IRECT& bounds)
const
90 return IGRAPHICS_DRAW_CLASS::MeasureText(text, str, bounds);
93void* IGraphicsMac::OpenWindow(
void* pParent)
97 IGRAPHICS_VIEW* pView = [[IGRAPHICS_VIEW alloc] initWithIGraphics:
this];
98 mView = (
void*) pView;
101 OnViewInitialized([pView layer]);
102 SetScreenScale([[NSScreen mainScreen] backingScaleFactor]);
103 GetDelegate()->LayoutUI(
this);
105 GetDelegate()->OnUIOpen();
109 [(NSView*) pParent addSubview: pView];
115void IGraphicsMac::AttachPlatformView(
const IRECT& r,
void* pView)
117 NSView* pNewSubView = (NSView*) pView;
118 [pNewSubView setFrame:ToNSRect(
this, r)];
120 [(IGRAPHICS_VIEW*) mView addSubview:(NSView*) pNewSubView];
123void IGraphicsMac::RemovePlatformView(
void* pView)
125 [(NSView*) pView removeFromSuperview];
128void IGraphicsMac::HidePlatformView(
void* pView,
bool hide)
130 [(NSView*) pView setHidden:hide];
133void IGraphicsMac::CloseWindow()
137 IGRAPHICS_VIEW* pView = (IGRAPHICS_VIEW*) mView;
139#if defined IGRAPHICS_GL2 || defined IGRAPHICS_GL3
140 [[pView pixelFormat] release];
141 [[pView openGLContext] release];
142#elif defined IGRAPHICS_GLES2 || defined IGRAPHICS_GLES3
143 [pView deactivateGLContext];
144 eglDestroySurface([pView getEGLDisplay], [pView getEGLSurface]);
145 eglDestroyContext([pView getEGLDisplay], [pView getEGLContext]);
146 eglTerminate([pView getEGLDisplay]);
149 [pView removeAllToolTips];
151 [pView removeFromSuperview];
159bool IGraphicsMac::WindowIsOpen()
164void IGraphicsMac::PlatformResize(
bool parentHasResized)
168 NSSize size = {
static_cast<CGFloat
>(WindowWidth()),
static_cast<CGFloat
>(WindowHeight()) };
170 [NSAnimationContext beginGrouping];
171 [[NSAnimationContext currentContext] setDuration:0.0];
172 [(IGRAPHICS_VIEW*) mView setFrameSize: size ];
174 [NSAnimationContext endGrouping];
180void IGraphicsMac::PointToScreen(
float& x,
float& y)
const
186 NSWindow* pWindow = [(IGRAPHICS_VIEW*) mView window];
187 NSPoint wndpt = [(IGRAPHICS_VIEW*) mView convertPoint:NSMakePoint(x, y) toView:nil];
188 NSPoint pt = [pWindow convertRectToScreen: NSMakeRect(wndpt.x, wndpt.y, 0.0, 0.0)].origin;
195void IGraphicsMac::ScreenToPoint(
float& x,
float& y)
const
199 NSWindow* pWindow = [(IGRAPHICS_VIEW*) mView window];
200 NSPoint wndpt = [pWindow convertRectFromScreen: NSMakeRect(x, y, 0.0, 0.0)].origin;
201 NSPoint pt = [(IGRAPHICS_VIEW*) mView convertPoint:NSMakePoint(wndpt.x, wndpt.y) fromView:nil];
203 x = pt.x / GetDrawScale();
204 y = pt.y / GetDrawScale();
208void IGraphicsMac::HideMouseCursor(
bool hide,
bool lock)
212#elif defined AUv3_API
213 if (!IsOOPAuv3AppExtension())
216 if (mCursorHidden == hide)
219 mCursorHidden = hide;
223 StoreCursorPosition();
224 CGDisplayHideCursor(kCGDirectMainDisplay);
229 DoCursorLock(mCursorX, mCursorY, mCursorX, mCursorY);
230 CGDisplayShowCursor(kCGDirectMainDisplay);
236void IGraphicsMac::MoveMouseCursor(
float x,
float y)
242 RepositionCursor(CGPoint{x, y});
243 StoreCursorPosition();
246void IGraphicsMac::DoCursorLock(
float x,
float y,
float& prevX,
float& prevY)
248 if (mCursorHidden && mCursorLock && !mTabletInput)
250 RepositionCursor(mCursorLockPosition);
256 mCursorX = prevX = x;
257 mCursorY = prevY = y;
261void IGraphicsMac::RepositionCursor(CGPoint point)
263 point = CGPoint{point.x, CGDisplayPixelsHigh(CGMainDisplayID()) - point.y};
264 CGAssociateMouseAndMouseCursorPosition(
false);
265 CGDisplayMoveCursorToPoint(CGMainDisplayID(), point);
266 CGAssociateMouseAndMouseCursorPosition(
true);
269void IGraphicsMac::StoreCursorPosition()
272 NSPoint mouse = [NSEvent mouseLocation];
273 mCursorX = mouse.x = std::round(mouse.x);
274 mCursorY = mouse.y = std::round(mouse.y);
275 mCursorLockPosition = CGPoint{mouse.x, mouse.y};
278 ScreenToPoint(mCursorX, mCursorY);
281void IGraphicsMac::GetMouseLocation(
float& x,
float&y)
const
284 NSPoint mouse = [NSEvent mouseLocation];
292EMsgBoxResult IGraphicsMac::ShowMessageBox(
const char* str,
const char* title, EMsgBoxType type, IMsgBoxCompletionHandlerFunc completionHandler)
294 ReleaseMouseCapture();
296 NSString* messageContent = @(str ? str :
"");
297 NSString* alertTitle = @(title ? title :
"");
299 NSAlert* alert = [[NSAlert alloc] init];
300 [alert setMessageText:alertTitle];
301 [alert setInformativeText:messageContent];
303 EMsgBoxResult result = kCANCEL;
308 [alert addButtonWithTitle:
@"OK"];
312 [alert addButtonWithTitle:
@"OK"];
313 [alert addButtonWithTitle:
@"Cancel"];
317 [alert addButtonWithTitle:
@"Yes"];
318 [alert addButtonWithTitle:
@"No"];
321 case kMB_RETRYCANCEL:
322 [alert addButtonWithTitle:
@"Retry"];
323 [alert addButtonWithTitle:
@"Cancel"];
326 case kMB_YESNOCANCEL:
327 [alert addButtonWithTitle:
@"Yes"];
328 [alert addButtonWithTitle:
@"No"];
329 [alert addButtonWithTitle:
@"Cancel"];
334 NSModalResponse response = [alert runModal];
342 result = (response == NSAlertFirstButtonReturn) ? kOK : kCANCEL;
345 result = (response == NSAlertFirstButtonReturn) ? kYES : kNO;
347 case kMB_RETRYCANCEL:
348 result = (response == NSAlertFirstButtonReturn) ? kRETRY : kCANCEL;
350 case kMB_YESNOCANCEL:
351 if (response == NSAlertFirstButtonReturn) result = kYES;
352 else if (response == NSAlertSecondButtonReturn) result = kNO;
353 else result = kCANCEL;
357 if (completionHandler)
359 completionHandler(result);
367void IGraphicsMac::ForceEndUserEdit()
371 [(IGRAPHICS_VIEW*) mView endUserInput];
375void IGraphicsMac::UpdateTooltips()
377 if (!(mView && TooltipsEnabled()))
382 [(IGRAPHICS_VIEW*) mView removeAllToolTips];
384 if (GetPopupMenuControl() && GetPopupMenuControl()->GetState() > IPopupMenuControl::kCollapsed)
389 auto func = [
this](
IControl* pControl)
391 if (pControl->GetTooltip() && !pControl->IsHidden())
393 IRECT pR = pControl->GetTargetRECT();
396 [(IGRAPHICS_VIEW*) mView registerToolTip: pR];
401 ForStandardControlsFunc(func);
406const char* IGraphicsMac::GetPlatformAPIStr()
411bool IGraphicsMac::RevealPathInExplorerOrFinder(WDL_String& path,
bool select)
413 BOOL success = FALSE;
419 NSString* pPath = [NSString stringWithUTF8String:path.Get()];
421 if([[NSFileManager defaultManager] fileExistsAtPath : pPath] == YES)
425 NSString* pParentDirectoryPath = [pPath stringByDeletingLastPathComponent];
427 if (pParentDirectoryPath)
429 success = [[NSWorkspace sharedWorkspace] openFile:pParentDirectoryPath];
432 success = [[NSWorkspace sharedWorkspace] selectFile: pPath inFileViewerRootedAtPath:pParentDirectoryPath];
436 success = [[NSWorkspace sharedWorkspace] openFile:pPath];
443 return (
bool) success;
446void IGraphicsMac::PromptForFile(WDL_String& fileName, WDL_String& path, EFileAction action,
const char* ext, IFileDialogCompletionHandlerFunc completionHandler)
454 NSString* pDefaultFileName = nil;
455 NSString* pDefaultPath = nil;
456 NSArray* pFileTypes = nil;
458 if (fileName.GetLength())
459 pDefaultFileName = [NSString stringWithUTF8String:fileName.Get()];
461 pDefaultFileName =
@"";
463 if (path.GetLength())
464 pDefaultPath = [NSString stringWithUTF8String:path.Get()];
470 if (CStringHasContents(ext))
471 pFileTypes = [[NSString stringWithUTF8String:ext] componentsSeparatedByString:
@" "];
473 auto doHandleResponse = [](NSPanel* pPanel, NSModalResponse response, WDL_String& fileName, WDL_String& path, IFileDialogCompletionHandlerFunc completionHandler){
474 if (response == NSOKButton)
476 NSString* pFullPath = [(NSSavePanel*) pPanel filename] ;
477 fileName.Set([pFullPath UTF8String]);
479 NSString* pTruncatedPath = [pFullPath stringByDeletingLastPathComponent];
483 path.Set([pTruncatedPath UTF8String]);
488 if (completionHandler)
489 completionHandler(fileName, path);
492 NSPanel* pPanel =
nullptr;
494 if (action == EFileAction::Save)
496 pPanel = [NSSavePanel savePanel];
498 [(NSSavePanel*) pPanel setAllowedFileTypes: pFileTypes];
499 [(NSSavePanel*) pPanel setDirectoryURL: [NSURL fileURLWithPath: pDefaultPath]];
500 [(NSSavePanel*) pPanel setNameFieldStringValue: pDefaultFileName];
501 [(NSSavePanel*) pPanel setAllowsOtherFileTypes: NO];
505 pPanel = [NSOpenPanel openPanel];
507 [(NSOpenPanel*) pPanel setAllowedFileTypes: pFileTypes];
508 [(NSOpenPanel*) pPanel setDirectoryURL: [NSURL fileURLWithPath: pDefaultPath]];
509 [(NSOpenPanel*) pPanel setCanChooseFiles:YES];
510 [(NSOpenPanel*) pPanel setCanChooseDirectories:NO];
511 [(NSOpenPanel*) pPanel setResolvesAliases:YES];
513 [pPanel setFloatingPanel: YES];
515 if (completionHandler)
517 NSWindow* pWindow = [(IGRAPHICS_VIEW*) mView window];
519 [(NSSavePanel*) pPanel beginSheetModalForWindow:pWindow completionHandler:^(NSModalResponse response) {
520 WDL_String fileNameAsync, pathAsync;
521 doHandleResponse(pPanel, response, fileNameAsync, pathAsync, completionHandler);
526 NSModalResponse response = [(NSSavePanel*) pPanel runModal];
527 doHandleResponse(pPanel, response, fileName, path,
nullptr);
531void IGraphicsMac::PromptForDirectory(WDL_String& dir, IFileDialogCompletionHandlerFunc completionHandler)
533 NSString* defaultPath;
537 defaultPath = [NSString stringWithUTF8String:dir.Get()];
541 defaultPath = [NSString stringWithUTF8String:DEFAULT_PATH];
542 dir.Set(DEFAULT_PATH);
545 NSOpenPanel* panelOpen = [NSOpenPanel openPanel];
547 [panelOpen setTitle:
@"Choose a Directory"];
548 [panelOpen setCanChooseFiles:NO];
549 [panelOpen setCanChooseDirectories:YES];
550 [panelOpen setResolvesAliases:YES];
551 [panelOpen setCanCreateDirectories:YES];
552 [panelOpen setFloatingPanel: YES];
553 [panelOpen setDirectoryURL: [NSURL fileURLWithPath: defaultPath]];
555 auto doHandleResponse = [](NSOpenPanel* pPanel, NSModalResponse response, WDL_String& pathAsync, IFileDialogCompletionHandlerFunc completionHandler){
556 if (response == NSOKButton)
558 NSString* fullPath = [pPanel filename] ;
559 pathAsync.Set([fullPath UTF8String]);
560 pathAsync.Append(
"/");
567 if (completionHandler)
569 WDL_String fileNameAsync;
570 completionHandler(fileNameAsync, pathAsync);
574 if (completionHandler)
576 NSWindow* pWindow = [(IGRAPHICS_VIEW*) mView window];
578 [panelOpen beginSheetModalForWindow:pWindow completionHandler:^(NSModalResponse response) {
579 WDL_String pathAsync;
580 doHandleResponse(panelOpen, response, pathAsync, completionHandler);
585 NSModalResponse response = [panelOpen runModal];
586 doHandleResponse(panelOpen, response, dir,
nullptr);
590bool IGraphicsMac::PromptForColor(
IColor& color,
const char* str, IColorPickerHandlerFunc func)
593 return [(IGRAPHICS_VIEW*) mView promptForColor:color : func];
602 dispatch_async(dispatch_get_main_queue(), ^{
607 NSRect areaRect = ToNSRect(
this, bounds);
608 pReturnMenu = [(IGRAPHICS_VIEW*) mView createPopupMenu: menu: areaRect];
611 if (pReturnMenu && pReturnMenu->GetFunction())
612 pReturnMenu->ExecFunction();
614 this->SetControlValueAfterPopupMenu(pReturnMenu);
620void IGraphicsMac::CreatePlatformTextEntry(
int paramIdx,
const IText& text,
const IRECT& bounds,
int length,
const char* str)
624 NSRect areaRect = ToNSRect(
this, bounds);
625 [(IGRAPHICS_VIEW*) mView createTextEntry: paramIdx : text: str: length: areaRect];
629ECursor IGraphicsMac::SetMouseCursor(ECursor cursorType)
632 [(IGRAPHICS_VIEW*) mView setMouseCursor: cursorType];
637bool IGraphicsMac::OpenURL(
const char* url,
const char* msgWindowTitle,
const char* confirmMsg,
const char* errMsgOnFailure)
639 #pragma REMINDER("Warning and error messages for OpenURL not implemented")
640 NSURL* pNSURL =
nullptr;
641 if (strstr(url,
"http"))
642 pNSURL = [NSURL URLWithString:[NSString stringWithUTF8String:url]];
644 pNSURL = [NSURL fileURLWithPath:[NSString stringWithUTF8String:url]];
648 bool ok = ([[NSWorkspace sharedWorkspace] openURL:pNSURL]);
654void* IGraphicsMac::GetWindow()
656 if (mView)
return mView;
661int IGraphicsMac::GetUserOSVersion()
663 return (
int) GetSystemVersion();
666bool IGraphicsMac::GetTextFromClipboard(WDL_String& str)
668 NSString* pTextOnClipboard = [[NSPasteboard generalPasteboard] stringForType: NSStringPboardType];
670 if (pTextOnClipboard == nil)
677 str.Set([pTextOnClipboard UTF8String]);
682bool IGraphicsMac::SetTextInClipboard(
const char* str)
684 NSString* pTextForClipboard = [NSString stringWithUTF8String:str];
685 [[NSPasteboard generalPasteboard] clearContents];
686 return [[NSPasteboard generalPasteboard] setString:pTextForClipboard forType:NSStringPboardType];
689bool IGraphicsMac::SetFilePathInClipboard(
const char* path)
691 NSPasteboard* pPasteboard = [NSPasteboard generalPasteboard];
692 [pPasteboard clearContents];
693 NSURL* pFileURL = [NSURL fileURLWithPath: [NSString stringWithUTF8String:path]];
694 BOOL success = [pPasteboard writeObjects: [NSArray arrayWithObject:pFileURL]];
695 return (
bool)success;
698bool IGraphicsMac::InitiateExternalFileDragDrop(
const char* path,
const IRECT& iconBounds)
700 NSPasteboardItem* pasteboardItem = [[NSPasteboardItem alloc] init];
701 NSURL* fileURL = [NSURL fileURLWithPath: [NSString stringWithUTF8String:path]];
702 [pasteboardItem setString:fileURL.absoluteString forType:NSPasteboardTypeFileURL];
704 NSDraggingItem* draggingItem = [[NSDraggingItem alloc] initWithPasteboardWriter:pasteboardItem];
705 NSRect draggingFrame = ToNSRect(
this, iconBounds);
706 NSImage* iconImage = [[NSWorkspace sharedWorkspace] iconForFile:fileURL.path];
707 [iconImage setSize:NSMakeSize(64, 64)];
708 [draggingItem setDraggingFrame:draggingFrame contents: iconImage];
710 IGRAPHICS_VIEW* view = (IGRAPHICS_VIEW*) mView;
711 NSDraggingSession* draggingSession = [view beginDraggingSessionWithItems:@[draggingItem] event:[NSApp currentEvent] source: view];
712 draggingSession.animatesToStartingPositionsOnCancelOrFail = YES;
713 draggingSession.draggingFormation = NSDraggingFormationNone;
715 ReleaseMouseCapture();
720EUIAppearance IGraphicsMac::GetUIAppearance()
const
722 if (@available(macOS 10.14, *)) {
725 IGRAPHICS_VIEW* pView = (IGRAPHICS_VIEW*) mView;
726 BOOL isDarkMode = [[[pView effectiveAppearance] name] isEqualToString: (NSAppearanceNameDarkAqua)];
727 return isDarkMode ? EUIAppearance::Dark : EUIAppearance::Light;
731 return EUIAppearance::Light;
734void IGraphicsMac::ActivateGLContext()
736 IGRAPHICS_VIEW* pView = (IGRAPHICS_VIEW*) mView;
737 [pView activateGLContext];
740void IGraphicsMac::DeactivateGLContext()
742 IGRAPHICS_VIEW* pView = (IGRAPHICS_VIEW*) mView;
743 [pView deactivateGLContext];
746#if defined IGRAPHICS_NANOVG
747 #include "IGraphicsNanoVG.cpp"
748#elif defined IGRAPHICS_SKIA
749 #include "IGraphicsSkia.cpp"
751 #error Either NO_IGRAPHICS or one and only one choice of graphics library must be defined!
This file contains the base IControl implementation, along with some base classes for specific types ...
The lowest level base class of an IGraphics control.
An editor delegate base class that uses IGraphics for the UI.
virtual ECursor SetMouseCursor(ECursor cursorType=ECursor::ARROW)
Sets the mouse cursor to one of ECursor (implementations should return the result of the base impleme...
Used to manage color data, independent of draw class/platform.
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,...