diff --git a/control.go b/control.go index ad91e9b..73c9953 100644 --- a/control.go +++ b/control.go @@ -94,3 +94,9 @@ func LibuiControlEnable(c uintptr) { func LibuiControlDisable(c uintptr) { C.uiControlDisable(touiControl(c)) } + +// LibuiFreeText allows implementations of Control +// to call the libui function uiFreeText. +func LibuiFreeText(c uintptr) { + C.uiFreeText((*C.char)(unsafe.Pointer(c))) +} diff --git a/main.go b/main.go index a5b4269..c7cb51a 100644 --- a/main.go +++ b/main.go @@ -57,8 +57,9 @@ func start(errchan chan error, f func()) { errchan <- nil } -// Quit queues an exit from the GUI thread. It does not exit the -// program. Quit must be called from the GUI thread. +// Quit queues a return from Main. It does not exit the program. +// It also does not immediately cause Main to return; Main will +// return when it next can. Quit must be called from the GUI thread. func Quit() { C.uiQuit() } diff --git a/window.go b/window.go new file mode 100644 index 0000000..53147f2 --- /dev/null +++ b/window.go @@ -0,0 +1,152 @@ +// 12 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +// extern int doOnClosing(uiWindow *, void *); +// static inline void realuiWindowOnClosing(uiWindow *w) +// { +// uiWindowOnClosing(w, doOnClosing, NULL); +// } +import "C" + +// no need to lock this; only the GUI thread can access it +var windows = make(map[*C.uiWindow]*Window) + +// Window is a Control that represents a top-level window. +// A Window contains one child Control that occupies the +// entirety of the window. Though a Window is a Control, +// a Window cannot be the child of another Control. +type Window struct { + c *C.uiControl + w *C.uiWindow + + child Control + + onClosing func(w *Window) bool +} + +// NewWindow creates a new Window. +func NewWindow(title string, width int, height int, hasMenubar bool) *Window { + w := new(Window) + + ctitle := C.CString(title) + // TODO wait why did I make these ints and not intmax_ts? + w.w = C.uiNewWindow(ctitle, C.int(width), C.int(height), frombool(hasMenubar)) + w.c = (*C.uiControl)(unsafe.Pointer(w.w)) + freestr(ctitle) + + C.realuiWindowOnClosing(w.w) + windows[w.w] = w + + return w +} + +// Destroy destroys the Window. If the Window has a child, +// Destroy calls Destroy on that as well. +func (w *Window) Destroy() { + // first hide ourselves + w.Hide() + // get rid of the child + if w.child != nil { + c := w.child + w.SetChild(nil) + c.Destroy() + } + // unregister events + delete(windows, w.w) + // and finally destroy ourselves + C.uiControlDestroy(w.c) +} + +// Handle returns the OS-level handle associated with this Window. +// On Windows this is an HWND of a libui-internal class. +// On GTK+ this is a pointer to a GtkWindow. +// On OS X this is a pointer to a NSWindow. +func (w *Window) Handle() uintptr { + return uintptr(C.uiControlHandle(w.c)) +} + +// Show shows the Window. It uses the OS conception of "presenting" +// the Window, whatever that may be on a given OS. +func (w *Window) Show() { + C.uiControlShow(w.c) +} + +// Hide hides the Window. +func (w *Window) Hide() { + C.uiControlHide(w.c) +} + +// Enable enables the Window. +func (w *Window) Enable() { + C.uiControlEnable(w.c) +} + +// Disable disables the Window. +func (w *Window) Disable() { + C.uiControlDisable(w.c) +} + +// Title returns the Window's title. +func (w *Window) Title() string { + ctitle := C.uiWindowTitle(w.w) + title := C.GoString(ctitle) + C.uiFreeText(ctitle) + return title +} + +// SetTitle sets the Window's title to title. +func (w *Window) SetTitle(title string) { + ctitle := C.CString(title) + C.uiWindowSetTitle(w.w, ctitle) + freestr(ctitle) +} + +// OnClosing registers f to be run when the user clicks the Window's +// close button. Only one function can be registered at a time. +// If f returns true, the window is destroyed with the Destroy method. +// If f returns false, or if OnClosing is never called, the window is not +// destroyed and is kept visible. +func (w *Window) OnClosing(f func(*Window) bool) { + w.onClosing = f +} + +//export doOnClosing +func doOnClosing(ww *C.uiWindow, data unsafe.Pointer) C.int { + w := windows[ww] + if w.onClosing == nil { + return 0 + } + if w.onClosing(w) { + w.Destroy() + } + return 0 +} + +// SetChild sets the Window's child to child. If child is nil, the Window +// will not have a child. +func (w *Window) SetChild(child Control) { + w.child = child + c := (*C.uiControl)(nil) + if w.child != nil { + c = touiControl(w.child.Handle()) + } + C.uiWindowSetChild(w.w, c) +} + +// Margined returns whether the Window has margins around its child. +func (w *Window) Margined() bool { + return tobool(C.uiWindowMargined(w.w)) +} + +// SetMargined controls whether the Window has margins around its +// child. The size of the margins are determined by the OS and its +// best practices. +func (w *Window) SetMargined(margined bool) { + C.uiWindowSetMargined(w.w, frombool(margined)) +} diff --git a/zz_test.go b/zz_test.go index f84e5b7..92ce522 100644 --- a/zz_test.go +++ b/zz_test.go @@ -6,8 +6,12 @@ import "testing" func TestIt(t *testing.T) { err := Main(func() { - t.Log("we're here") - Quit() + w := NewWindow("Hello", 320, 240, false) + w.OnClosing(func(w *Window) bool { + Quit() + return true + }) + w.Show() }) if err != nil { t.Fatal(err)