diff --git a/newctrl/button_windows.go b/newctrl/button_windows.go new file mode 100644 index 0000000..d7cfff8 --- /dev/null +++ b/newctrl/button_windows.go @@ -0,0 +1,73 @@ +// 15 july 2014 + +package ui + +import ( + "unsafe" +) + +// #include "winapi_windows.h" +import "C" + +type button struct { + *controlSingleHWNDWithText + clicked *event +} + +var buttonclass = toUTF16("BUTTON") + +func newButton(text string) *button { + hwnd := C.newControl(buttonclass, + C.BS_PUSHBUTTON|C.WS_TABSTOP, + 0) + b := &button{ + controlSingleHWNDWithText: newControlSingleHWNDWithText(hwnd), + clicked: newEvent(), + } + b.fpreferredSize = b.preferredSize + b.SetText(text) + C.controlSetControlFont(b.hwnd) + C.setButtonSubclass(b.hwnd, unsafe.Pointer(b)) + return b +} + +func (b *button) OnClicked(e func()) { + b.clicked.set(e) +} + +func (b *button) Text() string { + return b.text() +} + +func (b *button) SetText(text string) { + b.setText(text) +} + +//export buttonClicked +func buttonClicked(data unsafe.Pointer) { + b := (*button)(data) + b.clicked.fire() +} + +const ( + // from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing + buttonHeight = 14 +) + +func (b *button) preferredSize(d *sizing) (width, height int) { +return 0,0/*TODO/* + // comctl32.dll version 6 thankfully provides a method to grab this... + var size C.SIZE + + size.cx = 0 // explicitly ask for ideal size + size.cy = 0 + if C.SendMessageW(b.hwnd, C.BCM_GETIDEALSIZE, 0, C.LPARAM(uintptr(unsafe.Pointer(&size)))) != C.FALSE { + return int(size.cx), int(size.cy) + } + // that failed, fall back + println("message failed; falling back") + // don't worry about the error return from GetSystemMetrics(); there's no way to tell (explicitly documented as such) + xmargins := 2 * int(C.GetSystemMetrics(C.SM_CXEDGE)) + return xmargins + int(b.textlen), fromdlgunitsY(buttonHeight, d) +*/ +} diff --git a/newctrl/control.go b/newctrl/control.go new file mode 100644 index 0000000..028b195 --- /dev/null +++ b/newctrl/control.go @@ -0,0 +1,29 @@ +// 30 july 2014 + +package ui + +// Control represents a control. +type Control interface { + setParent(p *controlParent) // controlParent defined per-platform +// nChildren() int // TODO + preferredSize(d *sizing) (width, height int) + resize(x int, y int, width int, height int, d *sizing) +} + +type controlbase struct { + fsetParent func(p *controlParent) + fpreferredSize func(d *sizing) (width, height int) + fresize func(x int, y int, width int, height int, d *sizing) +} + +func (c *controlbase) setParent(p *controlParent) { + c.fsetParent(p) +} + +func (c *controlbase) preferredSize(d *sizing) (width, height int) { + return c.fpreferredSize(d) +} + +func (c *controlbase) resize(x int, y int, width int, height int, d *sizing) { + c.fresize(x, y, width, height, d) +} diff --git a/newctrl/control_windows.go b/newctrl/control_windows.go new file mode 100644 index 0000000..106727b --- /dev/null +++ b/newctrl/control_windows.go @@ -0,0 +1,61 @@ +// 30 july 2014 + +package ui + +// #include "winapi_windows.h" +import "C" + +type controlParent struct { + hwnd C.HWND +} +type sizing struct { + // TODO +} + +// don't specify preferredSize in any of these; they're per-control + +type controlSingleHWND struct { + *controlbase + hwnd C.HWND +} + +func newControlSingleHWND(hwnd C.HWND) *controlSingleHWND { + c := new(controlSingleHWND) + c.controlbase = &controlbase{ + fsetParent: c.setParent, + fresize: c.resize, + } + c.hwnd = hwnd + return c +} + +func (c *controlSingleHWND) setParent(p *controlParent) { + C.controlSetParent(c.hwnd, p.hwnd) +} + +func (c *controlSingleHWND) resize(x int, y int, width int, height int, d *sizing) { + C.moveWindow(c.hwnd, C.int(x), C.int(y), C.int(width), C.int(height)) +} + +// these are provided for convenience + +type controlSingleHWNDWithText struct { + *controlSingleHWND + textlen C.LONG +} + +func newControlSingleHWNDWithText(h C.HWND) *controlSingleHWNDWithText { + return &controlSingleHWNDWithText{ + controlSingleHWND: newControlSingleHWND(h), + } +} + +func (c *controlSingleHWNDWithText) text() string { + return getWindowText(c.hwnd) +} + +func (c *controlSingleHWNDWithText) setText(text string) { + t := toUTF16(text) + C.setWindowText(c.hwnd, t) + c.textlen = C.controlTextLength(c.hwnd, t) +} diff --git a/newctrl/window_windows.go b/newctrl/window_windows.go new file mode 100644 index 0000000..4dc5454 --- /dev/null +++ b/newctrl/window_windows.go @@ -0,0 +1,101 @@ +// 12 july 2014 + +package ui + +import ( + "fmt" + "syscall" + "unsafe" +) + +// #include "winapi_windows.h" +import "C" + +type window struct { + hwnd C.HWND + shownbefore bool + + closing *event + + child Control +} + +func makeWindowWindowClass() error { + var errmsg *C.char + + err := C.makeWindowWindowClass(&errmsg) + if err != 0 || errmsg != nil { + return fmt.Errorf("%s: %v", C.GoString(errmsg), syscall.Errno(err)) + } + return nil +} + +func newWindow(title string, width int, height int, control Control) *window { + w := &window{ + // hwnd set in WM_CREATE handler + closing: newEvent(), + child: control, + } + hwnd := C.newWindow(toUTF16(title), C.int(width), C.int(height), unsafe.Pointer(w)) + if hwnd != w.hwnd { + panic(fmt.Errorf("inconsistency: hwnd returned by CreateWindowEx() (%p) and hwnd stored in Window (%p) differ", hwnd, w.hwnd)) + } + hresult := C.EnableThemeDialogTexture(w.hwnd, C.ETDT_ENABLE|C.ETDT_USETABTEXTURE) + if hresult != C.S_OK { + panic(fmt.Errorf("error setting tab background texture on Window; HRESULT: 0x%X", hresult)) + } + w.child.setParent(&controlParent{w.hwnd}) + return w +} + +func (w *window) Title() string { + return getWindowText(w.hwnd) +} + +func (w *window) SetTitle(title string) { + C.setWindowText(w.hwnd, toUTF16(title)) +} + +func (w *window) Show() { + if !w.shownbefore { + C.ShowWindow(w.hwnd, C.nCmdShow) + C.updateWindow(w.hwnd) + w.shownbefore = true + } else { + C.ShowWindow(w.hwnd, C.SW_SHOW) + } +} + +func (w *window) Hide() { + C.ShowWindow(w.hwnd, C.SW_HIDE) +} + +func (w *window) Close() { + C.windowClose(w.hwnd) +} + +func (w *window) OnClosing(e func() bool) { + w.closing.setbool(e) +} + +//export storeWindowHWND +func storeWindowHWND(data unsafe.Pointer, hwnd C.HWND) { + w := (*window)(data) + w.hwnd = hwnd +} + +//export windowResize +func windowResize(data unsafe.Pointer, r *C.RECT) { + w := (*window)(data) + TODO := &sizing{} + w.child.resize(int(r.left), int (r.top), int(r.right - r.left), int(r.bottom - r.top), TODO) +} + +//export windowClosing +func windowClosing(data unsafe.Pointer) { + w := (*window)(data) + close := w.closing.fire() + if close { + C.windowClose(w.hwnd) + } +} diff --git a/newctrl/zz_test.go b/newctrl/zz_test.go new file mode 100644 index 0000000..a1537fd --- /dev/null +++ b/newctrl/zz_test.go @@ -0,0 +1,36 @@ +// 14 october 2014 + +package ui + +import "flag" +import "testing" + +var twindow *window + +func maketw(done chan struct{}) { + button := newButton("Greet") + twindow = newWindow("Hello", 200, 100, button) + twindow.OnClosing(func() bool { + Stop() + return true + }) + twindow.Show() +} + +// because Cocoa hates being run off the main thread, even if it's run exclusively off the main thread +func init() { + flag.Parse() + go func() { + done := make(chan struct{}) + Do(func() { maketw(done) }) + <-done + }() + err := Go() + if err != nil { + panic(err) + } +} + +func TestDummy(t *testing.T) { + // do nothing +}