You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
Armando Rivera 998102b1b4 Updated readme and AppBundler screenshot 6 months ago
cocoa Added AppDelegate object 6 months ago
doc Update 'doc/NSFunctions.md' 3 years ago
examples Minor widget alignment tweak 6 months ago
images Updated readme and AppBundler screenshot 6 months ago
tests Configured nimble to build/install the bundle binary 3 years ago
.gitignore Updated Exclusions 3 years ago
LICENSE Updated LICENSE file 3 years ago
README.md Updated readme and AppBundler screenshot 6 months ago
bundle.nim Rearraigned to address issue with Nimble 3 years ago
cocoa.nim Added 'setDelegate' method 6 months ago
cocoa.nimble Changed to use cocoa_lib.nim to create libcocoa.a library file 3 years ago
cocoa_lib.nim Added NSStatusBar (systray) support 3 years ago

README.md

NimCocoa

NimCocoa is an experimental implementation of a Native GUI for the Nim programming language running on macOS.

Rather than rely on low level calls to objc_msgsend and it's variants, it uses actual Objective-C code modules I created that are wrapped using C-Style functions, which Nim can then use.

At the moment, the following GUI objects are available:

NSWindow NSMenu NSSavedialog NSSlider
NSButton NSTextfield NSMessagebox NSListbox NSContainer (Groupbox)
NSLabel NSCheckbox NSOpendialog NSTextedit NSTableView
NSCombobox NSDialog NSLine NSRadioButton

Preliminary documentation for GUI objects is available in the doc folder and in the wiki.

Here is an example of what coding with NimCocoa looks like:

import Cocoa
import json, os, plists, times

var mainWin: ID

var lblFile, txtFile, btnFile, lblAuthor, txtAuthor, lblApp, 
    txtApp, lblImage, txtImage, btnImage, line1, lblVersion, 
    txtVersion, lblIdent, txtIdent, line2, chkLaunch, btnExec,
    btnQuit: ID

const
  width:cint = 800
  height:cint = 310
  winStyle = NSWindowStyleMaskTitled or NSWindowStyleMaskClosable or NSWindowStyleMaskMiniaturizable

proc getExecutable(sender: ID) {.cdecl.} =
  let fName = newOpenDialog(mainWin, "")
  if fName.len > 0:
    txtFile.text = fName

proc getImage(sender: ID) {.cdecl.} =
  let fName = newOpenDialog(mainWin, "icns")
  if fName.len > 0:
    txtImage.text = fName
  
proc quit(sender: ID) {.cdecl.} =
  Cocoa_Quit(mainWin)

proc createAppBundle(sender: ID) {.cdecl.} =
  let dt = now()

  let appAuthor = $txtAuthor.text
  let appName = $txtFile.text
  let iconFile = $txtImage.text
  let bundleName = $txtApp.text
  let bundleIdentifier = $txtIdent.text
  let appVersion = $txtVersion.text
  let appInfo = appVersion & " Created by " & appAuthor & " on " & dt.format("MM-dd-yyyy")
  let appCopyRight = "Copyright" & dt.format(" yyyy ") & appAuthor & ". All rights reserved."
  let appBundle = bundleName & ".app"

  var pl = %*
    { "CFBundlePackageType" : "APPL",
      "CFBundleInfoDictionaryVersion" : "6.0",
      "CFBundleName" : bundleName,
      "CFBundleExecutable" : bundleName,
      "CFBundleIconFile" : extractFilename(iconFile) ,
      "CFBundleIdentifier" : bundleIdentifier ,
      "CFBundleVersion" : appVersion ,
      "CFBundleGetInfoString" : appInfo,
      "CFBundleShortVersionString" : appVersion ,
      "NSHumanReadableCopyright" : appCopyRight ,
      "NSPrincipalClass" : "NSApplication" ,
      "NSMainNibFile" : "MainMenu" 
    }

  createDir(appBundle & "/Contents/MacOS")
  createDir(appBundle & "/Contents/Resources")
  createDir(appBundle & "/Contents/Frameworks")

  if appName.fileExists:
    appName.copyFileWithPermissions(appBundle & "/Contents/MacOS/" & bundleName)

    if iconFile.fileExists:
      iconFile.copyFileWithPermissions(appBundle & "/Contents/Resources/" & extractFileName(iconFile))

    if "Credits.rtf".fileExists:
      "Credits.rtf".copyFileWithPermissions(appBundle & "/Contents/Resources/Credits.rtf")

        
    pl.writePlist(appBundle & "/Contents/Info.plist")

    discard execShellCmd("touch " & appBundle)

    if chkLaunch.state == 1:
      discard execShellCmd("open " & appBundle)


proc main() =

  Cocoa_Init()

  mainWin = newWindow("macOS Application Bundler", width, height, winStyle)

  lblFile = newLabel(mainWin, "Select Executable",30, 20, 120, 25)
  txtFile = newTextField(mainWin, "", 160, 20, 500, 25)
  btnFile = newButton(mainWin, "Load", 680, 20, 100, 25, getExecutable)
  txtFile.anchor=akWidth; btnFile.anchor=akRight

  lblAuthor = newLabel(mainWin, "Author Name", 30, 60, 120, 25)
  txtAuthor = newTextField(mainWin, "", 160, 60, 500, 25)
  txtAuthor.anchor=akWidth

  lblApp = newLabel(mainWin, "Application Name", 30, 100, 120, 25)
  txtApp = newTextField(mainWin, "", 160, 100, 170, 25)
  txtApp.anchor=akWidth

  lblImage = newLabel(mainWin, "Select Icon File", 30, 200, 120, 25)
  txtImage = newTextField(mainWin, "", 160, 200, 500, 25)
  btnImage = newButton(mainWin, "Load", 680, 200, 100, 25, getImage)
  txtImage.anchor=akWidth; btnImage.anchor=akRight

  line1 = newSeparator(mainWin, 30, 140, 750)

  lblVersion = newLabel(mainWin, "Application Version", 350, 100, 130, 25)
  txtVersion = newTextField(mainWin, "", 490, 100, 170, 25)
  txtVersion.anchor=akRight; lblVersion.anchor=akRight

  lblIdent = newLabel(mainWin, "Bundle Identifier", 30, 160, 120, 25)
  txtIdent = newTextField(mainWin, "", 160, 160, 500, 25)
  # btnCredits = newButton(mainWin, "Load", 680, 160, 100, 25, nil)
  txtIdent.anchor=akWidth

  line2 = newSeparator(mainWin, 20, 250, 750)

  chkLaunch = newCheckBox(mainWin, "Launch Application?", 330, 270, 150, 25)
  btnExec = newButton(mainWin, "🟢 Execute", 680, 270, 100, 25, createAppBundle)
  chkLaunch.anchor=akLeft + akRight + akBottom; btnExec.anchor=akRight + akBottom

  btnQuit = newButton(mainWin, "🔴 Quit", 565, 270, 100, 25, quit)

  Cocoa_Run(mainWin)
  

if isMainModule:
  main()

Which results in the following (with Darkmode enabled):

Another screenshot:

Same app resized, showing how objects flow based on their associated 'anchor' setting:

I am working on documenting the available objects/functions as well as examples.

Stay Tuned!