164 lines
4.3 KiB
Go
164 lines
4.3 KiB
Go
// 15 july 2014
|
|
|
|
package ui
|
|
|
|
import (
|
|
"fmt"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
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,
|
|
// c_CW_USEDEFAULT, c_CW_USEDEFAULT,
|
|
100,100,
|
|
// 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,
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
type button struct {
|
|
*widgetbase
|
|
clicked *event
|
|
}
|
|
|
|
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})
|
|
b := &button{
|
|
widgetbase: w,
|
|
clicked: newEvent(),
|
|
}
|
|
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
|
|
},
|
|
resp: c,
|
|
}
|
|
}
|
|
|
|
func (b *button) OnClicked(e func(c Doer)) *Request {
|
|
c := make(chan interface{})
|
|
return &Request{
|
|
op: func() {
|
|
b.clicked.set(e)
|
|
c <- struct{}{}
|
|
},
|
|
resp: c,
|
|
}
|
|
}
|
|
|
|
func (b *button) Text() *Request {
|
|
return b.text()
|
|
}
|
|
|
|
func (b *button) SetText(text string) *Request {
|
|
return b.settext(text)
|
|
}
|
|
|
|
var buttonsubprocptr uintptr
|
|
|
|
// to avoid recursive initialization loop
|
|
func init() {
|
|
buttonsubprocptr = syscall.NewCallback(buttonSubProc)
|
|
}
|
|
|
|
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
|
|
}
|
|
return f_DefSubclassProc(hwnd, uMsg, wParam, lParam)
|
|
case c_WM_NCDESTROY:
|
|
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)
|
|
default:
|
|
return f_DefSubclassProc(hwnd, uMsg, wParam, lParam)
|
|
}
|
|
panic(fmt.Errorf("Button message %d does not return a value (bug in buttonSubProc())", uMsg))
|
|
}
|