public void Unmaximize() { if (ObjC.Call(Handle, "isZoomed") != IntPtr.Zero) { ObjC.Call(Handle, "zoom:", Handle); } }
private static void FinishUriSchemeCallback(IntPtr url, IntPtr schemeTask, IntPtr data, long contentLength, Uri uri) { IntPtr response = Foundation.Call("NSURLResponse", "alloc"); ObjC.Call( response, "initWithURL:MIMEType:expectedContentLength:textEncodingName:", url, NSString.Create(MimeTypes.FindForUri(uri)), new IntPtr(contentLength), IntPtr.Zero); ObjC.Call(schemeTask, "didReceiveResponse:", response); IntPtr nsData = Foundation.Call( "NSData", "dataWithBytesNoCopy:length:freeWhenDone:", data, new IntPtr(contentLength), IntPtr.Zero); ObjC.Call(schemeTask, "didReceiveData:", nsData); ObjC.Call(schemeTask, "didFinish"); }
public Task <string?> ExecuteScriptAsync(string script) { var taskResult = new TaskCompletionSource <string?>(); NSBlock?block = null; ScriptEvalCallbackDelegate callback = (IntPtr self, IntPtr result, IntPtr error) => { try { if (error != IntPtr.Zero) { string?message = NSString.GetString(ObjC.Call(error, "localizedDescription")); taskResult.TrySetException(new ScriptException($"Script execution failed with: \"{message}\"")); } else { string?content = NSString.GetString(result); taskResult.TrySetResult(content); } } catch (Exception ex) { taskResult.TrySetException(ex); } finally { block !.Dispose(); } }; block = new NSBlock(callback); ObjC.Call( Handle, "evaluateJavaScript:completionHandler:", NSString.Create(script), block.Handle); return(taskResult.Task); }
private static NativeClassDefinition CreateAppDelegate() { var definition = NativeClassDefinition.FromClass( "SpiderEyeAppDelegate", AppKit.GetClass("NSResponder"), // note: NSApplicationDelegate is not available at runtime and returns null AppKit.GetProtocol("NSApplicationDelegate"), AppKit.GetProtocol("NSTouchBarProvider")); definition.AddMethod <ShouldTerminateDelegate>( "applicationShouldTerminateAfterLastWindowClosed:", "c@:@", (self, op, notification) => (byte)(Application.ExitWithLastWindow ? 1 : 0)); definition.AddMethod <NotificationDelegate>( "applicationDidFinishLaunching:", "v@:@", (self, op, notification) => { var instance = definition.GetParent <CocoaApplication>(self); ObjC.Call(instance.Handle, "activateIgnoringOtherApps:", true); }); definition.FinishDeclaration(); return(definition); }
public CocoaWindow(WindowConfiguration config, WebviewBridge bridge) { if (config == null) { throw new ArgumentNullException(nameof(config)); } if (bridge == null) { throw new ArgumentNullException(nameof(bridge)); } Handle = AppKit.Call("NSWindow", "alloc"); canResizeField = config.CanResize; var style = GetWantedStyleMask(); ObjC.SendMessage( Handle, ObjC.RegisterName("initWithContentRect:styleMask:backing:defer:"), new CGRect(0, 0, config.Size.Width, config.Size.Height), new UIntPtr((uint)style), new UIntPtr(2), false); webview = new CocoaWebview(bridge); ObjC.Call(Handle, "setContentView:", webview.Handle); webview.TitleChanged += Webview_TitleChanged; windowDelegate = WindowDelegateDefinition.CreateInstance(this); ObjC.Call(Handle, "setDelegate:", windowDelegate.Handle); }
public NSRunLoop() { loop = Foundation.Call("NSRunLoop", "currentRunLoop"); mode = ObjC.Call(loop, "currentMode"); date = Foundation.Call("NSDate", "distantFuture"); method = ObjC.RegisterName("runMode:beforeDate:"); }
public void Show() { ObjC.Call(Handle, "center"); ObjC.Call(Handle, "makeKeyAndOrderFront:", IntPtr.Zero); MacApplication.SynchronizationContext.Post(s => Shown?.Invoke(this, EventArgs.Empty), null); }
public void ExitFullscreen() { if (StyleMask.HasFlag(NSWindowStyleMask.FullScreen)) { ObjC.Call(Handle, "toggleFullScreen:", Handle); } }
private string CreateSchemeHandler(IntPtr configuration) { string host = null; if (string.IsNullOrWhiteSpace(config.ExternalHost)) { const string scheme = "spidereye"; host = UriTools.GetRandomResourceUrl(scheme); IntPtr handlerClass = ObjC.AllocateClassPair(ObjC.GetClass("NSObject"), "SchemeHandler" + count, IntPtr.Zero); ObjC.AddProtocol(handlerClass, ObjC.GetProtocol("WKURLSchemeHandler")); ObjC.AddMethod( handlerClass, ObjC.RegisterName("webView:startURLSchemeTask:"), uriSchemeStartDelegate, "v@:@@"); ObjC.AddMethod( handlerClass, ObjC.RegisterName("webView:stopURLSchemeTask:"), uriSchemeStopDelegate, "v@:@@"); ObjC.RegisterClassPair(handlerClass); IntPtr handler = ObjC.Call(handlerClass, "new"); ObjC.Call(configuration, "setURLSchemeHandler:forURLScheme:", handler, NSString.Create(scheme)); } return(host); }
public void Dispose() { if (release && Handle != IntPtr.Zero) { ObjC.Call(Handle, "release"); } }
private IntPtr CreateCallbackClass() { IntPtr callbackClass = ObjC.AllocateClassPair(ObjC.GetClass("NSObject"), "CallbackClass" + count, IntPtr.Zero); ObjC.AddProtocol(callbackClass, ObjC.GetProtocol("WKNavigationDelegate")); ObjC.AddProtocol(callbackClass, ObjC.GetProtocol("WKScriptMessageHandler")); ObjC.AddMethod( callbackClass, ObjC.RegisterName("webView:didFinishNavigation:"), loadDelegate, "v@:@@"); ObjC.AddMethod( callbackClass, ObjC.RegisterName("webView:didFailNavigation:withError:"), loadFailedDelegate, "v@:@@@"); ObjC.AddMethod( callbackClass, ObjC.RegisterName("observeValueForKeyPath:ofObject:change:context:"), observedValueChangedDelegate, "v@:@@@@"); ObjC.AddMethod( callbackClass, ObjC.RegisterName("userContentController:didReceiveScriptMessage:"), scriptDelegate, "v@:@@"); ObjC.RegisterClassPair(callbackClass); return(ObjC.Call(callbackClass, "new")); }
/// <summary> /// Creates and adds an app menu with default values. /// </summary> /// <returns>The app menu.</returns> public static IMenu CreateDefaultAppMenu() { // TODO: use app name in menu items var menu = CreateAppMenu(); var appMenu = menu.AddLabelMenuItem(string.Empty); var about = appMenu.AddLabelMenuItem("About"); about.Click += (s, e) => ObjC.Call(AppHandle, "orderFrontStandardAboutPanel:", AppHandle); appMenu.AddSeparatorMenuItem(); var hide = appMenu.AddLabelMenuItem("Hide"); hide.SetShortcut(ModifierKey.Super, Key.H); hide.Click += (s, e) => ObjC.Call(AppHandle, "hide:", AppHandle); var hideOthers = appMenu.AddLabelMenuItem("Hide Others"); hideOthers.SetShortcut(ModifierKey.Super | ModifierKey.Alt, Key.H); hideOthers.Click += (s, e) => ObjC.Call(AppHandle, "hideOtherApplications:", AppHandle); var showAll = appMenu.AddLabelMenuItem("Show All"); showAll.Click += (s, e) => ObjC.Call(AppHandle, "unhideAllApplications:", AppHandle); appMenu.AddSeparatorMenuItem(); var quit = appMenu.AddLabelMenuItem("Quit"); quit.SetShortcut(ModifierKey.Super, Key.Q); quit.Click += (s, e) => Exit(); return(Application.appMenu = menu); }
public CocoaWebview(WindowConfiguration config, IContentProvider contentProvider, WebviewBridge bridge) { this.config = config ?? throw new ArgumentNullException(nameof(config)); this.contentProvider = contentProvider ?? throw new ArgumentNullException(nameof(contentProvider)); this.bridge = bridge ?? throw new ArgumentNullException(nameof(bridge)); Interlocked.Increment(ref count); // need to keep the delegates around or they will get garbage collected loadDelegate = LoadCallback; loadFailedDelegate = LoadFailedCallback; observedValueChangedDelegate = ObservedValueChanged; scriptDelegate = ScriptCallback; uriSchemeStartDelegate = UriSchemeStartCallback; uriSchemeStopDelegate = UriSchemeStopCallback; IntPtr configuration = WebKit.Call("WKWebViewConfiguration", "new"); IntPtr manager = ObjC.Call(configuration, "userContentController"); IntPtr callbackClass = CreateCallbackClass(); customHost = CreateSchemeHandler(configuration); if (config.EnableScriptInterface) { ObjC.Call(manager, "addScriptMessageHandler:name:", callbackClass, NSString.Create("external")); IntPtr script = WebKit.Call("WKUserScript", "alloc"); ObjC.Call( script, "initWithSource:injectionTime:forMainFrameOnly:", NSString.Create(Resources.GetInitScript("Mac")), IntPtr.Zero, IntPtr.Zero); ObjC.Call(manager, "addUserScript:", script); } Handle = WebKit.Call("WKWebView", "alloc"); ObjC.Call(Handle, "initWithFrame:configuration:", CGRect.Zero, configuration); ObjC.Call(Handle, "setNavigationDelegate:", callbackClass); IntPtr bgColor = NSColor.FromHex(config.BackgroundColor); ObjC.Call(Handle, "setBackgroundColor:", bgColor); IntPtr boolValue = Foundation.Call("NSNumber", "numberWithBool:", 0); ObjC.Call(Handle, "setValue:forKey:", boolValue, NSString.Create("drawsBackground")); if (config.UseBrowserTitle) { ObjC.Call(Handle, "addObserver:forKeyPath:options:context:", callbackClass, NSString.Create("title"), IntPtr.Zero, IntPtr.Zero); } if (enableDevTools) { var preferences = ObjC.Call(configuration, "preferences"); ObjC.Call(preferences, "setValue:forKey:", new IntPtr(1), NSString.Create("developerExtrasEnabled")); } }
public void SetShortcut(ModifierKey modifier, Key key) { NSEventModifierFlags nsModifier = KeyMapper.GetModifier(modifier); string mappedKey = KeyMapper.GetKey(key); ObjC.Call(Handle, "setKeyEquivalentModifierMask:", new UIntPtr((ulong)nsModifier)); ObjC.Call(Handle, "setKeyEquivalent:", NSString.Create(mappedKey)); }
/// <summary> /// Creates and adds an empty app menu. /// </summary> /// <returns>The app menu.</returns> public static IMenu CreateAppMenu() { var menu = new CocoaMenu(); ObjC.Call(AppHandle, "setMainMenu:", menu.Handle); return(appMenu = menu); }
public IMenu AddMenu() { var menu = new CocoaMenu(); ObjC.Call(statusItem, "setMenu:", menu.Handle); return(menu); }
public CocoaStatusIcon() { var statusBar = AppKit.Call("NSStatusBar", "systemStatusBar"); statusItem = ObjC.Call(statusBar, "statusItemWithLength:", -2.0); // -1 = variable size; -2 = square size ObjC.Call(statusItem, "setHighlightMode:", true); statusBarButton = ObjC.Call(statusItem, "button"); ObjC.Call(statusBarButton, "setImageScaling:", 3); // 3 = scale proportionally up or down }
public static string?GetAsString(IntPtr handle) { if (handle == IntPtr.Zero) { return(null); } return(NSString.GetString(ObjC.Call(handle, "absoluteString"))); }
private static void UriSchemeStartCallback(CocoaWebview instance, IntPtr schemeTask) { try { IntPtr request = ObjC.Call(schemeTask, "request"); IntPtr url = ObjC.Call(request, "URL"); var uri = URL.GetAsUri(url) !; var host = new Uri(uri.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped)); if (host == instance.customHost) { using var contentStream = Application.ContentProvider.GetStreamAsync(uri).GetAwaiter().GetResult(); if (contentStream != null) { if (contentStream is UnmanagedMemoryStream unmanagedMemoryStream) { unsafe { long length = unmanagedMemoryStream.Length - unmanagedMemoryStream.Position; var data = (IntPtr)unmanagedMemoryStream.PositionPointer; FinishUriSchemeCallback(url, schemeTask, data, length, uri); return; } } else { byte[] data; long length; if (contentStream is MemoryStream memoryStream) { data = memoryStream.GetBuffer(); length = memoryStream.Length; } else { using var copyStream = new MemoryStream(); contentStream.CopyTo(copyStream); data = copyStream.GetBuffer(); length = copyStream.Length; } unsafe { fixed(byte *dataPtr = data) { FinishUriSchemeCallback(url, schemeTask, (IntPtr)dataPtr, length, uri); return; } } } } } FinishUriSchemeCallbackWithError(schemeTask, 404); } catch { FinishUriSchemeCallbackWithError(schemeTask, 500); } }
public CocoaWindow(WindowConfiguration config, IUiFactory windowFactory) { if (windowFactory == null) { throw new ArgumentNullException(nameof(windowFactory)); } this.config = config ?? throw new ArgumentNullException(nameof(config)); Interlocked.Increment(ref count); // need to keep the delegates around or they will get garbage collected windowShouldCloseDelegate = WindowShouldCloseCallback; windowWillCloseDelegate = WindowWillCloseCallback; Handle = AppKit.Call("NSWindow", "alloc"); var style = NSWindowStyleMask.Titled | NSWindowStyleMask.Closable | NSWindowStyleMask.Miniaturizable; if (config.CanResize) { style |= NSWindowStyleMask.Resizable; } ObjC.SendMessage( Handle, ObjC.RegisterName("initWithContentRect:styleMask:backing:defer:"), new CGRect(0, 0, config.Width, config.Height), (int)style, 2, 0); Title = config.Title; IntPtr bgColor = NSColor.FromHex(config.BackgroundColor); ObjC.Call(Handle, "setBackgroundColor:", bgColor); var contentProvider = new EmbeddedFileProvider(config.ContentAssembly, config.ContentFolder); bridge = new WebviewBridge(); webview = new CocoaWebview(config, contentProvider, bridge); ObjC.Call(Handle, "setContentView:", webview.Handle); if (config.EnableScriptInterface) { bridge.Init(this, webview, windowFactory); } if (config.UseBrowserTitle) { webview.TitleChanged += Webview_TitleChanged; bridge.TitleChanged += Webview_TitleChanged; } SetWindowDelegate(Handle); }
private CocoaLabelMenuItem(string label, string action) : base(AppKit.Call("NSMenuItem", "alloc")) { ObjC.Call( Handle, "initWithTitle:action:keyEquivalent:", NSString.Create(label), ObjC.RegisterName(action), NSString.Create(string.Empty)); }
private void ObservedValueChanged(IntPtr self, IntPtr op, IntPtr keyPath, IntPtr obj, IntPtr change, IntPtr context) { string key = NSString.GetString(keyPath); if (key == "title") { string title = NSString.GetString(ObjC.Call(Handle, "title")); TitleChanged?.Invoke(this, title); } }
private static void ObservedValueChanged(CocoaWebview instance, IntPtr keyPath) { string?key = NSString.GetString(keyPath); if (key == "title" && instance.UseBrowserTitle) { string?title = NSString.GetString(ObjC.Call(instance.Handle, "title")); instance.TitleChanged?.Invoke(instance, title ?? string.Empty); } }
protected override NSDialog CreateDialog() { var panel = NSDialog.CreateOpenPanel(); ObjC.Call(panel.Handle, "setCanChooseFiles:", true); ObjC.Call(panel.Handle, "setCanChooseDirectories:", false); ObjC.Call(panel.Handle, "setAllowsMultipleSelection:", Multiselect); return(panel); }
public CocoaStatusIcon(string title) { var statusBar = AppKit.Call("NSStatusBar", "systemStatusBar"); statusItem = ObjC.Call(statusBar, "statusItemWithLength:", -2.0); // -1 = variable size; -2 = square size ObjC.Call(statusItem, "setHighlightMode:", true); statusBarButton = ObjC.Call(statusItem, "button"); ObjC.Call(statusBarButton, "setImageScaling:", new UIntPtr((uint)NSImageScaling.ProportionallyUpOrDown)); Title = title; }
private async void ScriptCallback(IntPtr self, IntPtr op, IntPtr notification, IntPtr message) { IntPtr body = ObjC.Call(message, "body"); IntPtr isString = ObjC.Call(body, "isKindOfClass:", Foundation.GetClass("NSString")); if (isString != IntPtr.Zero) { string data = NSString.GetString(body); await bridge.HandleScriptCall(data); } }
protected internal override void AddItem(IntPtr item) { if (submenu == null) { submenu = new CocoaMenu(); SetTitle(submenu.Handle, GetTitle()); ObjC.Call(Handle, "setSubmenu:", submenu.Handle); } submenu.AddItem(item); }
public void AddItem(IMenuItem item) { if (item == null) { throw new ArgumentNullException(nameof(item)); } var nativeItem = NativeCast.To <CocoaMenuItem>(item); ObjC.Call(Handle, "addItem:", nativeItem.Handle); }
private static void FinishUriSchemeCallbackWithError(IntPtr schemeTask, int errorCode) { var error = Foundation.Call( "NSError", "errorWithDomain:code:userInfo:", NSString.Create("com.bildstein.spidereye"), new IntPtr(errorCode), IntPtr.Zero); ObjC.Call(schemeTask, "didFailWithError:", error); }
public static string GetString(IntPtr handle) { if (handle == IntPtr.Zero) { return(null); } IntPtr utf8 = ObjC.Call(handle, "UTF8String"); return(Utf8PointerToString(utf8)); }