2014-03-01 12:29:24 -06:00
// 1 march 2014
2014-03-12 20:55:45 -05:00
2014-03-01 12:29:24 -06:00
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-30 10:19:13 -05:00
getinside func ( scrollview C . id ) 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
2014-03-02 15:46:27 -06:00
alttextsel C . SEL
2014-03-02 16:19:25 -06:00
append func ( id C . id , what string , alternate bool )
2014-03-02 16:44:13 -06:00
insertBefore func ( id C . id , what string , before int , alternate bool )
2014-03-02 17:38:45 -06:00
selIndex func ( id C . id ) int
2014-03-02 22:11:29 -06:00
selIndices func ( id C . id ) [ ] int
selTexts func ( id C . id ) [ ] string
2014-03-02 16:44:13 -06:00
delete func ( id C . id , index int )
2014-03-08 16:25:19 -06:00
len func ( id C . id ) int
2014-03-01 12:29:24 -06:00
}
var (
_NSWindow = objc_getClass ( "NSWindow" )
2014-03-01 20:34:37 -06:00
_NSButton = objc_getClass ( "NSButton" )
2014-03-02 16:19:25 -06:00
_NSPopUpButton = objc_getClass ( "NSPopUpButton" )
_NSComboBox = objc_getClass ( "NSComboBox" )
2014-03-02 18:01:34 -06:00
_NSTextField = objc_getClass ( "NSTextField" )
_NSSecureTextField = objc_getClass ( "NSSecureTextField" )
2014-03-03 14:32:54 -06:00
_NSProgressIndicator = objc_getClass ( "NSProgressIndicator" )
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-04-04 14:07:43 -05:00
_setDelegate = sel_getUid ( "setDelegate:" )
2014-04-03 17:33:37 -05:00
_setAcceptsMouseMovedEvents = sel_getUid ( "setAcceptsMouseMovedEvents:" )
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" )
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-02 16:19:25 -06:00
_initWithFramePullsDown = sel_getUid ( "initWithFrame:pullsDown:" )
_setUsesDataSource = sel_getUid ( "setUsesDataSource:" )
_addItemWithTitle = sel_getUid ( "addItemWithTitle:" )
_insertItemWithTitleAtIndex = sel_getUid ( "insertItemWithTitle:atIndex:" )
_removeItemAtIndex = sel_getUid ( "removeItemAtIndex:" )
_titleOfSelectedItem = sel_getUid ( "titleOfSelectedItem" )
_indexOfSelectedItem = sel_getUid ( "indexOfSelectedItem" )
_addItemWithObjectValue = sel_getUid ( "addItemWithObjectValue:" )
_insertItemWithObjectValueAtIndex = sel_getUid ( "insertItemWithObjectValue:atIndex:" )
2014-03-02 18:56:54 -06:00
_setEditable = sel_getUid ( "setEditable:" )
_setBordered = sel_getUid ( "setBordered:" )
_setDrawsBackground = sel_getUid ( "setDrawsBackground:" )
2014-03-03 14:32:54 -06:00
_setStyle = sel_getUid ( "setStyle:" )
_setControlSize = sel_getUid ( "setControlSize:" )
_setIndeterminate = sel_getUid ( "setIndeterminate:" )
_setDoubleValue = sel_getUid ( "setDoubleValue:" )
2014-03-08 16:25:19 -06:00
_numberOfItems = sel_getUid ( "numberOfItems" )
2014-03-01 12:29:24 -06:00
)
2014-03-03 14:52:39 -06:00
func addControl ( parentWindow C . id , control C . id ) {
windowView := C . objc_msgSend_noargs ( parentWindow , _contentView )
C . objc_msgSend_id ( windowView , _addSubview , control )
}
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-04-04 14:07:43 -05:00
C . objc_msgSend_id ( win , _setDelegate , appDelegate )
2014-04-03 17:33:37 -05:00
// this is needed for Areas in the window to receive mouse move events
2014-04-04 13:52:38 -05:00
// C.objc_msgSend_bool(win, _setAcceptsMouseMovedEvents, C.BOOL(C.YES))
2014-03-01 16:15:26 -06:00
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 )
2014-04-04 13:52:38 -05:00
// by default the button uses the wrong text size
// TODO do this for all controls
C . objc_setFont ( button , 0 ) // NSRegularControlSize
2014-03-03 14:52:39 -06:00
addControl ( parentWindow , button )
2014-03-01 20:34:37 -06:00
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
2014-03-03 14:52:39 -06:00
addControl ( parentWindow , checkbox )
2014-03-02 08:17:42 -06:00
return checkbox
} ,
show : controlShow ,
hide : controlHide ,
settextsel : _setTitle ,
textsel : _title ,
2014-03-01 12:29:24 -06:00
} ,
c_combobox : & classData {
2014-03-02 16:19:25 -06:00
make : func ( parentWindow C . id , alternate bool ) C . id {
var combobox C . id
if alternate {
combobox = objc_alloc ( _NSComboBox )
combobox = objc_msgSend_rect ( combobox , _initWithFrame ,
0 , 0 , 100 , 100 )
C . objc_msgSend_bool ( combobox , _setUsesDataSource , C . BOOL ( C . NO ) )
} else {
combobox = objc_alloc ( _NSPopUpButton )
combobox = objc_msgSend_rect_bool ( combobox , _initWithFramePullsDown ,
0 , 0 , 100 , 100 ,
C . BOOL ( C . NO ) )
}
2014-03-03 14:52:39 -06:00
addControl ( parentWindow , combobox )
2014-03-02 16:19:25 -06:00
return combobox
} ,
show : controlShow ,
hide : controlHide ,
textsel : _titleOfSelectedItem ,
alttextsel : _stringValue ,
append : func ( id C . id , what string , alternate bool ) {
str := toNSString ( what )
if alternate {
C . objc_msgSend_id ( id , _addItemWithObjectValue , str )
} else {
C . objc_msgSend_id ( id , _addItemWithTitle , str )
}
} ,
2014-03-02 16:44:13 -06:00
insertBefore : func ( id C . id , what string , before int , alternate bool ) {
str := toNSString ( what )
if alternate {
C . objc_msgSend_id_int ( id , _insertItemWithObjectValueAtIndex , str , C . intptr_t ( before ) )
} else {
C . objc_msgSend_id_int ( id , _insertItemWithTitleAtIndex , str , C . intptr_t ( before ) )
}
} ,
2014-03-02 17:38:45 -06:00
selIndex : func ( id C . id ) int {
return int ( C . objc_msgSend_intret_noargs ( id , _indexOfSelectedItem ) )
} ,
2014-03-02 16:44:13 -06:00
delete : func ( id C . id , index int ) {
C . objc_msgSend_int ( id , _removeItemAtIndex , C . intptr_t ( index ) )
} ,
2014-03-08 16:25:19 -06:00
len : func ( id C . id ) int {
return int ( C . objc_msgSend_intret_noargs ( id , _numberOfItems ) )
} ,
2014-03-01 12:29:24 -06:00
} ,
c_lineedit : & classData {
2014-03-02 18:01:34 -06:00
make : func ( parentWindow C . id , alternate bool ) C . id {
var lineedit C . id
if alternate {
lineedit = objc_alloc ( _NSSecureTextField )
} else {
lineedit = objc_alloc ( _NSTextField )
}
lineedit = objc_msgSend_rect ( lineedit , _initWithFrame ,
0 , 0 , 100 , 100 )
2014-03-03 14:52:39 -06:00
addControl ( parentWindow , lineedit )
2014-03-02 18:01:34 -06:00
return lineedit
} ,
show : controlShow ,
hide : controlHide ,
settextsel : _setStringValue ,
textsel : _stringValue ,
alttextsel : _stringValue ,
2014-03-01 12:29:24 -06:00
} ,
c_label : & classData {
2014-03-02 18:56:54 -06:00
make : func ( parentWindow C . id , alternate bool ) C . id {
label := objc_alloc ( _NSTextField )
label = objc_msgSend_rect ( label , _initWithFrame ,
0 , 0 , 100 , 100 )
C . objc_msgSend_bool ( label , _setEditable , C . BOOL ( C . NO ) )
C . objc_msgSend_bool ( label , _setBordered , C . BOOL ( C . NO ) )
C . objc_msgSend_bool ( label , _setDrawsBackground , C . BOOL ( C . NO ) )
// TODO others?
2014-03-03 14:52:39 -06:00
addControl ( parentWindow , label )
2014-03-02 18:56:54 -06:00
return label
} ,
show : controlShow ,
hide : controlHide ,
settextsel : _setStringValue ,
textsel : _stringValue ,
2014-03-01 12:29:24 -06:00
} ,
c_listbox : & classData {
2014-03-02 22:11:29 -06:00
make : makeListbox ,
show : controlShow ,
hide : controlHide ,
append : appendListbox ,
insertBefore : insertListboxBefore ,
selIndices : selectedListboxIndices ,
selTexts : selectedListboxTexts ,
delete : deleteListbox ,
2014-03-08 16:25:19 -06:00
len : listboxLen ,
2014-03-01 12:29:24 -06:00
} ,
c_progressbar : & classData {
2014-03-03 14:32:54 -06:00
make : func ( parentWindow C . id , alternate bool ) C . id {
pbar := objc_alloc ( _NSProgressIndicator )
pbar = objc_msgSend_rect ( pbar , _initWithFrame ,
0 , 0 , 100 , 100 )
// TODO really int?
C . objc_msgSend_int ( pbar , _setStyle , 0 ) // NSProgressIndicatorBarStyle
objc_msgSend_uint ( pbar , _setControlSize , 0 ) // NSRegularControlSize
C . objc_msgSend_bool ( pbar , _setIndeterminate , C . BOOL ( C . NO ) )
2014-03-03 14:52:39 -06:00
addControl ( parentWindow , pbar )
2014-03-03 14:32:54 -06:00
return pbar
} ,
show : controlShow ,
hide : controlHide ,
2014-03-01 12:29:24 -06:00
} ,
2014-03-30 10:19:13 -05:00
c_area : & classData {
make : makeArea ,
getinside : areaInScrollView ,
show : controlShow ,
hide : controlHide ,
} ,
2014-03-01 12:29:24 -06:00
}
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-04-01 16:05:16 -05:00
func ( s * sysData ) make ( window * sysData ) error {
2014-03-01 12:29:24 -06:00
var parentWindow C . id
ct := classTypes [ s . ctype ]
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
2014-03-30 10:19:13 -05:00
if ct . getinside != nil {
uitask <- func ( ) {
ret <- ct . getinside ( s . id )
}
addSysData ( <- ret , s )
} else {
addSysData ( s . id , s )
}
2014-03-01 12:29:24 -06:00
return nil
}
2014-03-09 20:40:14 -05:00
// used for Windows; nothing special needed elsewhere
func ( s * sysData ) firstShow ( ) error {
s . show ( )
return nil
}
2014-03-09 20:56:17 -05:00
func ( s * sysData ) show ( ) {
2014-03-01 12:29:24 -06:00
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
classTypes [ s . ctype ] . show ( s . id )
ret <- struct { } { }
}
<- ret
}
2014-03-09 20:56:17 -05:00
func ( s * sysData ) hide ( ) {
2014-03-01 12:29:24 -06:00
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
classTypes [ s . ctype ] . hide ( s . id )
ret <- struct { } { }
}
<- ret
}
2014-03-10 09:39:08 -05:00
func ( s * sysData ) setText ( text string ) {
2014-03-01 12:29:24 -06:00
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
C . objc_msgSend_id ( s . id , classTypes [ s . ctype ] . settextsel , toNSString ( text ) )
ret <- struct { } { }
}
<- ret
}
2014-03-03 16:44:03 -06:00
func ( s * sysData ) setRect ( x int , y int , width int , height int , winheight int ) error {
// winheight - y because (0,0) is the bottom-left corner of the window and not the top-left corner
// (winheight - y) - height because (x, y) is the bottom-left corner of the control and not the top-left
objc_msgSend_rect ( s . id , _setFrame , x , ( winheight - y ) - height , width , height )
2014-03-01 12:29:24 -06:00
return nil
}
func ( s * sysData ) isChecked ( ) bool {
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 {
2014-03-02 15:46:27 -06:00
sel := classTypes [ s . ctype ] . textsel
if s . alternate {
sel = classTypes [ s . ctype ] . alttextsel
}
2014-03-01 12:29:24 -06:00
ret := make ( chan string )
defer close ( ret )
uitask <- func ( ) {
2014-03-02 15:46:27 -06:00
str := C . objc_msgSend_noargs ( s . id , sel )
2014-03-01 12:29:24 -06:00
ret <- fromNSString ( str )
}
return <- ret
}
2014-03-09 15:02:17 -05:00
func ( s * sysData ) append ( what string ) {
2014-03-02 16:19:25 -06:00
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
classTypes [ s . ctype ] . append ( s . id , what , s . alternate )
ret <- struct { } { }
}
<- ret
2014-03-01 12:29:24 -06:00
}
2014-03-09 15:02:17 -05:00
func ( s * sysData ) insertBefore ( what string , before int ) {
2014-03-02 16:44:13 -06:00
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
classTypes [ s . ctype ] . insertBefore ( s . id , what , before , s . alternate )
ret <- struct { } { }
}
<- ret
2014-03-01 12:29:24 -06:00
}
func ( s * sysData ) selectedIndex ( ) int {
2014-03-02 17:38:45 -06:00
ret := make ( chan int )
defer close ( ret )
uitask <- func ( ) {
ret <- classTypes [ s . ctype ] . selIndex ( s . id )
}
return <- ret
2014-03-01 12:29:24 -06:00
}
func ( s * sysData ) selectedIndices ( ) [ ] int {
2014-03-02 22:36:46 -06:00
ret := make ( chan [ ] int )
defer close ( ret )
uitask <- func ( ) {
ret <- classTypes [ s . ctype ] . selIndices ( s . id )
}
return <- ret
2014-03-01 12:29:24 -06:00
}
func ( s * sysData ) selectedTexts ( ) [ ] string {
2014-03-02 22:36:46 -06:00
ret := make ( chan [ ] string )
defer close ( ret )
uitask <- func ( ) {
ret <- classTypes [ s . ctype ] . selTexts ( s . id )
}
return <- ret
2014-03-01 12:29:24 -06:00
}
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
}
2014-03-11 12:50:02 -05:00
func ( s * sysData ) delete ( index int ) {
2014-03-02 16:44:13 -06:00
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
classTypes [ s . ctype ] . delete ( s . id , index )
ret <- struct { } { }
}
<- ret
2014-03-01 12:29:24 -06:00
}
func ( s * sysData ) setProgress ( percent int ) {
2014-03-03 14:32:54 -06:00
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
2014-03-12 18:40:01 -05:00
if percent == - 1 {
2014-04-04 16:59:23 -05:00
// At least on Mac OS X 10.8, if the progressbar was already on 0 or 100% when turning on indeterminate mode, the indeterminate animation won't play, leaving just a still progress bar. This is a workaround. Note the selector call order.
C . objc_msgSend_double ( s . id , _setDoubleValue , C . double ( 50 ) )
2014-03-12 18:40:01 -05:00
C . objc_msgSend_bool ( s . id , _setIndeterminate , C . BOOL ( C . YES ) )
} else {
C . objc_msgSend_bool ( s . id , _setIndeterminate , C . BOOL ( C . NO ) )
C . objc_msgSend_double ( s . id , _setDoubleValue , C . double ( percent ) )
}
2014-03-03 14:32:54 -06:00
ret <- struct { } { }
}
<- ret
2014-03-01 12:29:24 -06:00
}
2014-03-08 16:25:19 -06:00
func ( s * sysData ) len ( ) int {
ret := make ( chan int )
defer close ( ret )
uitask <- func ( ) {
ret <- classTypes [ s . ctype ] . len ( s . id )
}
return <- ret
}
2014-03-30 10:19:13 -05:00
func ( s * sysData ) setAreaSize ( width int , height int ) {
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
area := areaInScrollView ( s . id )
2014-03-30 12:25:41 -05:00
objc_msgSend_rect ( area , _setFrame ,
int ( 0 ) , int ( 0 ) , width , height )
C . objc_msgSend_noargs ( area , _display ) // and redraw
2014-03-30 10:19:13 -05:00
ret <- struct { } { }
}
<- ret
}