andlabs-ui/objc_darwin.go

154 lines
4.1 KiB
Go

// 28 february 2014
package ui
import (
"fmt"
"unsafe"
)
// #cgo LDFLAGS: -lobjc -framework Foundation
// #include <stdlib.h>
// #include "objc_darwin.h"
// /* cgo doesn't like Nil */
// Class NilClass = Nil;
import "C"
func objc_getClass(class string) C.id {
cclass := C.CString(class)
defer C.free(unsafe.Pointer(cclass))
return C.objc_getClass(cclass)
}
func sel_getUid(sel string) C.SEL {
csel := C.CString(sel)
defer C.free(unsafe.Pointer(csel))
return C.sel_getUid(csel)
}
// Common Objective-C types and selectors.
var (
_NSObject = objc_getClass("NSObject")
_NSString = objc_getClass("NSString")
_alloc = sel_getUid("alloc")
_new = sel_getUid("new")
_release = sel_getUid("release")
_stringWithUTF8String = sel_getUid("stringWithUTF8String:")
_UTF8String = sel_getUid("UTF8String")
)
func toNSString(str string) C.id {
cstr := C.CString(str)
defer C.free(unsafe.Pointer(cstr))
return C.objc_msgSend_str(_NSString,
_stringWithUTF8String,
cstr)
}
func fromNSString(str C.id) string {
cstr := C.objc_msgSend_noargs(str, _UTF8String)
return C.GoString((*C.char)(unsafe.Pointer(cstr)))
}
// These consolidate the NSScrollView code (used by listbox_darwin.go and area_darwin.go) into a single place.
var (
_NSScrollView = objc_getClass("NSScrollView")
_setHasHorizontalScroller = sel_getUid("setHasHorizontalScroller:")
_setHasVerticalScroller = sel_getUid("setHasVerticalScroller:")
_setAutohidesScrollers = sel_getUid("setAutohidesScrollers:")
_setDocumentView = sel_getUid("setDocumentView:")
_documentView = sel_getUid("documentView")
)
func newScrollView(content C.id) C.id {
scrollview := C.objc_msgSend_noargs(_NSScrollView, _alloc)
scrollview = initWithDummyFrame(scrollview)
C.objc_msgSend_bool(scrollview, _setHasHorizontalScroller, C.BOOL(C.YES))
C.objc_msgSend_bool(scrollview, _setHasVerticalScroller, C.BOOL(C.YES))
C.objc_msgSend_bool(scrollview, _setAutohidesScrollers, C.BOOL(C.YES))
C.objc_msgSend_id(scrollview, _setDocumentView, content)
return scrollview
}
func getScrollViewContent(scrollview C.id) C.id {
return C.objc_msgSend_noargs(scrollview, _documentView)
}
// These create new classes.
// selector contains the information for a new selector.
type selector struct {
name string
imp uintptr // not unsafe.Pointer because https://code.google.com/p/go/issues/detail?id=7665
itype itype
desc string // for error reporting
}
// sel_[returntype] or sel_[returntype]_[arguments] (after the required self/sel arguments)
type itype uint
const (
sel_void_id itype = iota
sel_bool_id
sel_bool
sel_void_rect
sel_terminatereply_id
nitypes
)
var itypes = [nitypes][]C.char{
sel_void_id: []C.char{'v', '@', ':', '@', 0},
sel_bool_id: []C.char{'c', '@', ':', '@', 0},
sel_bool: []C.char{'c', '@', ':', 0},
sel_void_rect: nil, // see init() below
sel_terminatereply_id: nil,
}
func init() {
// see encodedNSRect in bleh_darwin.m
x := make([]C.char, 0, 256) // more than enough
x = append(x, 'v', '@', ':')
y := C.GoString(C.encodedNSRect)
for _, b := range y {
x = append(x, C.char(b))
}
x = append(x, 0)
itypes[sel_void_rect] = x
x = make([]C.char, 0, 256) // more than enough
y = C.GoString(C.encodedTerminateReply)
for _, b := range y {
x = append(x, C.char(b))
}
x = append(x, '@', ':', '@', 0)
itypes[sel_terminatereply_id] = x
}
func makeClass(name string, super C.id, sels []selector, desc string) (id C.id, err error) {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
// an id that describes a class is itself a Class
// thanks to Psy| in irc.freenode.net/##objc
c := C.objc_allocateClassPair(C.Class(unsafe.Pointer(super)), cname, 0)
if c == C.NilClass {
err = fmt.Errorf("unable to create Objective-C class %s for %s; reason unknown", name, desc)
return
}
C.objc_registerClassPair(c)
for _, v := range sels {
ok := C.class_addMethod(c, sel_getUid(v.name),
C.IMP(unsafe.Pointer(v.imp)), &itypes[v.itype][0])
if ok == C.BOOL(C.NO) {
err = fmt.Errorf("unable to add selector %s to class %s (needed for %s; reason unknown)", v.name, name, v.desc)
return
}
}
return objc_getClass(name), nil
}