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 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"
2014-05-12 14:15:26 -05:00
// #include "sysdata_darwin.h"
2014-03-01 12:29:24 -06:00
import "C"
type sysData struct {
cSysData
2014-05-10 13:59:11 -05:00
id C . id
trackingArea C . id // for Area
2014-03-01 12:29:24 -06:00
}
type classData struct {
2014-05-10 13:59:11 -05:00
make func ( parentWindow C . id , alternate bool , s * sysData ) 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-04-12 20:49:41 -05:00
selectIndex func ( id C . id , index int , alternate bool )
2014-04-12 21:05:34 -05:00
selectIndices func ( id C . id , indices [ ] 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-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-04-13 19:16:05 -05:00
_setContentSize = sel_getUid ( "setContentSize:" )
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-04-13 17:05:07 -05:00
_cell = sel_getUid ( "cell" )
_setLineBreakMode = sel_getUid ( "setLineBreakMode:" )
2014-03-03 14:32:54 -06:00
_setStyle = sel_getUid ( "setStyle:" )
_setControlSize = sel_getUid ( "setControlSize:" )
_setIndeterminate = sel_getUid ( "setIndeterminate:" )
2014-05-11 20:31:20 -05:00
_startAnimation = sel_getUid ( "startAnimation:" )
_stopAnimation = sel_getUid ( "stopAnimation:" )
2014-03-03 14:32:54 -06:00
_setDoubleValue = sel_getUid ( "setDoubleValue:" )
2014-03-08 16:25:19 -06:00
_numberOfItems = sel_getUid ( "numberOfItems" )
2014-04-12 20:49:41 -05:00
_selectItemAtIndex = sel_getUid ( "selectItemAtIndex:" )
_deselectItemAtIndex = sel_getUid ( "deselectItemAtIndex:" )
2014-03-01 12:29:24 -06:00
)
2014-04-04 18:51:23 -05:00
// because the only way to make a new NSControl/NSView is with a frame (it gets overridden later)
func initWithDummyFrame ( self C . id ) C . id {
return C . objc_msgSend_rect ( self , _initWithFrame ,
C . int64_t ( 0 ) , C . int64_t ( 0 ) , C . int64_t ( 100 ) , C . int64_t ( 100 ) )
}
2014-03-03 14:52:39 -06:00
func addControl ( parentWindow C . id , control C . id ) {
2014-05-12 14:15:26 -05:00
C . addControl ( parentWindow , control )
2014-03-03 14:52:39 -06:00
}
2014-03-01 12:29:24 -06:00
func controlShow ( what C . id ) {
2014-05-12 14:15:26 -05:00
C . controlShow ( what )
2014-03-01 12:29:24 -06:00
}
func controlHide ( what C . id ) {
2014-05-12 14:15:26 -05:00
C . controlHide ( what )
2014-03-01 12:29:24 -06:00
}
2014-04-05 12:59:11 -05:00
const (
_NSRegularControlSize = 0
)
2014-04-05 14:10:02 -05:00
// By default some controls do not use the correct font.
// These functions set the appropriate control font.
// Which one is used on each control was determined by comparing https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/AppleHIGuidelines/Characteristics/Characteristics.html#//apple_ref/doc/uid/TP40002721-SW10 to what Interface Builder says for each control.
// (not applicable to ProgressBar, Area)
// Button, Checkbox, Combobox, LineEdit, Label, Listbox
func applyStandardControlFont ( id C . id ) {
2014-05-12 14:15:26 -05:00
C . applyStandardControlFont ( id )
2014-04-05 14:10:02 -05:00
}
2014-03-01 12:29:24 -06:00
var classTypes = [ nctypes ] * classData {
c_window : & classData {
2014-05-10 13:59:11 -05:00
make : func ( parentWindow C . id , alternate bool , s * sysData ) C . id {
2014-05-12 14:15:26 -05:00
win := C . makeWindow ( )
2014-04-04 14:07:43 -05:00
C . objc_msgSend_id ( win , _setDelegate , appDelegate )
2014-05-10 20:03:04 -05:00
// we do not need setAcceptsMouseMovedEvents: here since we are using a tracking rect in Areas for that
2014-03-01 16:15:26 -06:00
return win
2014-03-01 12:29:24 -06:00
} ,
show : func ( what C . id ) {
2014-05-12 14:15:26 -05:00
C . windowShow ( what )
2014-03-01 12:29:24 -06:00
} ,
hide : func ( what C . id ) {
2014-05-12 14:15:26 -05:00
C . windowHide ( what )
2014-03-01 12:29:24 -06:00
} ,
settextsel : _setTitle ,
textsel : _title ,
} ,
c_button : & classData {
2014-05-10 13:59:11 -05:00
make : func ( parentWindow C . id , alternate bool , s * sysData ) C . id {
2014-05-12 18:34:13 -05:00
button := C . makeButton ( )
C . buttonSetTargetAction ( button , appDelegate )
2014-04-05 14:10:02 -05:00
applyStandardControlFont ( button )
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-05-10 13:59:11 -05:00
make : func ( parentWindow C . id , alternate bool , s * sysData ) C . id {
2014-05-12 18:34:13 -05:00
checkbox := C . makeCheckbox ( )
2014-04-05 14:10:02 -05:00
applyStandardControlFont ( checkbox )
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-05-10 13:59:11 -05:00
make : func ( parentWindow C . id , alternate bool , s * sysData ) C . id {
2014-03-02 16:19:25 -06:00
var combobox C . id
if alternate {
2014-05-12 18:34:13 -05:00
combobox = C . makeCombobox ( C . YES )
2014-03-02 16:19:25 -06:00
} else {
2014-05-12 18:34:13 -05:00
combobox = C . makeCombobox ( C . NO )
2014-03-02 16:19:25 -06:00
}
2014-04-05 14:10:02 -05:00
applyStandardControlFont ( combobox )
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 {
2014-05-12 18:34:13 -05:00
C . comboboxAppend ( id , C . YES , str )
2014-03-02 16:19:25 -06:00
} else {
2014-05-12 18:34:13 -05:00
C . comboboxAppend ( id , C . NO , str )
2014-03-02 16:19:25 -06:00
}
} ,
2014-03-02 16:44:13 -06:00
insertBefore : func ( id C . id , what string , before int , alternate bool ) {
str := toNSString ( what )
if alternate {
2014-05-12 18:34:13 -05:00
C . comboboxInsertBefore ( id , C . YES , str , C . intptr_t ( before ) )
2014-03-02 16:44:13 -06:00
} else {
2014-05-12 18:34:13 -05:00
C . comboboxInsertBefore ( id , C . NO , str , C . intptr_t ( before ) )
2014-03-02 16:44:13 -06:00
}
} ,
2014-03-02 17:38:45 -06:00
selIndex : func ( id C . id ) int {
2014-05-12 18:34:13 -05:00
return int ( C . comboboxSelectedIndex ( id ) )
2014-03-02 17:38:45 -06:00
} ,
2014-03-02 16:44:13 -06:00
delete : func ( id C . id , index int ) {
2014-05-12 18:34:13 -05:00
C . comboboxDelete ( id , C . intptr_t ( index ) )
2014-03-02 16:44:13 -06:00
} ,
2014-03-08 16:25:19 -06:00
len : func ( id C . id ) int {
2014-05-12 18:34:13 -05:00
return int ( C . comboboxLen ( id ) )
2014-03-08 16:25:19 -06:00
} ,
2014-04-12 20:49:41 -05:00
selectIndex : func ( id C . id , index int , alternate bool ) {
// NSPopUpButton makes this easy
2014-05-12 18:34:13 -05:00
if alternate {
C . comboboxSelectIndex ( id , C . YES , C . intptr_t ( index ) )
} else {
C . comboboxSelectIndex ( id , C . NO , C . intptr_t ( index ) )
2014-04-12 20:49:41 -05:00
}
} ,
2014-03-01 12:29:24 -06:00
} ,
c_lineedit : & classData {
2014-05-10 13:59:11 -05:00
make : func ( parentWindow C . id , alternate bool , s * sysData ) C . id {
2014-03-02 18:01:34 -06:00
var lineedit C . id
if alternate {
2014-05-12 18:34:13 -05:00
lineedit = C . makeLineEdit ( C . YES )
2014-03-02 18:01:34 -06:00
} else {
2014-05-12 18:34:13 -05:00
lineedit = C . makeLineEdit ( C . NO )
2014-03-02 18:01:34 -06:00
}
2014-04-05 14:10:02 -05:00
applyStandardControlFont ( lineedit )
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-05-10 13:59:11 -05:00
make : func ( parentWindow C . id , alternate bool , s * sysData ) C . id {
2014-05-12 18:34:13 -05:00
label := C . makeLabel ( )
2014-04-05 14:10:02 -05:00
applyStandardControlFont ( label )
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-04-12 21:05:34 -05:00
selectIndices : selectListboxIndices ,
2014-03-01 12:29:24 -06:00
} ,
c_progressbar : & classData {
2014-05-10 13:59:11 -05:00
make : func ( parentWindow C . id , alternate bool , s * sysData ) C . id {
2014-05-12 18:34:13 -05:00
pbar := C . makeProgressBar ( )
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-05-10 13:59:11 -05:00
ret <- ct . make ( parentWindow , s . alternate , s )
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
2014-05-12 18:34:13 -05:00
C . setRect ( s . id ,
C . intptr_t ( x ) , C . intptr_t ( ( winheight - y ) - height ) ,
C . intptr_t ( width ) , C . intptr_t ( height ) )
2014-03-01 12:29:24 -06:00
return nil
}
func ( s * sysData ) isChecked ( ) bool {
ret := make ( chan bool )
defer close ( ret )
uitask <- func ( ) {
2014-05-12 18:34:13 -05:00
ret <- C . isCheckboxChecked ( s . id ) != C . NO
2014-03-01 12:29:24 -06:00
}
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-05-12 18:34:13 -05:00
C . windowSetContentSize ( s . id , C . intptr_t ( width ) , C . intptr_t ( height ) )
2014-03-01 12:29:24 -06:00
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-05-12 18:34:13 -05:00
C . setProgress ( s . id , C . intptr_t ( 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 ( ) {
2014-05-12 18:34:13 -05:00
C . setAreaSize ( s . id , C . intptr_t ( width ) , C . intptr_t ( height ) )
2014-03-30 10:19:13 -05:00
ret <- struct { } { }
}
<- ret
}
2014-04-12 20:49:41 -05:00
func ( s * sysData ) selectIndex ( index int ) {
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
classTypes [ s . ctype ] . selectIndex ( s . id , index , s . alternate )
ret <- struct { } { }
}
<- ret
}
2014-04-12 21:05:34 -05:00
func ( s * sysData ) selectIndices ( indices [ ] int ) {
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
classTypes [ s . ctype ] . selectIndices ( s . id , indices )
ret <- struct { } { }
}
<- ret
}