2014-07-15 21:29:45 -05:00
// 15 july 2014
package ui
import (
"fmt"
"syscall"
2014-07-17 14:10:26 -05:00
"unsafe"
2014-07-15 21:29:45 -05:00
)
type widgetbase struct {
hwnd uintptr
}
var emptystr = syscall . StringToUTF16Ptr ( "" )
func newWidget ( class * uint16 , style uintptr , extstyle uintptr ) * widgetbase {
hwnd , err := f_CreateWindowExW (
extstyle ,
class , emptystr ,
style | c_WS_CHILD | c_WS_VISIBLE ,
c_CW_USEDEFAULT , c_CW_USEDEFAULT ,
2014-07-15 21:44:27 -05:00
// c_CW_USEDEFAULT, c_CW_USEDEFAULT,
100 , 100 ,
2014-07-15 21:29:45 -05:00
// the following has the consequence of making the control message-only at first
// this shouldn't cause any problems... hopefully not
// but see the msgwndproc() for caveat info
// also don't use low control IDs as they will conflict with dialog boxes (IDCANCEL, etc.)
msgwin , 100 , hInstance , nil )
if hwnd == hNULL {
panic ( fmt . Errorf ( "creating control of class %q failed: %v" , class , err ) )
}
return & widgetbase {
hwnd : hwnd ,
}
}
// these few methods are embedded by all the various Controls since they all will do the same thing
func ( w * widgetbase ) unparent ( ) {
res , err := f_SetParent ( w . hwnd , msgwin )
if res == hNULL { // result type is HWND
panic ( fmt . Errorf ( "error unparenting control: %v" , err ) )
}
}
func ( w * widgetbase ) parent ( win * window ) {
res , err := f_SetParent ( w . hwnd , win . hwnd )
if res == hNULL { // result type is HWND
panic ( fmt . Errorf ( "error parenting control: %v" , err ) )
}
}
// don't embed these as exported; let each Control decide if it should
func ( w * widgetbase ) text ( ) * Request {
c := make ( chan interface { } )
return & Request {
op : func ( ) {
c <- getWindowText ( w . hwnd )
} ,
resp : c ,
}
}
func ( w * widgetbase ) settext ( text string , results ... t_LRESULT ) * Request {
c := make ( chan interface { } )
return & Request {
op : func ( ) {
setWindowText ( w . hwnd , text , append ( [ ] t_LRESULT { c_FALSE } , results ... ) )
c <- struct { } { }
} ,
resp : c ,
}
}
2014-07-17 14:10:26 -05:00
// all controls that have events receive the events themselves through subclasses
// to do this, all windows (including the message-only window; see http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q104069) forward WM_COMMAND to each control with this function
func forwardCommand ( hwnd uintptr , uMsg t_UINT , wParam t_WPARAM , lParam t_LPARAM ) t_LRESULT {
control := uintptr ( lParam )
// don't generate an event if the control (if there is one) is unparented (a child of the message-only window)
if control != hNULL && f_IsChild ( msgwin , control ) == 0 {
return f_SendMessageW ( control , msgCOMMAND , wParam , lParam )
}
return f_DefWindowProcW ( hwnd , uMsg , wParam , lParam )
}
2014-07-15 21:29:45 -05:00
type button struct {
* widgetbase
2014-07-17 14:10:26 -05:00
clicked * event
2014-07-15 21:29:45 -05:00
}
var buttonclass = syscall . StringToUTF16Ptr ( "BUTTON" )
func newButton ( text string ) * Request {
c := make ( chan interface { } )
return & Request {
op : func ( ) {
w := newWidget ( buttonclass ,
c_BS_PUSHBUTTON | c_WS_TABSTOP ,
0 )
setWindowText ( w . hwnd , text , [ ] t_LRESULT { c_FALSE } )
2014-07-17 17:36:44 -05:00
b := & button {
2014-07-15 21:29:45 -05:00
widgetbase : w ,
2014-07-17 14:10:26 -05:00
clicked : newEvent ( ) ,
2014-07-15 21:29:45 -05:00
}
2014-07-17 17:36:44 -05:00
res , err := f_SetWindowSubclass ( w . hwnd , buttonsubprocptr , 0 , t_DWORD_PTR ( uintptr ( unsafe . Pointer ( b ) ) ) )
if res == c_FALSE {
panic ( fmt . Errorf ( "error subclassing Button to give it its own event handler: %v" , err ) )
}
c <- b
2014-07-15 21:29:45 -05:00
} ,
resp : c ,
}
}
func ( b * button ) OnClicked ( e func ( c Doer ) ) * Request {
2014-07-17 14:10:26 -05:00
c := make ( chan interface { } )
return & Request {
op : func ( ) {
b . clicked . set ( e )
c <- struct { } { }
} ,
resp : c ,
}
2014-07-15 21:29:45 -05:00
}
func ( b * button ) Text ( ) * Request {
return b . text ( )
}
func ( b * button ) SetText ( text string ) * Request {
return b . settext ( text )
}
2014-07-17 14:10:26 -05:00
2014-07-17 17:36:44 -05:00
var buttonsubprocptr uintptr
// to avoid recursive initialization loop
func init ( ) {
buttonsubprocptr = syscall . NewCallback ( buttonSubProc )
}
2014-07-17 14:10:26 -05:00
func buttonSubProc ( hwnd uintptr , uMsg t_UINT , wParam t_WPARAM , lParam t_LPARAM , id t_UINT_PTR , data t_DWORD_PTR ) t_LRESULT {
b := ( * button ) ( unsafe . Pointer ( uintptr ( data ) ) )
switch uMsg {
case msgCOMMAND :
if wParam . HIWORD ( ) == c_BN_CLICKED {
b . clicked . fire ( )
println ( "button clicked" )
return 0
}
2014-07-17 17:36:44 -05:00
return f_DefSubclassProc ( hwnd , uMsg , wParam , lParam )
2014-07-17 14:10:26 -05:00
case c_WM_NCDESTROY :
2014-07-17 17:36:44 -05:00
res , err := f_RemoveWindowSubclass ( b . hwnd , buttonsubprocptr , id )
if res == c_FALSE {
panic ( fmt . Errorf ( "error removing Button subclass (which was for its own event handler): %v" , err ) )
}
return f_DefSubclassProc ( hwnd , uMsg , wParam , lParam )
2014-07-17 14:10:26 -05:00
default :
2014-07-17 17:36:44 -05:00
return f_DefSubclassProc ( hwnd , uMsg , wParam , lParam )
2014-07-17 14:10:26 -05:00
}
panic ( fmt . Errorf ( "Button message %d does not return a value (bug in buttonSubProc())" , uMsg ) )
}