2014-03-01 12:29:24 -06:00
|
|
|
// 1 march 2014
|
|
|
|
package ui
|
|
|
|
|
|
|
|
import (
|
2014-03-01 12:53:29 -06:00
|
|
|
"fmt"
|
2014-03-01 12:29:24 -06:00
|
|
|
"unsafe"
|
2014-03-01 16:06:06 -06:00
|
|
|
"sync"
|
2014-03-01 12:29:24 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
// #cgo LDFLAGS: -lobjc -framework Foundation -framework AppKit
|
|
|
|
// #include "objc_darwin.h"
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
type sysData struct {
|
|
|
|
cSysData
|
|
|
|
|
|
|
|
id C.id
|
|
|
|
}
|
|
|
|
|
|
|
|
type classData struct {
|
2014-03-02 13:43:31 -06:00
|
|
|
make func(parentWindow C.id, alternate bool) C.id
|
2014-03-01 12:53:29 -06:00
|
|
|
show func(what C.id)
|
|
|
|
hide func(what C.id)
|
2014-03-01 12:29:24 -06:00
|
|
|
settextsel C.SEL
|
|
|
|
textsel C.SEL
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
_NSWindow = objc_getClass("NSWindow")
|
2014-03-01 20:34:37 -06:00
|
|
|
_NSButton = objc_getClass("NSButton")
|
2014-03-01 12:29:24 -06:00
|
|
|
|
|
|
|
_initWithContentRect = sel_getUid("initWithContentRect:styleMask:backing:defer:")
|
2014-03-01 20:34:37 -06:00
|
|
|
_initWithFrame = sel_getUid("initWithFrame:")
|
2014-03-01 12:29:24 -06:00
|
|
|
_makeKeyAndOrderFront = sel_getUid("makeKeyAndOrderFront:")
|
|
|
|
_orderOut = sel_getUid("orderOut:")
|
|
|
|
_setHidden = sel_getUid("setHidden:")
|
|
|
|
_setTitle = sel_getUid("setTitle:")
|
|
|
|
_setStringValue = sel_getUid("setStringValue:")
|
|
|
|
_setFrame = sel_getUid("setFrame:")
|
|
|
|
_state = sel_getUid("state")
|
|
|
|
_title = sel_getUid("title")
|
|
|
|
_stringValue = sel_getUid("stringValue")
|
|
|
|
// TODO others
|
2014-03-01 14:35:42 -06:00
|
|
|
_frame = sel_getUid("frame")
|
2014-03-01 12:29:24 -06:00
|
|
|
_setFrameDisplay = sel_getUid("setFrame:display:")
|
2014-03-01 20:34:37 -06:00
|
|
|
_setBezelStyle = sel_getUid("setBezelStyle:")
|
|
|
|
_setTarget = sel_getUid("setTarget:")
|
|
|
|
_setAction = sel_getUid("setAction:")
|
|
|
|
_contentView = sel_getUid("contentView")
|
|
|
|
_addSubview = sel_getUid("addSubview:")
|
2014-03-02 08:17:42 -06:00
|
|
|
_setButtonType = sel_getUid("setButtonType:")
|
2014-03-01 12:29:24 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
func controlShow(what C.id) {
|
|
|
|
C.objc_msgSend_bool(what, _setHidden, C.BOOL(C.NO))
|
|
|
|
}
|
|
|
|
|
|
|
|
func controlHide(what C.id) {
|
|
|
|
C.objc_msgSend_bool(what, _setHidden, C.BOOL(C.YES))
|
|
|
|
}
|
|
|
|
|
|
|
|
var classTypes = [nctypes]*classData{
|
|
|
|
c_window: &classData{
|
2014-03-02 13:43:31 -06:00
|
|
|
make: func(parentWindow C.id, alternate bool) C.id {
|
2014-03-01 12:29:24 -06:00
|
|
|
const (
|
|
|
|
NSBorderlessWindowMask = 0
|
|
|
|
NSTitledWindowMask = 1 << 0
|
|
|
|
NSClosableWindowMask = 1 << 1
|
|
|
|
NSMiniaturizableWindowMask = 1 << 2
|
|
|
|
NSResizableWindowMask = 1 << 3
|
|
|
|
NSTexturedBackgroundWindowMask = 1 << 8
|
|
|
|
)
|
|
|
|
|
|
|
|
// we have to specify a content rect to start; it will be overridden soon though
|
|
|
|
win := objc_alloc(_NSWindow)
|
2014-03-01 16:15:26 -06:00
|
|
|
win = objc_msgSend_rect_uint_uint_bool(win,
|
2014-03-01 12:29:24 -06:00
|
|
|
_initWithContentRect,
|
|
|
|
0, 0, 100, 100,
|
2014-03-01 17:04:09 -06:00
|
|
|
NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask,
|
2014-03-01 12:29:24 -06:00
|
|
|
2, // NSBackingStoreBuffered - the only backing store method that Apple says we should use (the others are legacy)
|
|
|
|
C.BOOL(C.YES)) // defer creation of device until we show the window
|
2014-03-01 16:15:26 -06:00
|
|
|
objc_setDelegate(win, appDelegate)
|
|
|
|
return win
|
2014-03-01 12:29:24 -06:00
|
|
|
},
|
|
|
|
show: func(what C.id) {
|
2014-03-01 12:53:29 -06:00
|
|
|
C.objc_msgSend_id(what, _makeKeyAndOrderFront, what)
|
2014-03-01 12:29:24 -06:00
|
|
|
},
|
|
|
|
hide: func(what C.id) {
|
2014-03-01 12:53:29 -06:00
|
|
|
C.objc_msgSend_id(what, _orderOut, what)
|
2014-03-01 12:29:24 -06:00
|
|
|
},
|
|
|
|
settextsel: _setTitle,
|
|
|
|
textsel: _title,
|
|
|
|
},
|
|
|
|
c_button: &classData{
|
2014-03-02 13:43:31 -06:00
|
|
|
make: func(parentWindow C.id, alternate bool) C.id {
|
2014-03-01 20:34:37 -06:00
|
|
|
button := objc_alloc(_NSButton)
|
|
|
|
// NSControl requires that we specify a frame; dummy frame for now
|
|
|
|
button = objc_msgSend_rect(button, _initWithFrame,
|
|
|
|
0, 0, 100, 100)
|
|
|
|
objc_msgSend_uint(button, _setBezelStyle, 1) // NSRoundedBezelStyle
|
|
|
|
C.objc_msgSend_id(button, _setTarget, appDelegate)
|
|
|
|
C.objc_msgSend_sel(button, _setAction, _buttonClicked)
|
|
|
|
windowView := C.objc_msgSend_noargs(parentWindow, _contentView)
|
|
|
|
C.objc_msgSend_id(windowView, _addSubview, button)
|
|
|
|
return button
|
|
|
|
},
|
|
|
|
show: controlShow,
|
|
|
|
hide: controlHide,
|
|
|
|
settextsel: _setTitle,
|
|
|
|
textsel: _title,
|
2014-03-01 12:29:24 -06:00
|
|
|
},
|
|
|
|
c_checkbox: &classData{
|
2014-03-02 13:43:31 -06:00
|
|
|
make: func(parentWindow C.id, alternate bool) C.id {
|
2014-03-02 08:17:42 -06:00
|
|
|
checkbox := objc_alloc(_NSButton)
|
|
|
|
checkbox = objc_msgSend_rect(checkbox, _initWithFrame,
|
|
|
|
0, 0, 100, 100)
|
|
|
|
objc_msgSend_uint(checkbox, _setButtonType, 3) // NSSwitchButton
|
|
|
|
windowView := C.objc_msgSend_noargs(parentWindow, _contentView)
|
|
|
|
C.objc_msgSend_id(windowView, _addSubview, checkbox)
|
|
|
|
return checkbox
|
|
|
|
},
|
|
|
|
show: controlShow,
|
|
|
|
hide: controlHide,
|
|
|
|
settextsel: _setTitle,
|
|
|
|
textsel: _title,
|
2014-03-01 12:29:24 -06:00
|
|
|
},
|
|
|
|
c_combobox: &classData{
|
|
|
|
},
|
|
|
|
c_lineedit: &classData{
|
|
|
|
},
|
|
|
|
c_label: &classData{
|
|
|
|
},
|
|
|
|
c_listbox: &classData{
|
|
|
|
},
|
|
|
|
c_progressbar: &classData{
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2014-03-01 16:06:06 -06:00
|
|
|
// I need to access sysData from appDelegate, but appDelegate doesn't store any data. So, this.
|
|
|
|
var (
|
|
|
|
sysdatas = make(map[C.id]*sysData)
|
|
|
|
sysdatalock sync.Mutex
|
|
|
|
)
|
|
|
|
|
|
|
|
func addSysData(key C.id, value *sysData) {
|
|
|
|
sysdatalock.Lock()
|
|
|
|
sysdatas[key] = value
|
|
|
|
sysdatalock.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func getSysData(key C.id) *sysData {
|
|
|
|
sysdatalock.Lock()
|
|
|
|
defer sysdatalock.Unlock()
|
|
|
|
|
|
|
|
v, ok := sysdatas[key]
|
|
|
|
if !ok {
|
|
|
|
panic(fmt.Errorf("internal error: getSysData(%v) unknown", key))
|
|
|
|
}
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2014-03-01 12:29:24 -06:00
|
|
|
func (s *sysData) make(initText string, window *sysData) error {
|
|
|
|
var parentWindow C.id
|
|
|
|
|
|
|
|
ct := classTypes[s.ctype]
|
|
|
|
if ct.make == nil {
|
|
|
|
println(s.ctype, "not implemented")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if window != nil {
|
|
|
|
parentWindow = window.id
|
|
|
|
}
|
|
|
|
ret := make(chan C.id)
|
|
|
|
defer close(ret)
|
|
|
|
uitask <- func() {
|
2014-03-02 13:43:31 -06:00
|
|
|
ret <- ct.make(parentWindow, s.alternate)
|
2014-03-01 12:29:24 -06:00
|
|
|
}
|
|
|
|
s.id = <-ret
|
|
|
|
err := s.setText(initText)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error setting initial text of new window/control: %v", err)
|
|
|
|
}
|
2014-03-01 16:06:06 -06:00
|
|
|
addSysData(s.id, s)
|
2014-03-01 12:29:24 -06:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *sysData) show() error {
|
|
|
|
if classTypes[s.ctype].show == nil { return nil }
|
|
|
|
ret := make(chan struct{})
|
|
|
|
defer close(ret)
|
|
|
|
uitask <- func() {
|
|
|
|
classTypes[s.ctype].show(s.id)
|
|
|
|
ret <- struct{}{}
|
|
|
|
}
|
|
|
|
<-ret
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *sysData) hide() error {
|
|
|
|
if classTypes[s.ctype].hide == nil { return nil }
|
|
|
|
ret := make(chan struct{})
|
|
|
|
defer close(ret)
|
|
|
|
uitask <- func() {
|
|
|
|
classTypes[s.ctype].hide(s.id)
|
|
|
|
ret <- struct{}{}
|
|
|
|
}
|
|
|
|
<-ret
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *sysData) setText(text string) error {
|
|
|
|
var zero C.SEL
|
|
|
|
if classTypes[s.ctype].settextsel == zero { return nil }
|
|
|
|
ret := make(chan struct{})
|
|
|
|
defer close(ret)
|
|
|
|
uitask <- func() {
|
|
|
|
C.objc_msgSend_id(s.id, classTypes[s.ctype].settextsel, toNSString(text))
|
|
|
|
ret <- struct{}{}
|
|
|
|
}
|
|
|
|
<-ret
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *sysData) setRect(x int, y int, width int, height int) error {
|
|
|
|
if classTypes[s.ctype].make == nil { return nil }
|
2014-03-01 12:53:29 -06:00
|
|
|
objc_msgSend_rect(s.id, _setFrame, x, y, width, height)
|
2014-03-01 12:29:24 -06:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *sysData) isChecked() bool {
|
2014-03-01 12:53:29 -06:00
|
|
|
if classTypes[s.ctype].make == nil { return false }
|
2014-03-01 12:29:24 -06:00
|
|
|
const (
|
|
|
|
NSOnState = 1
|
|
|
|
)
|
|
|
|
|
|
|
|
ret := make(chan bool)
|
|
|
|
defer close(ret)
|
|
|
|
uitask <- func() {
|
|
|
|
k := C.objc_msgSend_noargs(s.id, _state)
|
|
|
|
ret <- uintptr(unsafe.Pointer(k)) == NSOnState
|
|
|
|
}
|
|
|
|
return <-ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *sysData) text() string {
|
|
|
|
var zero C.SEL
|
2014-03-01 12:53:29 -06:00
|
|
|
if classTypes[s.ctype].textsel == zero { return "" }
|
2014-03-01 12:29:24 -06:00
|
|
|
ret := make(chan string)
|
|
|
|
defer close(ret)
|
|
|
|
uitask <- func() {
|
|
|
|
str := C.objc_msgSend_noargs(s.id, classTypes[s.ctype].textsel)
|
|
|
|
ret <- fromNSString(str)
|
|
|
|
}
|
|
|
|
return <-ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *sysData) append(what string) error {
|
|
|
|
// TODO
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *sysData) insertBefore(what string, before int) error {
|
|
|
|
// TODO
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *sysData) selectedIndex() int {
|
|
|
|
// TODO
|
2014-03-01 12:53:29 -06:00
|
|
|
return -1
|
2014-03-01 12:29:24 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *sysData) selectedIndices() []int {
|
|
|
|
// TODO
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *sysData) selectedTexts() []string {
|
|
|
|
// TODO
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-03-01 12:53:29 -06:00
|
|
|
func (s *sysData) setWindowSize(width int, height int) error {
|
2014-03-01 12:29:24 -06:00
|
|
|
ret := make(chan struct{})
|
|
|
|
defer close(ret)
|
|
|
|
uitask <- func() {
|
2014-03-01 12:53:29 -06:00
|
|
|
// we need to get the top left point
|
2014-03-01 12:58:38 -06:00
|
|
|
r := C.objc_msgSend_stret_rect_noargs(s.id, _frame)
|
2014-03-01 12:29:24 -06:00
|
|
|
objc_msgSend_rect_bool(s.id, _setFrameDisplay,
|
2014-03-01 12:58:38 -06:00
|
|
|
int(r.x), int(r.y), width, height,
|
2014-03-01 12:29:24 -06:00
|
|
|
C.BOOL(C.YES)) // TODO set to NO to prevent subviews from being redrawn before they are resized?
|
|
|
|
ret <- struct{}{}
|
|
|
|
}
|
|
|
|
<-ret
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *sysData) delete(index int) error {
|
|
|
|
// TODO
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *sysData) setProgress(percent int) {
|
|
|
|
// TODO
|
|
|
|
}
|