diff options
Diffstat (limited to 'AAA_GOFILES')
31 files changed, 3805 insertions, 0 deletions
diff --git a/AAA_GOFILES/area.go b/AAA_GOFILES/area.go new file mode 100644 index 0000000..358a391 --- /dev/null +++ b/AAA_GOFILES/area.go @@ -0,0 +1,156 @@ +// 16 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +import "C" + +// no need to lock this; only the GUI thread can access it +var areas = make(map[*C.uiArea]*Area) + +// Area is a Control that represents a blank canvas that a program +// can draw on as it wishes. Areas also receive keyboard and mouse +// events, and programs can react to those as they see fit. Drawing +// and event handling are handled through an instance of a type +// that implements AreaHandler that every Area has; see AreaHandler +// for details. +// +// There are two types of areas. Non-scrolling areas are rectangular +// and have no scrollbars. Programs can draw on and get mouse +// events from any point in the Area, and the size of the Area is +// decided by package ui itself, according to the layout of controls +// in the Window the Area is located in and the size of said Window. +// There is no way to query the Area's size or be notified when its +// size changes; instead, you are given the area size as part of the +// draw and mouse event handlers, for use solely within those +// handlers. +// +// Scrolling areas have horziontal and vertical scrollbars. The amount +// that can be scrolled is determined by the area's size, which is +// decided by the programmer (both when creating the Area and by +// a call to SetSize). Only a portion of the Area is visible at any time; +// drawing and mouse events are automatically adjusted to match +// what portion is visible, so you do not have to worry about scrolling +// in your event handlers. AreaHandler has more information. +// +// The internal coordinate system of an Area is points, which are +// floating-point and device-independent. For more details, see +// AreaHandler. The size of a scrolling Area must be an exact integer +// number of points (that is, you cannot have an Area that is 32.5 +// points tall) and thus the parameters to NewScrollingArea and +// SetSize are ints. All other instances of points in parameters and +// structures (including sizes of drawn objects) are float64s. +type Area struct { + c *C.uiControl + a *C.uiArea + + ah *C.uiAreaHandler + + scrolling bool +} + +// NewArea creates a new non-scrolling Area. +func NewArea(handler AreaHandler) *Area { + a := new(Area) + a.scrolling = false + a.ah = registerAreaHandler(handler) + + a.a = C.uiNewArea(a.ah) + a.c = (*C.uiControl)(unsafe.Pointer(a.a)) + + areas[a.a] = a + + return a +} + +// NewScrollingArea creates a new scrolling Area of the given size, +// in points. +func NewScrollingArea(handler AreaHandler, width int, height int) *Area { + a := new(Area) + a.scrolling = true + a.ah = registerAreaHandler(handler) + + a.a = C.uiNewScrollingArea(a.ah, C.intmax_t(width), C.intmax_t(height)) + a.c = (*C.uiControl)(unsafe.Pointer(a.a)) + + areas[a.a] = a + + return a +} + +// Destroy destroys the Area. +func (a *Area) Destroy() { + delete(areas, a.a) + C.uiControlDestroy(a.c) + unregisterAreaHandler(a.ah) +} + +// LibuiControl returns the libui uiControl pointer that backs +// the Area. This is only used by package ui itself and should +// not be called by programs. +func (a *Area) LibuiControl() uintptr { + return uintptr(unsafe.Pointer(a.c)) +} + +// Handle returns the OS-level handle associated with this Area. +// On Windows this is an HWND of a libui-internal class. +// On GTK+ this is a pointer to a GtkScrolledWindow with a +// GtkViewport as its child. The child of the viewport is the +// GtkDrawingArea that provides the Area itself. +// On OS X this is a pointer to a NSScrollView whose document view +// is the NSView that provides the Area itself. +func (a *Area) Handle() uintptr { + return uintptr(C.uiControlHandle(a.c)) +} + +// Show shows the Area. +func (a *Area) Show() { + C.uiControlShow(a.c) +} + +// Hide hides the Area. +func (a *Area) Hide() { + C.uiControlHide(a.c) +} + +// Enable enables the Area. +func (a *Area) Enable() { + C.uiControlEnable(a.c) +} + +// Disable disables the Area. +func (a *Area) Disable() { + C.uiControlDisable(a.c) +} + +// SetSize sets the size of a scrolling Area to the given size, in points. +// SetSize panics if called on a non-scrolling Area. +func (a *Area) SetSize(width int, height int) { + if !a.scrolling { + panic("attempt to call SetSize on non-scrolling Area") + } + C.uiAreaSetSize(a.a, C.intmax_t(width), C.intmax_t(height)) +} + +// QueueRedrawAll queues the entire Area for redraw. +// The Area is not redrawn before this function returns; it is +// redrawn when next possible. +func (a *Area) QueueRedrawAll() { + C.uiAreaQueueRedrawAll(a.a) +} + +// ScrollTo scrolls the Area to show the given rectangle; what this +// means is implementation-defined, but you can safely assume +// that as much of the given rectangle as possible will be visible +// after this call. (TODO verify this on OS X) ScrollTo panics if called +// on a non-scrolling Area. +func (a *Area) ScrollTo(x float64, y float64, width float64, height float64) { + if !a.scrolling { + panic("attempt to call ScrollTo on non-scrolling Area") + } + C.uiAreaScrollTo(a.a, C.double(x), C.double(y), C.double(width), C.double(height)) +} diff --git a/AAA_GOFILES/areahandler.go b/AAA_GOFILES/areahandler.go new file mode 100644 index 0000000..74cbe56 --- /dev/null +++ b/AAA_GOFILES/areahandler.go @@ -0,0 +1,321 @@ +// 13 december 2015 + +package ui + +// #include <stdlib.h> +// #include "ui.h" +// extern void doAreaHandlerDraw(uiAreaHandler *, uiArea *, uiAreaDrawParams *); +// extern void doAreaHandlerMouseEvent(uiAreaHandler *, uiArea *, uiAreaMouseEvent *); +// extern void doAreaHandlerMouseCrossed(uiAreaHandler *, uiArea *, int); +// extern void doAreaHandlerDragBroken(uiAreaHandler *, uiArea *); +// extern int doAreaHandlerKeyEvent(uiAreaHandler *, uiArea *, uiAreaKeyEvent *); +// static inline uiAreaHandler *allocAreaHandler(void) +// { +// uiAreaHandler *ah; +// +// ah = (uiAreaHandler *) malloc(sizeof (uiAreaHandler)); +// if (ah == NULL) // TODO +// return NULL; +// ah->Draw = doAreaHandlerDraw; +// ah->MouseEvent = doAreaHandlerMouseEvent; +// ah->MouseCrossed = doAreaHandlerMouseCrossed; +// ah->DragBroken = doAreaHandlerDragBroken; +// ah->KeyEvent = doAreaHandlerKeyEvent; +// return ah; +// } +// static inline void freeAreaHandler(uiAreaHandler *ah) +// { +// free(ah); +// } +import "C" + +// no need to lock this; only the GUI thread can access it +var areahandlers = make(map[*C.uiAreaHandler]AreaHandler) + +// AreaHandler defines the functionality needed for handling events +// from an Area. Each of the methods on AreaHandler is called from +// the GUI thread, and every parameter (other than the Area itself) +// should be assumed to only be valid during the life of the method +// call (so for instance, do not save AreaDrawParams.AreaWidth, as +// that might change without generating an event). +// +// Coordinates to Draw and MouseEvent are given in points. Points +// are generic, floating-point, device-independent coordinates with +// (0,0) at the top left corner. You never have to worry about the +// mapping between points and pixels; simply draw everything using +// points and you get nice effects like looking sharp on high-DPI +// monitors for free. Proper documentation on the matter is being +// written. In the meantime, there are several referenes to this kind of +// drawing, most notably on Apple's website: https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html#//apple_ref/doc/uid/TP40012302-CH4-SW1 +// +// For a scrolling Area, points are automatically offset by the scroll +// position. So if the mouse moves to position (5,5) while the +// horizontal scrollbar is at position 10 and the horizontal scrollbar is +// at position 20, the coordinate stored in the AreaMouseEvent +// structure is (15,25). The same applies to drawing. +type AreaHandler interface { + // Draw is sent when a part of the Area needs to be drawn. + // dp will contain a drawing context to draw on, the rectangle + // that needs to be drawn in, and (for a non-scrolling area) the + // size of the area. The rectangle that needs to be drawn will + // have been cleared by the system prior to drawing, so you are + // always working on a clean slate. + // + // If you call Save on the drawing context, you must call Release + // before returning from Draw, and the number of calls to Save + // and Release must match. Failure to do so results in undefined + // behavior. + Draw(a *Area, dp *AreaDrawParams) + + // MouseEvent is called when the mouse moves over the Area + // or when a mouse button is pressed or released. See + // AreaMouseEvent for more details. + // + // If a mouse button is being held, MouseEvents will continue to + // be generated, even if the mouse is not within the area. On + // some systems, the system can interrupt this behavior; + // see DragBroken. + MouseEvent(a *Area, me *AreaMouseEvent) + + // MouseCrossed is called when the mouse either enters or + // leaves the Area. It is called even if the mouse buttons are being + // held (see MouseEvent above). If the mouse has entered the + // Area, left is false; if it has left the Area, left is true. + // + // If, when the Area is first shown, the mouse is already inside + // the Area, MouseCrossed will be called with left=false. + // TODO what about future shows? + MouseCrossed(a *Area, left bool) + + // DragBroken is called if a mouse drag is interrupted by the + // system. As noted above, when a mouse button is held, + // MouseEvent will continue to be called, even if the mouse is + // outside the Area. On some systems, this behavior can be + // stopped by the system itself for a variety of reasons. This + // method is provided to allow your program to cope with the + // loss of the mouse in this case. You should cope by cancelling + // whatever drag-related operation you were doing. + // + // Note that this is only generated on some systems under + // specific conditions. Do not implement behavior that only + // takes effect when DragBroken is called. + DragBroken(a *Area) + + // KeyEvent is called when a key is pressed while the Area has + // keyboard focus (if the Area has been tabbed into or if the + // mouse has been clicked on it). See AreaKeyEvent for specifics. + // + // Because some keyboard events are handled by the system + // (for instance, menu accelerators and global hotkeys), you + // must return whether you handled the key event; return true + // if you did or false if you did not. If you wish to ignore the + // keyboard outright, the correct implementation of KeyEvent is + // func (h *MyHandler) KeyEvent(a *ui.Area, ke *ui.AreaKeyEvent) (handled bool) { + // return false + // } + // DO NOT RETURN TRUE UNCONDITIONALLY FROM THIS + // METHOD. BAD THINGS WILL HAPPEN IF YOU DO. + KeyEvent(a *Area, ke *AreaKeyEvent) (handled bool) +} + +func registerAreaHandler(ah AreaHandler) *C.uiAreaHandler { + uah := C.allocAreaHandler() + areahandlers[uah] = ah + return uah +} + +func unregisterAreaHandler(uah *C.uiAreaHandler) { + delete(areahandlers, uah) + C.freeAreaHandler(uah) +} + +// AreaDrawParams provides a drawing context that can be used +// to draw on an Area and tells you where to draw. See AreaHandler +// for introductory information. +type AreaDrawParams struct { + // Context is the drawing context to draw on. See DrawContext + // for how to draw. + Context *DrawContext + + // AreaWidth and AreaHeight provide the size of the Area for + // non-scrolling Areas. For scrolling Areas both values are zero. + // + // To reiterate the AreaHandler documentation, do NOT save + // these values for later; they can change without generating + // an event. + AreaWidth float64 + AreaHeight float64 + + // These four fields define the rectangle that needs to be + // redrawn. The system will not draw anything outside this + // rectangle, but you can make your drawing faster if you + // also stay within the lines. + ClipX float64 + ClipY float64 + ClipWidth float64 + ClipHeight float64 +} + +//export doAreaHandlerDraw +func doAreaHandlerDraw(uah *C.uiAreaHandler, ua *C.uiArea, udp *C.uiAreaDrawParams) { + ah := areahandlers[uah] + a := areas[ua] + dp := &AreaDrawParams{ + Context: &DrawContext{udp.Context}, + AreaWidth: float64(udp.AreaWidth), + AreaHeight: float64(udp.AreaHeight), + ClipX: float64(udp.ClipX), + ClipY: float64(udp.ClipY), + ClipWidth: float64(udp.ClipWidth), + ClipHeight: float64(udp.ClipHeight), + } + ah.Draw(a, dp) +} + +// TODO document all these +// +// TODO note that in the case of a drag, X and Y can be out of bounds, or in the event of a scrolling area, in places that are not visible +type AreaMouseEvent struct { + X float64 + Y float64 + + // AreaWidth and AreaHeight provide the size of the Area for + // non-scrolling Areas. For scrolling Areas both values are zero. + // + // To reiterate the AreaHandler documentation, do NOT save + // these values for later; they can change without generating + // an event. + AreaWidth float64 + AreaHeight float64 + + Down uint + Up uint + Count uint + Modifiers Modifiers + Held []uint +} + +func appendBits(out []uint, held C.uint64_t) []uint { + n := uint(1) + for i := 0; i < 64; i++ { + if held & 1 != 0 { + out = append(out, n) + } + held >>= 1 + n++ + } + return out +} + +//export doAreaHandlerMouseEvent +func doAreaHandlerMouseEvent(uah *C.uiAreaHandler, ua *C.uiArea, ume *C.uiAreaMouseEvent) { + ah := areahandlers[uah] + a := areas[ua] + me := &AreaMouseEvent{ + X: float64(ume.X), + Y: float64(ume.Y), + AreaWidth: float64(ume.AreaWidth), + AreaHeight: float64(ume.AreaHeight), + Down: uint(ume.Down), + Up: uint(ume.Up), + Count: uint(ume.Count), + Modifiers: Modifiers(ume.Modifiers), + Held: make([]uint, 0, 64), + } + me.Held = appendBits(me.Held, ume.Held1To64) + ah.MouseEvent(a, me) +} + +//export doAreaHandlerMouseCrossed +func doAreaHandlerMouseCrossed(uah *C.uiAreaHandler, ua *C.uiArea, left C.int) { + ah := areahandlers[uah] + a := areas[ua] + ah.MouseCrossed(a, tobool(left)) +} + +//export doAreaHandlerDragBroken +func doAreaHandlerDragBroken(uah *C.uiAreaHandler, ua *C.uiArea) { + ah := areahandlers[uah] + a := areas[ua] + ah.DragBroken(a) +} + +// TODO document all these +type AreaKeyEvent struct { + Key rune + ExtKey ExtKey + Modifier Modifiers + Modifiers Modifiers + Up bool +} + +//export doAreaHandlerKeyEvent +func doAreaHandlerKeyEvent(uah *C.uiAreaHandler, ua *C.uiArea, uke *C.uiAreaKeyEvent) C.int { + ah := areahandlers[uah] + a := areas[ua] + ke := &AreaKeyEvent{ + Key: rune(uke.Key), + ExtKey: ExtKey(uke.ExtKey), + Modifier: Modifiers(uke.Modifier), + Modifiers: Modifiers(uke.Modifiers), + Up: tobool(uke.Up), + } + return frombool(ah.KeyEvent(a, ke)) +} + +// TODO document +// +// Note: these must be numerically identical to their libui equivalents. +type Modifiers uint +const ( + Ctrl Modifiers = 1 << iota + Alt + Shift + Super +) + +// TODO document +// +// Note: these must be numerically identical to their libui equivalents. +type ExtKey int +const ( + Escape ExtKey = iota + 1 + Insert // equivalent to "Help" on Apple keyboards + Delete + Home + End + PageUp + PageDown + Up + Down + Left + Right + F1 // F1..F12 are guaranteed to be consecutive + F2 + F3 + F4 + F5 + F6 + F7 + F8 + F9 + F10 + F11 + F12 + N0 // numpad keys; independent of Num Lock state + N1 // N0..N9 are guaranteed to be consecutive + N2 + N3 + N4 + N5 + N6 + N7 + N8 + N9 + NDot + NEnter + NAdd + NSubtract + NMultiply + NDivide +) diff --git a/AAA_GOFILES/box.go b/AAA_GOFILES/box.go new file mode 100644 index 0000000..caf49e3 --- /dev/null +++ b/AAA_GOFILES/box.go @@ -0,0 +1,121 @@ +// 12 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +import "C" + +// Box is a Control that holds a group of Controls horizontally +// or vertically. If horizontally, then all controls have the same +// height. If vertically, then all controls have the same width. +// By default, each control has its preferred width (horizontal) +// or height (vertical); if a control is marked "stretchy", it will +// take whatever space is left over. If multiple controls are marked +// stretchy, they will be given equal shares of the leftover space. +// There can also be space between each control ("padding"). +type Box struct { + c *C.uiControl + b *C.uiBox + + children []Control +} + +// NewHorizontalBox creates a new horizontal Box. +func NewHorizontalBox() *Box { + b := new(Box) + + b.b = C.uiNewHorizontalBox() + b.c = (*C.uiControl)(unsafe.Pointer(b.b)) + + return b +} + +// NewVerticalBox creates a new vertical Box. +func NewVerticalBox() *Box { + b := new(Box) + + b.b = C.uiNewVerticalBox() + b.c = (*C.uiControl)(unsafe.Pointer(b.b)) + + return b +} + +// Destroy destroys the Box. If the Box has children, +// Destroy calls Destroy on those Controls as well. +func (b *Box) Destroy() { + for len(b.children) != 0 { + c := b.children[0] + b.Delete(0) + c.Destroy() + } + C.uiControlDestroy(b.c) +} + +// LibuiControl returns the libui uiControl pointer that backs +// the Box. This is only used by package ui itself and should +// not be called by programs. +func (b *Box) LibuiControl() uintptr { + return uintptr(unsafe.Pointer(b.c)) +} + +// Handle returns the OS-level handle associated with this Box. +// On Windows this is an HWND of a libui-internal class. +// On GTK+ this is a pointer to a GtkBox. +// On OS X this is a pointer to a NSView. +func (b *Box) Handle() uintptr { + return uintptr(C.uiControlHandle(b.c)) +} + +// Show shows the Box. +func (b *Box) Show() { + C.uiControlShow(b.c) +} + +// Hide hides the Box. +func (b *Box) Hide() { + C.uiControlHide(b.c) +} + +// Enable enables the Box. +func (b *Box) Enable() { + C.uiControlEnable(b.c) +} + +// Disable disables the Box. +func (b *Box) Disable() { + C.uiControlDisable(b.c) +} + +// Append adds the given control to the end of the Box. +func (b *Box) Append(child Control, stretchy bool) { + c := (*C.uiControl)(nil) + if child != nil { + c = touiControl(child.LibuiControl()) + } + C.uiBoxAppend(b.b, c, frombool(stretchy)) + b.children = append(b.children, child) +} + +// Delete deletes the nth control of the Box. +func (b *Box) Delete(n int) { + b.children = append(b.children[:n], b.children[n + 1:]...) + // TODO why is this uintmax_t instead of intmax_t + C.uiBoxDelete(b.b, C.uintmax_t(n)) +} + +// Padded returns whether there is space between each control +// of the Box. +func (b *Box) Padded() bool { + return tobool(C.uiBoxPadded(b.b)) +} + +// SetPadded controls whether there is space between each control +// of the Box. The size of the padding is determined by the OS and +// its best practices. +func (b *Box) SetPadded(padded bool) { + C.uiBoxSetPadded(b.b, frombool(padded)) +} diff --git a/AAA_GOFILES/button.go b/AAA_GOFILES/button.go new file mode 100644 index 0000000..2696f82 --- /dev/null +++ b/AAA_GOFILES/button.go @@ -0,0 +1,114 @@ +// 12 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +// extern void doButtonOnClicked(uiButton *, void *); +// static inline void realuiButtonOnClicked(uiButton *b) +// { +// uiButtonOnClicked(b, doButtonOnClicked, NULL); +// } +import "C" + +// no need to lock this; only the GUI thread can access it +var buttons = make(map[*C.uiButton]*Button) + +// Button is a Control that represents a button that the user can +// click to perform an action. A Button has a text label that should +// describe what the button does. +type Button struct { + c *C.uiControl + b *C.uiButton + + onClicked func(*Button) +} + +// NewButton creates a new Button with the given text as its label. +func NewButton(text string) *Button { + b := new(Button) + + ctext := C.CString(text) + b.b = C.uiNewButton(ctext) + b.c = (*C.uiControl)(unsafe.Pointer(b.b)) + freestr(ctext) + + C.realuiButtonOnClicked(b.b) + buttons[b.b] = b + + return b +} + +// Destroy destroys the Button. +func (b *Button) Destroy() { + delete(buttons, b.b) + C.uiControlDestroy(b.c) +} + +// LibuiControl returns the libui uiControl pointer that backs +// the Button. This is only used by package ui itself and should +// not be called by programs. +func (b *Button) LibuiControl() uintptr { + return uintptr(unsafe.Pointer(b.c)) +} + +// Handle returns the OS-level handle associated with this Button. +// On Windows this is an HWND of a standard Windows API BUTTON +// class (as provided by Common Controls version 6). +// On GTK+ this is a pointer to a GtkButton. +// On OS X this is a pointer to a NSButton. +func (b *Button) Handle() uintptr { + return uintptr(C.uiControlHandle(b.c)) +} + +// Show shows the Button. +func (b *Button) Show() { + C.uiControlShow(b.c) +} + +// Hide hides the Button. +func (b *Button) Hide() { + C.uiControlHide(b.c) +} + +// Enable enables the Button. +func (b *Button) Enable() { + C.uiControlEnable(b.c) +} + +// Disable disables the Button. +func (b *Button) Disable() { + C.uiControlDisable(b.c) +} + +// Text returns the Button's text. +func (b *Button) Text() string { + ctext := C.uiButtonText(b.b) + text := C.GoString(ctext) + C.uiFreeText(ctext) + return text +} + +// SetText sets the Button's text to text. +func (b *Button) SetText(text string) { + ctext := C.CString(text) + C.uiButtonSetText(b.b, ctext) + freestr(ctext) +} + +// OnClicked registers f to be run when the user clicks the Button. +// Only one function can be registered at a time. +func (b *Button) OnClicked(f func(*Button)) { + b.onClicked = f +} + +//export doButtonOnClicked +func doButtonOnClicked(bb *C.uiButton, data unsafe.Pointer) { + b := buttons[bb] + if b.onClicked != nil { + b.onClicked(b) + } +} diff --git a/AAA_GOFILES/checkbox.go b/AAA_GOFILES/checkbox.go new file mode 100644 index 0000000..b518b29 --- /dev/null +++ b/AAA_GOFILES/checkbox.go @@ -0,0 +1,124 @@ +// 12 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +// extern void doCheckboxOnToggled(uiCheckbox *, void *); +// static inline void realuiCheckboxOnToggled(uiCheckbox *c) +// { +// uiCheckboxOnToggled(c, doCheckboxOnToggled, NULL); +// } +import "C" + +// no need to lock this; only the GUI thread can access it +var checkboxes = make(map[*C.uiCheckbox]*Checkbox) + +// Checkbox is a Control that represents a box with a text label at its +// side. When the user clicks the checkbox, a check mark will appear +// in the box; clicking it again removes the check. +type Checkbox struct { + co *C.uiControl + c *C.uiCheckbox + + onToggled func(*Checkbox) +} + +// NewCheckbox creates a new Checkbox with the given text as its label. +func NewCheckbox(text string) *Checkbox { + c := new(Checkbox) + + ctext := C.CString(text) + c.c = C.uiNewCheckbox(ctext) + c.co = (*C.uiControl)(unsafe.Pointer(c.c)) + freestr(ctext) + + C.realuiCheckboxOnToggled(c.c) + checkboxes[c.c] = c + + return c +} + +// Destroy destroys the Checkbox. +func (c *Checkbox) Destroy() { + delete(checkboxes, c.c) + C.uiControlDestroy(c.co) +} + +// LibuiControl returns the libui uiControl pointer that backs +// the Window. This is only used by package ui itself and should +// not be called by programs. +func (c *Checkbox) LibuiControl() uintptr { + return uintptr(unsafe.Pointer(c.co)) +} + +// Handle returns the OS-level handle associated with this Checkbox. +// On Windows this is an HWND of a standard Windows API BUTTON +// class (as provided by Common Controls version 6). +// On GTK+ this is a pointer to a GtkCheckButton. +// On OS X this is a pointer to a NSButton. +func (c *Checkbox) Handle() uintptr { + return uintptr(C.uiControlHandle(c.co)) +} + +// Show shows the Checkbox. +func (c *Checkbox) Show() { + C.uiControlShow(c.co) +} + +// Hide hides the Checkbox. +func (c *Checkbox) Hide() { + C.uiControlHide(c.co) +} + +// Enable enables the Checkbox. +func (c *Checkbox) Enable() { + C.uiControlEnable(c.co) +} + +// Disable disables the Checkbox. +func (c *Checkbox) Disable() { + C.uiControlDisable(c.co) +} + +// Text returns the Checkbox's text. +func (c *Checkbox) Text() string { + ctext := C.uiCheckboxText(c.c) + text := C.GoString(ctext) + C.uiFreeText(ctext) + return text +} + +// SetText sets the Checkbox's text to text. +func (c *Checkbox) SetText(text string) { + ctext := C.CString(text) + C.uiCheckboxSetText(c.c, ctext) + freestr(ctext) +} + +// OnToggled registers f to be run when the user clicks the Checkbox. +// Only one function can be registered at a time. +func (c *Checkbox) OnToggled(f func(*Checkbox)) { + c.onToggled = f +} + +//export doCheckboxOnToggled +func doCheckboxOnToggled(cc *C.uiCheckbox, data unsafe.Pointer) { + c := checkboxes[cc] + if c.onToggled != nil { + c.onToggled(c) + } +} + +// Checked returns whether the Checkbox is checked. +func (c *Checkbox) Checked() bool { + return tobool(C.uiCheckboxChecked(c.c)) +} + +// SetChecked sets whether the Checkbox is checked. +func (c *Checkbox) SetChecked(checked bool) { + C.uiCheckboxSetChecked(c.c, frombool(checked)) +} diff --git a/AAA_GOFILES/combobox.go b/AAA_GOFILES/combobox.go new file mode 100644 index 0000000..2afa294 --- /dev/null +++ b/AAA_GOFILES/combobox.go @@ -0,0 +1,132 @@ +// 12 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +// extern void doComboboxOnSelected(uiCombobox *, void *); +// static inline void realuiComboboxOnSelected(uiCombobox *c) +// { +// uiComboboxOnSelected(c, doComboboxOnSelected, NULL); +// } +import "C" + +// no need to lock this; only the GUI thread can access it +var comboboxes = make(map[*C.uiCombobox]*Combobox) + +// Combobox is a Control that represents a drop-down list of strings +// that the user can choose one of at any time. An editable +// Combobox also has an entry field that the user can type an alternate +// choice into. +type Combobox struct { + co *C.uiControl + c *C.uiCombobox + + onSelected func(*Combobox) +} + +// NewCombobox creates a new Combobox. +// This Combobox is not editable. +func NewCombobox() *Combobox { + c := new(Combobox) + + c.c = C.uiNewCombobox() + c.co = (*C.uiControl)(unsafe.Pointer(c.c)) + + C.realuiComboboxOnSelected(c.c) + comboboxes[c.c] = c + + return c +} +/*TODO +// NewEditableCombobox creates a new editable Combobox. +func NewEditableCombobox() *Combobox { + c := new(Combobox) + + c.c = C.uiNewEditableCombobox() + c.co = (*C.uiControl)(unsafe.Pointer(c.c)) + + C.realuiComboboxOnSelected(c.c) + comboboxes[c.c] = c + + return c +} +*/ +// Destroy destroys the Combobox. +func (c *Combobox) Destroy() { + delete(comboboxes, c.c) + C.uiControlDestroy(c.co) +} + +// LibuiControl returns the libui uiControl pointer that backs +// the Window. This is only used by package ui itself and should +// not be called by programs. +func (c *Combobox) LibuiControl() uintptr { + return uintptr(unsafe.Pointer(c.co)) +} + +// Handle returns the OS-level handle associated with this Combobox. +// On Windows this is an HWND of a standard Windows API COMBOBOX +// class (as provided by Common Controls version 6). +// On GTK+ this is a pointer to a GtkComboBoxText. +// On OS X this is a pointer to a NSComboBox for editable Comboboxes +// and to a NSPopUpButton for noneditable Comboboxes. +func (c *Combobox) Handle() uintptr { + return uintptr(C.uiControlHandle(c.co)) +} + +// Show shows the Combobox. +func (c *Combobox) Show() { + C.uiControlShow(c.co) +} + +// Hide hides the Combobox. +func (c *Combobox) Hide() { + C.uiControlHide(c.co) +} + +// Enable enables the Combobox. +func (c *Combobox) Enable() { + C.uiControlEnable(c.co) +} + +// Disable disables the Combobox. +func (c *Combobox) Disable() { + C.uiControlDisable(c.co) +} + +// Append adds the named item to the end of the Combobox. +func (c *Combobox) Append(text string) { + ctext := C.CString(text) + C.uiComboboxAppend(c.c, ctext) + freestr(ctext) +} + +// Selected returns the index of the currently selected item in the +// Combobox, or -1 if nothing is selected. +func (c *Combobox) Selected() int { + return int(C.uiComboboxSelected(c.c)) +} + +// SetChecked sets the currently select item in the Combobox +// to index. If index is -1 no item will be selected. +func (c *Combobox) SetSelected(index int) { + C.uiComboboxSetSelected(c.c, C.intmax_t(index)) +} + +// OnSelected registers f to be run when the user selects an item in +// the Combobox. Only one function can be registered at a time. +func (c *Combobox) OnSelected(f func(*Combobox)) { + c.onSelected = f +} + +//export doComboboxOnSelected +func doComboboxOnSelected(cc *C.uiCombobox, data unsafe.Pointer) { + c := comboboxes[cc] + if c.onSelected != nil { + c.onSelected(c) + } +} diff --git a/AAA_GOFILES/control.go b/AAA_GOFILES/control.go new file mode 100644 index 0000000..94ea34f --- /dev/null +++ b/AAA_GOFILES/control.go @@ -0,0 +1,107 @@ +// 12 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +import "C" + +// Control represents a GUI control. It provdes methods +// common to all Controls. +// +// To create a new Control, implement the control on +// the libui side, then provide access to that control on +// the Go side via an implementation of Control as +// described. +type Control interface { + // Destroy destroys the Control. + // + // Implementations should do any necessary cleanup, + // then call LibuiControlDestroy. + Destroy() + + // LibuiControl returns the libui uiControl pointer that backs + // the Control. This is only used by package ui itself and should + // not be called by programs. + LibuiControl() uintptr + + // Handle returns the OS-level handle that backs the + // Control. On OSs that use reference counting for + // controls, Handle does not increment the reference + // count; you are sharing package ui's reference. + // + // Implementations should call LibuiControlHandle and + // document exactly what kind of handle is returned. + Handle() uintptr + + // Show shows the Control. + // + // Implementations should call LibuiControlShow. + Show() + + // Hide shows the Control. Hidden controls do not participate + // in layout (that is, Box, Grid, etc. does not reserve space for + // hidden controls). + // + // Implementations should call LibuiControlHide. + Hide() + + // Enable enables the Control. + // + // Implementations should call LibuiControlEnable. + Enable() + + // Disable disables the Control. + // + // Implementations should call LibuiControlDisable. + Disable() +} + +func touiControl(c uintptr) *C.uiControl { + return (*C.uiControl)(unsafe.Pointer(c)) +} + +// LibuiControlDestroy allows implementations of Control +// to call the libui function uiControlDestroy. +func LibuiControlDestroy(c uintptr) { + C.uiControlDestroy(touiControl(c)) +} + +// LibuiControlHandle allows implementations of Control +// to call the libui function uiControlHandle. +func LibuiControlHandle(c uintptr) uintptr { + return uintptr(C.uiControlHandle(touiControl(c))) +} + +// LibuiControlShow allows implementations of Control +// to call the libui function uiControlShow. +func LibuiControlShow(c uintptr) { + C.uiControlShow(touiControl(c)) +} + +// LibuiControlHide allows implementations of Control +// to call the libui function uiControlHide. +func LibuiControlHide(c uintptr) { + C.uiControlHide(touiControl(c)) +} + +// LibuiControlEnable allows implementations of Control +// to call the libui function uiControlEnable. +func LibuiControlEnable(c uintptr) { + C.uiControlEnable(touiControl(c)) +} + +// LibuiControlDisable allows implementations of Control +// to call the libui function uiControlDisable. +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/AAA_GOFILES/datetimepicker.go b/AAA_GOFILES/datetimepicker.go new file mode 100644 index 0000000..216b8b7 --- /dev/null +++ b/AAA_GOFILES/datetimepicker.go @@ -0,0 +1,92 @@ +// 12 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +import "C" + +// DateTimePicker is a Control that represents a field where the user +// can enter a date and/or a time. +type DateTimePicker struct { + c *C.uiControl + d *C.uiDateTimePicker +} + +// NewDateTimePicker creates a new DateTimePicker that shows +// both a date and a time. +func NewDateTimePicker() *DateTimePicker { + d := new(DateTimePicker) + + d.d = C.uiNewDateTimePicker() + d.c = (*C.uiControl)(unsafe.Pointer(d.d)) + + return d +} + +// NewDatePicker creates a new DateTimePicker that shows +// only a date. +func NewDatePicker() *DateTimePicker { + d := new(DateTimePicker) + + d.d = C.uiNewDatePicker() + d.c = (*C.uiControl)(unsafe.Pointer(d.d)) + + return d +} + +// NewTimePicker creates a new DateTimePicker that shows +// only a time. +func NewTimePicker() *DateTimePicker { + d := new(DateTimePicker) + + d.d = C.uiNewTimePicker() + d.c = (*C.uiControl)(unsafe.Pointer(d.d)) + + return d +} + +// Destroy destroys the DateTimePicker. +func (d *DateTimePicker) Destroy() { + C.uiControlDestroy(d.c) +} + +// LibuiControl returns the libui uiControl pointer that backs +// the Window. This is only used by package ui itself and should +// not be called by programs. +func (d *DateTimePicker) LibuiControl() uintptr { + return uintptr(unsafe.Pointer(d.c)) +} + +// Handle returns the OS-level handle associated with this DateTimePicker. +// On Windows this is an HWND of a standard Windows API +// DATETIMEPICK_CLASS class (as provided by Common Controls +// version 6). +// On GTK+ this is a pointer to a libui-internal class. +// On OS X this is a pointer to a NSDatePicker. +func (d *DateTimePicker) Handle() uintptr { + return uintptr(C.uiControlHandle(d.c)) +} + +// Show shows the DateTimePicker. +func (d *DateTimePicker) Show() { + C.uiControlShow(d.c) +} + +// Hide hides the DateTimePicker. +func (d *DateTimePicker) Hide() { + C.uiControlHide(d.c) +} + +// Enable enables the DateTimePicker. +func (d *DateTimePicker) Enable() { + C.uiControlEnable(d.c) +} + +// Disable disables the DateTimePicker. +func (d *DateTimePicker) Disable() { + C.uiControlDisable(d.c) +} diff --git a/AAA_GOFILES/draw.go b/AAA_GOFILES/draw.go new file mode 100644 index 0000000..eb4158d --- /dev/null +++ b/AAA_GOFILES/draw.go @@ -0,0 +1,834 @@ +// 13 december 2015 + +package ui + +// #include <stdlib.h> +// #include "ui.h" +// // TODO figure this one out +// extern void *uimalloc(size_t); +// static uiDrawBrush *newBrush(void) +// { +// uiDrawBrush *b; +// +// b = (uiDrawBrush *) uimalloc(sizeof (uiDrawBrush)); +// return b; +// } +// static uiDrawBrushGradientStop *newStops(size_t n) +// { +// uiDrawBrushGradientStop *stops; +// +// stops = (uiDrawBrushGradientStop *) malloc(n * sizeof (uiDrawBrushGradientStop)); +// // TODO +// return stops; +// } +// static void setStop(uiDrawBrushGradientStop *stops, size_t i, double pos, double r, double g, double b, double a) +// { +// stops[i].Pos = pos; +// stops[i].R = r; +// stops[i].G = g; +// stops[i].B = b; +// stops[i].A = a; +// } +// static void freeBrush(uiDrawBrush *b) +// { +// if (b->Type == uiDrawBrushTypeLinearGradient || b->Type == uiDrawBrushTypeRadialGradient) +// free(b->Stops); +// free(b); +// } +// static uiDrawStrokeParams *newStrokeParams(void) +// { +// uiDrawStrokeParams *b; +// +// b = (uiDrawStrokeParams *) malloc(sizeof (uiDrawStrokeParams)); +// // TODO +// return b; +// } +// static double *newDashes(size_t n) +// { +// double *dashes; +// +// dashes = (double *) malloc(n * sizeof (double)); +// // TODO +// return dashes; +// } +// static void setDash(double *dashes, size_t i, double dash) +// { +// dashes[i] = dash; +// } +// static void freeStrokeParams(uiDrawStrokeParams *sp) +// { +// if (sp->Dashes != NULL) +// free(sp->Dashes); +// free(sp); +// } +// static uiDrawMatrix *newMatrix(void) +// { +// uiDrawMatrix *m; +// +// m = (uiDrawMatrix *) malloc(sizeof (uiDrawMatrix)); +// // TODO +// return m; +// } +// static void freeMatrix(uiDrawMatrix *m) +// { +// free(m); +// } +// static uiDrawTextFontDescriptor *newFontDescriptor(void) +// { +// uiDrawTextFontDescriptor *desc; +// +// desc = (uiDrawTextFontDescriptor *) malloc(sizeof (uiDrawTextFontDescriptor)); +// // TODO +// return desc; +// } +// static uiDrawTextFont *newFont(uiDrawTextFontDescriptor *desc) +// { +// uiDrawTextFont *font; +// +// font = uiDrawLoadClosestFont(desc); +// free((char *) (desc->Family)); +// free(desc); +// return font; +// } +// static uiDrawTextLayout *newTextLayout(char *text, uiDrawTextFont *defaultFont, double width) +// { +// uiDrawTextLayout *layout; +// +// layout = uiDrawNewTextLayout(text, defaultFont, width); +// free(text); +// return layout; +// } +// static uiDrawTextFontMetrics *newFontMetrics(void) +// { +// uiDrawTextFontMetrics *m; +// +// m = (uiDrawTextFontMetrics *) malloc(sizeof (uiDrawTextFontMetrics)); +// // TODO +// return m; +// } +// static void freeFontMetrics(uiDrawTextFontMetrics *m) +// { +// free(m); +// } +// static double *newDouble(void) +// { +// double *d; +// +// d = (double *) malloc(sizeof (double)); +// // TODO +// return d; +// } +// static void freeDoubles(double *a, double *b) +// { +// free(a); +// free(b); +// } +import "C" + +// BUG(andlabs): Ideally, all the drawing APIs should be in another package ui/draw (they all have the "uiDraw" prefix in C to achieve a similar goal of avoiding confusing programmers via namespace pollution); managing the linkage of the libui shared library itself across multiple packages is likely going to be a pain, though. (Custom controls implemented using libui won't have this issue, as they *should* only need libui present when linking the shared object, not when linking the Go wrapper. I'm not sure; I'd have to find out first.) + +// Path represents a geometric path in a drawing context. +// This is the basic unit of drawing: all drawing operations consist of +// forming a path, then stroking, filling, or clipping to that path. +// A path is an OS resource; you must explicitly free it when finished. +// Paths consist of multiple figures. Once you have added all the +// figures to a path, you must "end" the path to make it ready to draw +// with. +// TODO rewrite all that +// +// Or more visually, the lifecycle of a Path is +// p := NewPath() +// for every figure { +// p.NewFigure(...) // or NewFigureWithArc +// p.LineTo(...) // any number of these in any order +// p.ArcTo(...) +// p.BezierTo(...) +// if figure should be closed { +// p.CloseFigure() +// } +// } +// p.End() +// // ... +// dp.Context.Stroke(p, ...) // any number of these in any order +// dp.Context.Fill(p, ...) +// dp.Context.Clip(p) +// // ... +// p.Free() // when done with the path +// +// A Path also defines its fill mode. (This should ideally be a fill +// parameter, but some implementations prevent it.) +// TODO talk about fill modes +type Path struct { + p *C.uiDrawPath +} + +// TODO +// +// TODO disclaimer +type FillMode uint +const ( + Winding FillMode = iota + Alternate +) + +// NewPath creates a new Path with the given fill mode. +func NewPath(fillMode FillMode) *Path { + var fm C.uiDrawFillMode + + switch fillMode { + case Winding: + fm = C.uiDrawFillModeWinding + case Alternate: + fm = C.uiDrawFillModeAlternate + default: + panic("invalid fill mode passed to ui.NewPath()") + } + return &Path{ + p: C.uiDrawNewPath(fm), + } +} + +// Free destroys a Path. After calling Free the Path cannot be used. +func (p *Path) Free() { + C.uiDrawFreePath(p.p) +} + +// NewFigure starts a new figure in the Path. The current point +// is set to the given point. +func (p *Path) NewFigure(x float64, y float64) { + C.uiDrawPathNewFigure(p.p, C.double(x), C.double(y)) +} + +// NewFigureWithArc starts a new figure in the Path and adds an arc +// as the first element of the figure. Unlike ArcTo, NewFigureWithArc +// does not draw an initial line segment. Otherwise, see ArcTo. +func (p *Path) NewFigureWithArc(xCenter float64, yCenter float64, radius float64, startAngle float64, sweep float64, isNegative bool) { + C.uiDrawPathNewFigureWithArc(p.p, + C.double(xCenter), C.double(yCenter), + C.double(radius), + C.double(startAngle), C.double(sweep), + frombool(isNegative)) +} + +// LineTo adds a line to the current figure of the Path starting from +// the current point and ending at the given point. The current point +// is set to the ending point. +func (p *Path) LineTo(x float64, y float64) { + C.uiDrawPathLineTo(p.p, C.double(x), C.double(y)) +} + +// ArcTo adds a circular arc to the current figure of the Path. +// You pass it the center of the arc, its radius in radians, the starting +// angle (couterclockwise) in radians, and the number of radians the +// arc should sweep (counterclockwise). A line segment is drawn from +// the current point to the start of the arc. The current point is set to +// the end of the arc. +func (p *Path) ArcTo(xCenter float64, yCenter float64, radius float64, startAngle float64, sweep float64, isNegative bool) { + C.uiDrawPathArcTo(p.p, + C.double(xCenter), C.double(yCenter), + C.double(radius), + C.double(startAngle), C.double(sweep), + frombool(isNegative)) +} + +// BezierTo adds a cubic Bezier curve to the current figure of the Path. +// Its start point is the current point. c1x and c1y are the first control +// point. c2x and c2y are the second control point. endX and endY +// are the end point. The current point is set to the end point. +func (p *Path) BezierTo(c1x float64, c1y float64, c2x float64, c2y float64, endX float64, endY float64) { + C.uiDrawPathBezierTo(p.p, + C.double(c1x), C.double(c1y), + C.double(c2x), C.double(c2y), + C.double(endX), C.double(endY)) +} + +// CloseFigure draws a line segment from the current point of the +// current figure of the Path back to its initial point. After calling this, +// the current figure is over and you must either start a new figure +// or end the Path. If this is not called and you start a new figure or +// end the Path, then the current figure will not have this closing line +// segment added to it (but the figure will still be over). +func (p *Path) CloseFigure() { + C.uiDrawPathCloseFigure(p.p) +} + +// AddRectangle creates a new figure in the Path that consists entirely +// of a rectangle whose top-left corner is at the given point and whose +// size is the given size. The rectangle is a closed figure; you must +// either start a new figure or end the Path after calling this method. +func (p *Path) AddRectangle(x float64, y float64, width float64, height float64) { + C.uiDrawPathAddRectangle(p.p, C.double(x), C.double(y), C.double(width), C.double(height)) +} + +// End ends the current Path. You cannot add figures to a Path that has +// been ended. You cannot draw with a Path that has not been ended. +func (p *Path) End() { + C.uiDrawPathEnd(p.p) +} + +// DrawContext represents a drawing surface that you can draw to. +// At present the only DrawContexts are surfaces associated with +// Areas and are provided by package ui; see AreaDrawParams. +type DrawContext struct { + c *C.uiDrawContext +} + +// BrushType defines the various types of brushes. +// +// TODO disclaimer +type BrushType int +const ( + Solid BrushType = iota + LinearGradient + RadialGradient + Image // presently unimplemented +) + +// TODO +// +// TODO disclaimer +// TODO rename these to put LineCap at the beginning? or just Cap? +type LineCap int +const ( + FlatCap LineCap = iota + RoundCap + SquareCap +) + +// TODO +// +// TODO disclaimer +type LineJoin int +const ( + MiterJoin LineJoin = iota + RoundJoin + BevelJoin +) + +// TODO document +const DefaultMiterLimit = 10.0 + +// TODO +type Brush struct { + Type BrushType + + // If Type is Solid. + // TODO + R float64 + G float64 + B float64 + A float64 + + // If Type is LinearGradient or RadialGradient. + // TODO + X0 float64 // start point for both + Y0 float64 + X1 float64 // linear: end point; radial: circle center + Y1 float64 + OuterRadius float64 // for radial gradients only + Stops []GradientStop +} + +// TODO +type GradientStop struct { + Pos float64 // between 0 and 1 inclusive + R float64 + G float64 + B float64 + A float64 +} + +func (b *Brush) toC() *C.uiDrawBrush { + cb := C.newBrush() + cb.Type = C.uiDrawBrushType(b.Type) + switch b.Type { + case Solid: + cb.R = C.double(b.R) + cb.G = C.double(b.G) + cb.B = C.double(b.B) + cb.A = C.double(b.A) + case LinearGradient, RadialGradient: + cb.X0 = C.double(b.X0) + cb.Y0 = C.double(b.Y0) + cb.X1 = C.double(b.X1) + cb.Y1 = C.double(b.Y1) + cb.OuterRadius = C.double(b.OuterRadius) + cb.NumStops = C.size_t(len(b.Stops)) + cb.Stops = C.newStops(cb.NumStops) + for i, s := range b.Stops { + C.setStop(cb.Stops, C.size_t(i), + C.double(s.Pos), + C.double(s.R), + C.double(s.G), + C.double(s.B), + C.double(s.A)) + } + case Image: + panic("unimplemented") + default: + panic("invalid brush type in Brush.toC()") + } + return cb +} + +// TODO +type StrokeParams struct { + Cap LineCap + Join LineJoin + Thickness float64 + MiterLimit float64 + Dashes []float64 + DashPhase float64 +} + +func (sp *StrokeParams) toC() *C.uiDrawStrokeParams { + csp := C.newStrokeParams() + csp.Cap = C.uiDrawLineCap(sp.Cap) + csp.Join = C.uiDrawLineJoin(sp.Join) + csp.Thickness = C.double(sp.Thickness) + csp.MiterLimit = C.double(sp.MiterLimit) + csp.Dashes = nil + csp.NumDashes = C.size_t(len(sp.Dashes)) + if csp.NumDashes != 0 { + csp.Dashes = C.newDashes(csp.NumDashes) + for i, d := range sp.Dashes { + C.setDash(csp.Dashes, C.size_t(i), C.double(d)) + } + } + csp.DashPhase = C.double(sp.DashPhase) + return csp +} + +// TODO +func (c *DrawContext) Stroke(p *Path, b *Brush, sp *StrokeParams) { + cb := b.toC() + csp := sp.toC() + C.uiDrawStroke(c.c, p.p, cb, csp) + C.freeBrush(cb) + C.freeStrokeParams(csp) +} + +// TODO +func (c *DrawContext) Fill(p *Path, b *Brush) { + cb := b.toC() + C.uiDrawFill(c.c, p.p, cb) + C.freeBrush(cb) +} + +// TODO +// TODO should the methods of these return self for chaining? +type Matrix struct { + M11 float64 + M12 float64 + M21 float64 + M22 float64 + M31 float64 + M32 float64 +} + +// TODO identity matrix +func NewMatrix() *Matrix { + m := new(Matrix) + m.SetIdentity() + return m +} + +// TODO +func (m *Matrix) SetIdentity() { + m.M11 = 1 + m.M12 = 0 + m.M21 = 0 + m.M22 = 1 + m.M31 = 0 + m.M32 = 0 +} + +func (m *Matrix) toC() *C.uiDrawMatrix { + cm := C.newMatrix() + cm.M11 = C.double(m.M11) + cm.M12 = C.double(m.M12) + cm.M21 = C.double(m.M21) + cm.M22 = C.double(m.M22) + cm.M31 = C.double(m.M31) + cm.M32 = C.double(m.M32) + return cm +} + +func (m *Matrix) fromC(cm *C.uiDrawMatrix) { + m.M11 = float64(cm.M11) + m.M12 = float64(cm.M12) + m.M21 = float64(cm.M21) + m.M22 = float64(cm.M22) + m.M31 = float64(cm.M31) + m.M32 = float64(cm.M32) + C.freeMatrix(cm) +} + +// TODO +func (m *Matrix) Translate(x float64, y float64) { + cm := m.toC() + C.uiDrawMatrixTranslate(cm, C.double(x), C.double(y)) + m.fromC(cm) +} + +// TODO +func (m *Matrix) Scale(xCenter float64, yCenter float64, x float64, y float64) { + cm := m.toC() + C.uiDrawMatrixScale(cm, + C.double(xCenter), C.double(yCenter), + C.double(x), C.double(y)) + m.fromC(cm) +} + +// TODO +func (m *Matrix) Rotate(x float64, y float64, amount float64) { + cm := m.toC() + C.uiDrawMatrixRotate(cm, C.double(x), C.double(y), C.double(amount)) + m.fromC(cm) +} + +// TODO +func (m *Matrix) Skew(x float64, y float64, xamount float64, yamount float64) { + cm := m.toC() + C.uiDrawMatrixSkew(cm, + C.double(x), C.double(y), + C.double(xamount), C.double(yamount)) + m.fromC(cm) +} + +// TODO +func (m *Matrix) Multiply(m2 *Matrix) { + cm := m.toC() + cm2 := m2.toC() + C.uiDrawMatrixMultiply(cm, cm2) + C.freeMatrix(cm2) + m.fromC(cm) +} + +// TODO +func (m *Matrix) Invertible() bool { + cm := m.toC() + res := C.uiDrawMatrixInvertible(cm) + C.freeMatrix(cm) + return tobool(res) +} + +// TODO +// +// If m is not invertible, false is returned and m is left unchanged. +func (m *Matrix) Invert() bool { + cm := m.toC() + res := C.uiDrawMatrixInvert(cm) + m.fromC(cm) + return tobool(res) +} + +// TODO unimplemented +func (m *Matrix) TransformPoint(x float64, y float64) (xout float64, yout float64) { + panic("TODO") +} + +// TODO unimplemented +func (m *Matrix) TransformSize(x float64, y float64) (xout float64, yout float64) { + panic("TODO") +} + +// TODO +func (c *DrawContext) Transform(m *Matrix) { + cm := m.toC() + C.uiDrawTransform(c.c, cm) + C.freeMatrix(cm) +} + +// TODO +func (c *DrawContext) Clip(p *Path) { + C.uiDrawClip(c.c, p.p) +} + +// TODO +func (c *DrawContext) Save() { + C.uiDrawSave(c.c) +} + +// TODO +func (c *DrawContext) Restore() { + C.uiDrawRestore(c.c) +} + +// FontFamilies represents an enumerator over the font families +// available for use by package ui. A FontFamilies object behaves +// similarly to a []string, except that since family names are loaded +// on demand (depending on the operating system), it is not an +// actual []string. You call ListFontFamilies to obtain a FontFamilies +// object, which should reflect the available fonts at the time of the +// call (TODO verify). Use NumFamilies to get the number of families, +// and Family to get the name of a given family by index. When +// finished, call Free. +// +// There is no guarantee that the list of families is sorted. You will +// need to do sorting yourself if you need it. +// +// TODO thread affinity +type FontFamilies struct { + ff *C.uiDrawFontFamilies +} + +// ListFontFamilies creates a new FontFamilies object ready for use. +func ListFontFamilies() *FontFamilies { + return &FontFamilies{ + ff: C.uiDrawListFontFamilies(), + } +} + +// Free destroys a FontFamilies. After calling Free, the FontFamilies +// cannot be used. +func (f *FontFamilies) Free() { + C.uiDrawFreeFontFamilies(f.ff) +} + +// NumFamilies returns the number of font families available. +func (f *FontFamilies) NumFamilies() int { + return int(C.uiDrawFontFamiliesNumFamilies(f.ff)) +} + +// Family returns the name of the nth family in the list. +func (f *FontFamilies) Family(n int) string { + cname := C.uiDrawFontFamiliesFamily(f.ff, C.uintmax_t(n)) + name := C.GoString(cname) + C.uiFreeText(cname) + return name +} + +// TextWeight defines the various text weights, in order of +// increasing weight. +// +// Note that if you leave this field unset, it will default to +// TextWeightThin. If you want the normal font weight, explicitly +// use the constant TextWeightNormal instead. +// TODO realign these? +// +// TODO disclaimer +type TextWeight int +const ( + TextWeightThin TextWeight = iota + TextWeightUltraLight + TextWeightLight + TextWeightBook + TextWeightNormal + TextWeightMedium + TextWeightSemiBold + TextWeightBold + TextWeightUtraBold + TextWeightHeavy + TextWeightUltraHeavy +) + +// TextItalic defines the various text italic modes. +// +// TODO disclaimer +type TextItalic int +const ( + TextItalicNormal TextItalic = iota + TextItalicOblique // merely slanted text + TextItalicItalic // true italics +) + +// TextStretch defines the various text stretches, in order of +// increasing wideness. +// +// Note that if you leave this field unset, it will default to +// TextStretchUltraCondensed. If you want the normal font +// stretch, explicitly use the constant TextStretchNormal +// instead. +// TODO realign these? +// +// TODO disclaimer +type TextStretch int +const ( + TextStretchUltraCondensed TextStretch = iota + TextStretchExtraCondensed + TextStretchCondensed + TextStretchSemiCondensed + TextStretchNormal + TextStretchSemiExpanded + TextStretchExpanded + TextStretchExtraExpanded + TextStretchUltraExpanded +) + +// FontDescriptor describes a Font. +type FontDescriptor struct { + Family string + Size float64 // as a text size, for instance 12 for a 12-point font + Weight TextWeight + Italic TextItalic + Stretch TextStretch +} + +// Font represents an actual font that can be drawn with. +type Font struct { + f *C.uiDrawTextFont +} + +// LoadClosestFont loads a Font. +// +// You pass the properties of the ideal font you want to load in the +// FontDescriptor you pass to this function. If the requested font +// is not available on the system, the closest matching font is used. +// This means that, for instance, if you specify a Weight of +// TextWeightUltraHeavy and the heaviest weight available for the +// chosen font family is actually TextWeightBold, that will be used +// instead. The specific details of font matching beyond this +// description are implementation defined. This also means that +// getting a descriptor back out of a Font may return a different +// desriptor. +// +// TODO guarantee that passing *that* back into LoadClosestFont() returns the same font +func LoadClosestFont(desc *FontDescriptor) *Font { + d := C.newFontDescriptor() // both of these are freed by C.newFont() + d.Family = C.CString(desc.Family) + d.Size = C.double(desc.Size) + d.Weight = C.uiDrawTextWeight(desc.Weight) + d.Italic = C.uiDrawTextItalic(desc.Italic) + d.Stretch = C.uiDrawTextStretch(desc.Stretch) + return &Font{ + f: C.newFont(d), + } +} + +// Free destroys a Font. After calling Free the Font cannot be used. +func (f *Font) Free() { + C.uiDrawFreeTextFont(f.f) +} + +// Handle returns the OS font object that backs this Font. On OSs +// that use reference counting for font objects, Handle does not +// increment the reference count; you are sharing package ui's +// reference. +// +// On Windows this is a pointer to an IDWriteFont. +// +// On Unix systems this is a pointer to a PangoFont. +// +// On OS X this is a CTFontRef. +func (f *Font) Handle() uintptr { + return uintptr(C.uiDrawTextFontHandle(f.f)) +} + +// Describe returns the FontDescriptor that most closely matches +// this Font. +// TODO guarantees about idempotency +// TODO rewrite that first sentence +func (f *Font) Describe() *FontDescriptor { + panic("TODO unimplemented") +} + +// FontMetrics holds various measurements about a Font. +// All metrics are in the same point units used for drawing. +type FontMetrics struct { + // Ascent is the ascent of the font; that is, the distance from + // the top of the character cell to the baseline. + Ascent float64 + + // Descent is the descent of the font; that is, the distance from + // the baseline to the bottom of the character cell. The sum of + // Ascent and Descent is the height of the character cell (and + // thus, the maximum height of a line of text). + Descent float64 + + // Leading is the amount of space the font designer suggests + // to have between lines (between the bottom of the first line's + // character cell and the top of the second line's character cell). + // This is a suggestion; it is chosen by the font designer to + // improve legibility. + Leading float64 + + // TODO figure out what these are + UnderlinePos float64 + UnderlineThickness float64 +} + +// Metrics returns metrics about the given Font. +func (f *Font) Metrics() *FontMetrics { + m := new(FontMetrics) + mm := C.newFontMetrics() + C.uiDrawTextFontGetMetrics(f.f, mm) + m.Ascent = float64(mm.Ascent) + m.Descent = float64(mm.Descent) + m.Leading = float64(mm.Leading) + m.UnderlinePos = float64(mm.UnderlinePos) + m.UnderlineThickness = float64(mm.UnderlineThickness) + C.freeFontMetrics(mm) + return m +} + +// TextLayout is the entry point for formatting a block of text to be +// drawn onto a DrawContext. +// +// The block of text to lay out and the default font that is used if no +// font attributes are applied to a given character are provided +// at TextLayout creation time and cannot be changed later. +// However, you may add attributes to various points of the text +// at any time, even after drawing the text once (unlike a DrawPath). +// Some of these attributes also have initial values; refer to each +// method to see what they are. +// +// The block of text can either be a single line or multiple +// word-wrapped lines, each with a given maximum width. +type TextLayout struct { + l *C.uiDrawTextLayout +} + +// NewTextLayout creates a new TextLayout. +// For details on the width parameter, see SetWidth. +func NewTextLayout(text string, defaultFont *Font, width float64) *TextLayout { + l := new(TextLayout) + ctext := C.CString(text) // freed by C.newTextLayout() + l.l = C.newTextLayout(ctext, defaultFont.f, C.double(width)) + return l +} + +// Free destroys a TextLayout. After calling Free the TextLayout +// cannot be used. +func (l *TextLayout) Free() { + C.uiDrawFreeTextLayout(l.l) +} + +// SetWidth sets the maximum width of the lines of text in a +// TextLayout. If the given width is negative, then the TextLayout +// will draw as a single line of text instead. +func (l *TextLayout) SetWidth(width float64) { + C.uiDrawTextLayoutSetWidth(l.l, C.double(width)) +} + +// Extents returns the width and height that the TextLayout will +// actually take up when drawn. This measures full line allocations, +// even if no glyph reaches to the top of its ascent or bottom of its +// descent; it does not return a "best fit" rectnagle for the points that +// are actually drawn. +// +// For a single-line TextLayout (where the width is negative), if there +// are no font changes throughout the TextLayout, then the height +// returned by TextLayout is equivalent to the sum of the ascent and +// descent of its default font's metrics. Or in other words, after +// f := ui.LoadClosestFont(...) +// l := ui.NewTextLayout("text", f, -1) +// metrics := f.Metrics() +// _, height := l.Extents() +// metrics.Ascent+metrics.Descent and height are equivalent. +func (l *TextLayout) Extents() (width float64, height float64) { + cwidth := C.newDouble() + cheight := C.newDouble() + C.uiDrawTextLayoutExtents(l.l, cwidth, cheight) + width = float64(*cwidth) + height = float64(*cheight) + C.freeDoubles(cwidth, cheight) + return width, height +} + +// Text draws the given TextLayout onto c at the given point. +// The point refers to the top-left corner of the text. +// (TODO bounding box or typographical extent?) +func (c *DrawContext) Text(x float64, y float64, layout *TextLayout) { + C.uiDrawText(c.c, C.double(x), C.double(y), layout.l) +} diff --git a/AAA_GOFILES/entry.go b/AAA_GOFILES/entry.go new file mode 100644 index 0000000..ee14fc8 --- /dev/null +++ b/AAA_GOFILES/entry.go @@ -0,0 +1,125 @@ +// 12 december 2015 + +// TODO typing in entry in OS X crashes libui +// I've had similar issues with checkboxes on libui +// something's wrong with NSMapTable + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +// extern void doEntryOnChanged(uiEntry *, void *); +// static inline void realuiEntryOnChanged(uiEntry *b) +// { +// uiEntryOnChanged(b, doEntryOnChanged, NULL); +// } +import "C" + +// no need to lock this; only the GUI thread can access it +var entries = make(map[*C.uiEntry]*Entry) + +// Entry is a Control that represents a space that the user can +// type a single line of text into. +type Entry struct { + c *C.uiControl + e *C.uiEntry + + onChanged func(*Entry) +} + +// NewEntry creates a new Entry. +func NewEntry() *Entry { + e := new(Entry) + + e.e = C.uiNewEntry() + e.c = (*C.uiControl)(unsafe.Pointer(e.e)) + + C.realuiEntryOnChanged(e.e) + entries[e.e] = e + + return e +} + +// Destroy destroys the Entry. +func (e *Entry) Destroy() { + delete(entries, e.e) + C.uiControlDestroy(e.c) +} + +// LibuiControl returns the libui uiControl pointer that backs +// the Window. This is only used by package ui itself and should +// not be called by programs. +func (e *Entry) LibuiControl() uintptr { + return uintptr(unsafe.Pointer(e.c)) +} + +// Handle returns the OS-level handle associated with this Entry. +// On Windows this is an HWND of a standard Windows API EDIT +// class (as provided by Common Controls version 6). +// On GTK+ this is a pointer to a GtkEntry. +// On OS X this is a pointer to a NSTextField. +func (e *Entry) Handle() uintptr { + return uintptr(C.uiControlHandle(e.c)) +} + +// Show shows the Entry. +func (e *Entry) Show() { + C.uiControlShow(e.c) +} + +// Hide hides the Entry. +func (e *Entry) Hide() { + C.uiControlHide(e.c) +} + +// Enable enables the Entry. +func (e *Entry) Enable() { + C.uiControlEnable(e.c) +} + +// Disable disables the Entry. +func (e *Entry) Disable() { + C.uiControlDisable(e.c) +} + +// Text returns the Entry's text. +func (e *Entry) Text() string { + ctext := C.uiEntryText(e.e) + text := C.GoString(ctext) + C.uiFreeText(ctext) + return text +} + +// SetText sets the Entry's text to text. +func (e *Entry) SetText(text string) { + ctext := C.CString(text) + C.uiEntrySetText(e.e, ctext) + freestr(ctext) +} + +// OnChanged registers f to be run when the user makes a change to +// the Entry. Only one function can be registered at a time. +func (e *Entry) OnChanged(f func(*Entry)) { + e.onChanged = f +} + +//export doEntryOnChanged +func doEntryOnChanged(ee *C.uiEntry, data unsafe.Pointer) { + e := entries[ee] + if e.onChanged != nil { + e.onChanged(e) + } +} + +// ReadOnly returns whether the Entry can be changed. +func (e *Entry) ReadOnly() bool { + return tobool(C.uiEntryReadOnly(e.e)) +} + +// SetReadOnly sets whether the Entry can be changed. +func (e *Entry) SetReadOnly(ro bool) { + C.uiEntrySetReadOnly(e.e, frombool(ro)) +} diff --git a/AAA_GOFILES/fontbutton.go b/AAA_GOFILES/fontbutton.go new file mode 100644 index 0000000..4087794 --- /dev/null +++ b/AAA_GOFILES/fontbutton.go @@ -0,0 +1,4 @@ +// 20 april 2016 +package ui + +// TODO diff --git a/AAA_GOFILES/group.go b/AAA_GOFILES/group.go new file mode 100644 index 0000000..0f9b99a --- /dev/null +++ b/AAA_GOFILES/group.go @@ -0,0 +1,117 @@ +// 12 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +import "C" + +// Group is a Control that holds another Control and wraps it around +// a labelled box (though some systems make this box invisible). +// You can use this to group related controls together. +type Group struct { + c *C.uiControl + g *C.uiGroup + + child Control +} + +// NewGroup creates a new Group. +func NewGroup(title string) *Group { + g := new(Group) + + ctitle := C.CString(title) + g.g = C.uiNewGroup(ctitle) + g.c = (*C.uiControl)(unsafe.Pointer(g.g)) + freestr(ctitle) + + return g +} + +// Destroy destroys the Group. If the Group has a child, +// Destroy calls Destroy on that as well. +func (g *Group) Destroy() { + if g.child != nil { + c := g.child + g.SetChild(nil) + c.Destroy() + } + C.uiControlDestroy(g.c) +} + +// LibuiControl returns the libui uiControl pointer that backs +// the Group. This is only used by package ui itself and should +// not be called by programs. +func (g *Group) LibuiControl() uintptr { + return uintptr(unsafe.Pointer(g.c)) +} + +// Handle returns the OS-level handle associated with this Group. +// On Windows this is an HWND of a standard Windows API BUTTON +// class (as provided by Common Controls version 6). +// On GTK+ this is a pointer to a GtkFrame. +// On OS X this is a pointer to a NSBox. +func (g *Group) Handle() uintptr { + return uintptr(C.uiControlHandle(g.c)) +} + +// Show shows the Group. +func (g *Group) Show() { + C.uiControlShow(g.c) +} + +// Hide hides the Group. +func (g *Group) Hide() { + C.uiControlHide(g.c) +} + +// Enable enables the Group. +func (g *Group) Enable() { + C.uiControlEnable(g.c) +} + +// Disable disables the Group. +func (g *Group) Disable() { + C.uiControlDisable(g.c) +} + +// Title returns the Group's title. +func (g *Group) Title() string { + ctitle := C.uiGroupTitle(g.g) + title := C.GoString(ctitle) + C.uiFreeText(ctitle) + return title +} + +// SetTitle sets the Group's title to title. +func (g *Group) SetTitle(title string) { + ctitle := C.CString(title) + C.uiGroupSetTitle(g.g, ctitle) + freestr(ctitle) +} + +// SetChild sets the Group's child to child. If child is nil, the Group +// will not have a child. +func (g *Group) SetChild(child Control) { + g.child = child + c := (*C.uiControl)(nil) + if g.child != nil { + c = touiControl(g.child.LibuiControl()) + } + C.uiGroupSetChild(g.g, c) +} + +// Margined returns whether the Group has margins around its child. +func (g *Group) Margined() bool { + return tobool(C.uiGroupMargined(g.g)) +} + +// SetMargined controls whether the Group has margins around its +// child. The size of the margins are determined by the OS and its +// best practices. +func (g *Group) SetMargined(margined bool) { + C.uiGroupSetMargined(g.g, frombool(margined)) +} diff --git a/AAA_GOFILES/label.go b/AAA_GOFILES/label.go new file mode 100644 index 0000000..e143d50 --- /dev/null +++ b/AAA_GOFILES/label.go @@ -0,0 +1,85 @@ +// 12 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +import "C" + +// Label is a Control that represents a line of text that cannot be +// interacted with. TODO rest of documentation. +type Label struct { + c *C.uiControl + l *C.uiLabel +} + +// NewLabel creates a new Label with the given text. +func NewLabel(text string) *Label { + l := new(Label) + + ctext := C.CString(text) + l.l = C.uiNewLabel(ctext) + l.c = (*C.uiControl)(unsafe.Pointer(l.l)) + freestr(ctext) + + return l +} + +// Destroy destroys the Label. +func (l *Label) Destroy() { + C.uiControlDestroy(l.c) +} + +// LibuiControl returns the libui uiControl pointer that backs +// the Window. This is only used by package ui itself and should +// not be called by programs. +func (l *Label) LibuiControl() uintptr { + return uintptr(unsafe.Pointer(l.c)) +} + +// Handle returns the OS-level handle associated with this Label. +// On Windows this is an HWND of a standard Windows API STATIC +// class (as provided by Common Controls version 6). +// On GTK+ this is a pointer to a GtkLabel. +// On OS X this is a pointer to a NSTextField. +func (l *Label) Handle() uintptr { + return uintptr(C.uiControlHandle(l.c)) +} + +// Show shows the Label. +func (l *Label) Show() { + C.uiControlShow(l.c) +} + +// Hide hides the Label. +func (l *Label) Hide() { + C.uiControlHide(l.c) +} + +// Enable enables the Label. +func (l *Label) Enable() { + C.uiControlEnable(l.c) +} + +// Disable disables the Label. +func (l *Label) Disable() { + C.uiControlDisable(l.c) +} + +// Text returns the Label's text. +func (l *Label) Text() string { + ctext := C.uiLabelText(l.l) + text := C.GoString(ctext) + C.uiFreeText(ctext) + return text +} + +// SetText sets the Label's text to text. +func (l *Label) SetText(text string) { + ctext := C.CString(text) + C.uiLabelSetText(l.l, ctext) + freestr(ctext) +} diff --git a/AAA_GOFILES/link_darwin_amd64.go b/AAA_GOFILES/link_darwin_amd64.go new file mode 100644 index 0000000..687a62a --- /dev/null +++ b/AAA_GOFILES/link_darwin_amd64.go @@ -0,0 +1,7 @@ +// 13 december 2015 + +package ui + +// #cgo CFLAGS: -mmacosx-version-min=10.8 -DMACOSX_DEPLOYMENT_TARGET=10.8 +// #cgo LDFLAGS: ${SRCDIR}/libui_darwin_amd64.a -framework Foundation -framework AppKit -mmacosx-version-min=10.8 +import "C" diff --git a/AAA_GOFILES/link_linux_386.go b/AAA_GOFILES/link_linux_386.go new file mode 100644 index 0000000..00a0598 --- /dev/null +++ b/AAA_GOFILES/link_linux_386.go @@ -0,0 +1,10 @@ +// +build !windows +// +build !darwin + +// 11 december 2015 + +package ui + +// #cgo LDFLAGS: ${SRCDIR}/libui_linux_386.a -lm -ldl +// #cgo pkg-config: gtk+-3.0 +import "C" diff --git a/AAA_GOFILES/link_linux_amd64.go b/AAA_GOFILES/link_linux_amd64.go new file mode 100644 index 0000000..a1b2b3b --- /dev/null +++ b/AAA_GOFILES/link_linux_amd64.go @@ -0,0 +1,10 @@ +// +build !windows +// +build !darwin + +// 11 december 2015 + +package ui + +// #cgo LDFLAGS: ${SRCDIR}/libui_linux_amd64.a -lm -ldl +// #cgo pkg-config: gtk+-3.0 +import "C" diff --git a/AAA_GOFILES/link_windows_386.go b/AAA_GOFILES/link_windows_386.go new file mode 100644 index 0000000..d617a20 --- /dev/null +++ b/AAA_GOFILES/link_windows_386.go @@ -0,0 +1,8 @@ +// 13 december 2015 + +package ui + +// #cgo LDFLAGS: ${SRCDIR}/libui_windows_386.a +// /* note the order; also note the lack of uuid */ +// #cgo LDFLAGS: -luser32 -lkernel32 -lusp10 -lgdi32 -lcomctl32 -luxtheme -lmsimg32 -lcomdlg32 -ld2d1 -ldwrite -lole32 -loleaut32 -loleacc -static -static-libgcc -static-libstdc++ +import "C" diff --git a/AAA_GOFILES/link_windows_amd64.go b/AAA_GOFILES/link_windows_amd64.go new file mode 100644 index 0000000..73c94af --- /dev/null +++ b/AAA_GOFILES/link_windows_amd64.go @@ -0,0 +1,8 @@ +// 13 december 2015 + +package ui + +// #cgo LDFLAGS: ${SRCDIR}/libui_windows_amd64.a +// /* note the order; also note the lack of uuid */ +// #cgo LDFLAGS: -luser32 -lkernel32 -lusp10 -lgdi32 -lcomctl32 -luxtheme -lmsimg32 -lcomdlg32 -ld2d1 -ldwrite -lole32 -loleaut32 -loleacc -static -static-libgcc -static-libstdc++ +import "C" diff --git a/AAA_GOFILES/main.go b/AAA_GOFILES/main.go new file mode 100644 index 0000000..dd1443a --- /dev/null +++ b/AAA_GOFILES/main.go @@ -0,0 +1,138 @@ +// 11 december 2015 + +package ui + +import ( + "runtime" + "errors" + "sync" + "unsafe" +) + +// #include "ui.h" +// extern void doQueued(void *); +// extern int doOnShouldQuit(void *); +// /* I forgot how dumb cgo is... ./main.go:73: cannot use _Cgo_ptr(_Cfpvar_fp_doQueued) (type unsafe.Pointer) as type *[0]byte in argument to _Cfunc_uiQueueMain */ +// /* I'm pretty sure this worked before... */ +// static inline void realQueueMain(void *x) +// { +// uiQueueMain(doQueued, x); +// } +// static inline void realOnShouldQuit(void) +// { +// uiOnShouldQuit(doOnShouldQuit, NULL); +// } +import "C" + +// make sure main() runs on the first thread created by the OS +// if main() calls Main(), things will just work on macOS, where the first thread created by the OS is the only thread allowed to be the main GUI thread +// we might as well lock the OS thread for the other platforms here too (though on those it doesn't matter *which* thread we lock to) +// TODO describe the source of this trick +func init() { + runtime.LockOSThread() +} + +// Main initializes package ui, runs f to set up the program, +// and executes the GUI main loop. f should set up the program's +// initial state: open the main window, create controls, and set up +// events. It should then return, at which point Main will +// process events until Quit is called, at which point Main will return +// nil. If package ui fails to initialize, Main returns an appropriate +// error. +func Main(f func()) error { + // TODO HEAP SAFETY + opts := C.uiInitOptions{} + estr := C.uiInit(&opts) + if estr != nil { + err := errors.New(C.GoString(estr)) + C.uiFreeInitError(estr) + return err + } + // set up OnShouldQuit() + C.realOnShouldQuit() + QueueMain(f) + C.uiMain() + return nil +} + +// 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() +} + +// These prevent the passing of Go functions into C land. +// TODO make an actual sparse list instead of this monotonic map thingy +var ( + qmmap = make(map[uintptr]func()) + qmcurrent = uintptr(0) + qmlock sync.Mutex +) + +// QueueMain queues f to be executed on the GUI thread when +// next possible. It returns immediately; that is, it does not wait +// for the function to actually be executed. QueueMain is the only +// function that can be called from other goroutines, and its +// primary purpose is to allow communication between other +// goroutines and the GUI thread. Calling QueueMain after Quit +// has been called results in undefined behavior. +// +// If you start a goroutine in f, it also cannot call package ui +// functions. So for instance, the following will result in +// undefined behavior: +// +// ui.QueueMain(func() { +// go ui.MsgBox(...) +// }) +func QueueMain(f func()) { + qmlock.Lock() + defer qmlock.Unlock() + + n := uintptr(0) + for { + n = qmcurrent + qmcurrent++ + if qmmap[n] == nil { + break + } + } + qmmap[n] = f + C.realQueueMain(unsafe.Pointer(n)) +} + +//export doQueued +func doQueued(nn unsafe.Pointer) { + qmlock.Lock() + + n := uintptr(nn) + f := qmmap[n] + delete(qmmap, n) + + // allow uiQueueMain() to be called by a queued function + // TODO explicitly allow this in libui too + qmlock.Unlock() + + f() +} + +// no need to lock this; this API is only safe on the main thread +var shouldQuitFunc func() bool + +// OnShouldQuit schedules f to be exeucted when the OS wants +// the program to quit or when a Quit menu item has been clicked. +// Only one function may be registered at a time. If the function +// returns true, Quit will be called. If the function returns false, or +// if OnShouldQuit is never called. Quit will not be called and the +// OS will be told that the program needs to continue running. +func OnShouldQuit(f func() bool) { + shouldQuitFunc = f +} + +//export doOnShouldQuit +func doOnShouldQuit(unused unsafe.Pointer) C.int { + if shouldQuitFunc == nil { + return 0 + } + return frombool(shouldQuitFunc()) +} diff --git a/AAA_GOFILES/progressbar.go b/AAA_GOFILES/progressbar.go new file mode 100644 index 0000000..ef5116c --- /dev/null +++ b/AAA_GOFILES/progressbar.go @@ -0,0 +1,77 @@ +// 12 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +import "C" + +// ProgressBar is a Control that represents a horizontal bar that +// is filled in progressively over time as a process completes. +type ProgressBar struct { + c *C.uiControl + p *C.uiProgressBar +} + +// NewProgressBar creates a new ProgressBar. +func NewProgressBar() *ProgressBar { + p := new(ProgressBar) + + p.p = C.uiNewProgressBar() + p.c = (*C.uiControl)(unsafe.Pointer(p.p)) + + return p +} + +// Destroy destroys the ProgressBar. +func (p *ProgressBar) Destroy() { + C.uiControlDestroy(p.c) +} + +// LibuiControl returns the libui uiControl pointer that backs +// the Window. This is only used by package ui itself and should +// not be called by programs. +func (p *ProgressBar) LibuiControl() uintptr { + return uintptr(unsafe.Pointer(p.c)) +} + +// Handle returns the OS-level handle associated with this ProgressBar. +// On Windows this is an HWND of a standard Windows API +// PROGRESS_CLASS class (as provided by Common Controls +// version 6). +// On GTK+ this is a pointer to a GtkProgressBar. +// On OS X this is a pointer to a NSProgressIndicator. +func (p *ProgressBar) Handle() uintptr { + return uintptr(C.uiControlHandle(p.c)) +} + +// Show shows the ProgressBar. +func (p *ProgressBar) Show() { + C.uiControlShow(p.c) +} + +// Hide hides the ProgressBar. +func (p *ProgressBar) Hide() { + C.uiControlHide(p.c) +} + +// Enable enables the ProgressBar. +func (p *ProgressBar) Enable() { + C.uiControlEnable(p.c) +} + +// Disable disables the ProgressBar. +func (p *ProgressBar) Disable() { + C.uiControlDisable(p.c) +} + +// TODO Value + +// SetValue sets the ProgressBar's currently displayed percentage +// to value. value must be between 0 and 100 inclusive. +func (p *ProgressBar) SetValue(value int) { + C.uiProgressBarSetValue(p.p, C.int(value)) +} diff --git a/AAA_GOFILES/radiobuttons.go b/AAA_GOFILES/radiobuttons.go new file mode 100644 index 0000000..b8399ea --- /dev/null +++ b/AAA_GOFILES/radiobuttons.go @@ -0,0 +1,77 @@ +// 12 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +import "C" + +// RadioButtons is a Control that represents a set of checkable +// buttons from which exactly one may be chosen by the user. +type RadioButtons struct { + c *C.uiControl + r *C.uiRadioButtons +} + +// NewRadioButtons creates a new RadioButtons. +func NewRadioButtons() *RadioButtons { + r := new(RadioButtons) + + r.r = C.uiNewRadioButtons() + r.c = (*C.uiControl)(unsafe.Pointer(r.r)) + + return r +} + +// Destroy destroys the RadioButtons. +func (r *RadioButtons) Destroy() { + C.uiControlDestroy(r.c) +} + +// LibuiControl returns the libui uiControl pointer that backs +// the Window. This is only used by package ui itself and should +// not be called by programs. +func (r *RadioButtons) LibuiControl() uintptr { + return uintptr(unsafe.Pointer(r.c)) +} + +// Handle returns the OS-level handle associated with this RadioButtons. +// On Windows this is an HWND of a libui-internal class; its +// child windows are instances of the standard Windows API +// BUTTON class (as provided by Common Controls version 6). +// On GTK+ this is a pointer to a GtkBox containing GtkRadioButtons. +// On OS X this is a pointer to a NSView with each radio button as a NSButton subview. +func (r *RadioButtons) Handle() uintptr { + return uintptr(C.uiControlHandle(r.c)) +} + +// Show shows the RadioButtons. +func (r *RadioButtons) Show() { + C.uiControlShow(r.c) +} + +// Hide hides the RadioButtons. +func (r *RadioButtons) Hide() { + C.uiControlHide(r.c) +} + +// Enable enables the RadioButtons. +func (r *RadioButtons) Enable() { + C.uiControlEnable(r.c) +} + +// Disable disables the RadioButtons. +func (r *RadioButtons) Disable() { + C.uiControlDisable(r.c) +} + +// Append adds the named button to the end of the RadioButtons. +// If this button is the first button, it is automatically selected. +func (r *RadioButtons) Append(text string) { + ctext := C.CString(text) + C.uiRadioButtonsAppend(r.r, ctext) + freestr(ctext) +} diff --git a/AAA_GOFILES/separator.go b/AAA_GOFILES/separator.go new file mode 100644 index 0000000..5f41891 --- /dev/null +++ b/AAA_GOFILES/separator.go @@ -0,0 +1,68 @@ +// 12 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +import "C" + +// Separator is a Control that represents a horizontal line that +// visually separates controls. +type Separator struct { + c *C.uiControl + s *C.uiSeparator +} + +// NewSeparator creates a new horizontal Separator. +func NewHorizontalSeparator() *Separator { + s := new(Separator) + + s.s = C.uiNewHorizontalSeparator() + s.c = (*C.uiControl)(unsafe.Pointer(s.s)) + + return s +} + +// Destroy destroys the Separator. +func (s *Separator) Destroy() { + C.uiControlDestroy(s.c) +} + +// LibuiControl returns the libui uiControl pointer that backs +// the Window. This is only used by package ui itself and should +// not be called by programs. +func (s *Separator) LibuiControl() uintptr { + return uintptr(unsafe.Pointer(s.c)) +} + +// Handle returns the OS-level handle associated with this Separator. +// On Windows this is an HWND of a standard Windows API STATIC +// class (as provided by Common Controls version 6). +// On GTK+ this is a pointer to a GtkSeparator. +// On OS X this is a pointer to a NSBox. +func (s *Separator) Handle() uintptr { + return uintptr(C.uiControlHandle(s.c)) +} + +// Show shows the Separator. +func (s *Separator) Show() { + C.uiControlShow(s.c) +} + +// Hide hides the Separator. +func (s *Separator) Hide() { + C.uiControlHide(s.c) +} + +// Enable enables the Separator. +func (s *Separator) Enable() { + C.uiControlEnable(s.c) +} + +// Disable disables the Separator. +func (s *Separator) Disable() { + C.uiControlDisable(s.c) +} diff --git a/AAA_GOFILES/slider.go b/AAA_GOFILES/slider.go new file mode 100644 index 0000000..99b8d2c --- /dev/null +++ b/AAA_GOFILES/slider.go @@ -0,0 +1,108 @@ +// 12 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +// extern void doSliderOnChanged(uiSlider *, void *); +// static inline void realuiSliderOnChanged(uiSlider *b) +// { +// uiSliderOnChanged(b, doSliderOnChanged, NULL); +// } +import "C" + +// no need to lock this; only the GUI thread can access it +var sliders = make(map[*C.uiSlider]*Slider) + +// Slider is a Control that represents a horizontal bar that represents +// a range of integers. The user can drag a pointer on the bar to +// select an integer. +type Slider struct { + c *C.uiControl + s *C.uiSlider + + onChanged func(*Slider) +} + +// NewSlider creates a new Slider. If min >= max, they are swapped. +func NewSlider(min int, max int) *Slider { + s := new(Slider) + + s.s = C.uiNewSlider(C.intmax_t(min), C.intmax_t(max)) + s.c = (*C.uiControl)(unsafe.Pointer(s.s)) + + C.realuiSliderOnChanged(s.s) + sliders[s.s] = s + + return s +} + +// Destroy destroys the Slider. +func (s *Slider) Destroy() { + delete(sliders, s.s) + C.uiControlDestroy(s.c) +} + +// LibuiControl returns the libui uiControl pointer that backs +// the Window. This is only used by package ui itself and should +// not be called by programs. +func (s *Slider) LibuiControl() uintptr { + return uintptr(unsafe.Pointer(s.c)) +} + +// Handle returns the OS-level handle associated with this Slider. +// On Windows this is an HWND of a standard Windows API +// TRACKBAR_CLASS class (as provided by Common Controls +// version 6). +// On GTK+ this is a pointer to a GtkScale. +// On OS X this is a pointer to a NSSlider. +func (s *Slider) Handle() uintptr { + return uintptr(C.uiControlHandle(s.c)) +} + +// Show shows the Slider. +func (s *Slider) Show() { + C.uiControlShow(s.c) +} + +// Hide hides the Slider. +func (s *Slider) Hide() { + C.uiControlHide(s.c) +} + +// Enable enables the Slider. +func (s *Slider) Enable() { + C.uiControlEnable(s.c) +} + +// Disable disables the Slider. +func (s *Slider) Disable() { + C.uiControlDisable(s.c) +} + +// Value returns the Slider's current value. +func (s *Slider) Value() int { + return int(C.uiSliderValue(s.s)) +} + +// SetText sets the Slider's current value to value. +func (s *Slider) SetValue(value int) { + C.uiSliderSetValue(s.s, C.intmax_t(value)) +} + +// OnChanged registers f to be run when the user changes the value +// of the Slider. Only one function can be registered at a time. +func (s *Slider) OnChanged(f func(*Slider)) { + s.onChanged = f +} + +//export doSliderOnChanged +func doSliderOnChanged(ss *C.uiSlider, data unsafe.Pointer) { + s := sliders[ss] + if s.onChanged != nil { + s.onChanged(s) + } +} diff --git a/AAA_GOFILES/spinbox.go b/AAA_GOFILES/spinbox.go new file mode 100644 index 0000000..5e5e43f --- /dev/null +++ b/AAA_GOFILES/spinbox.go @@ -0,0 +1,111 @@ +// 12 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +// extern void doSpinboxOnChanged(uiSpinbox *, void *); +// static inline void realuiSpinboxOnChanged(uiSpinbox *b) +// { +// uiSpinboxOnChanged(b, doSpinboxOnChanged, NULL); +// } +import "C" + +// no need to lock this; only the GUI thread can access it +var spinboxes = make(map[*C.uiSpinbox]*Spinbox) + +// Spinbox is a Control that represents a space where the user can +// enter integers. The space also comes with buttons to add or +// subtract 1 from the integer. +type Spinbox struct { + c *C.uiControl + s *C.uiSpinbox + + onChanged func(*Spinbox) +} + +// NewSpinbox creates a new Spinbox. If min >= max, they are swapped. +func NewSpinbox(min int, max int) *Spinbox { + s := new(Spinbox) + + s.s = C.uiNewSpinbox(C.intmax_t(min), C.intmax_t(max)) + s.c = (*C.uiControl)(unsafe.Pointer(s.s)) + + C.realuiSpinboxOnChanged(s.s) + spinboxes[s.s] = s + + return s +} + +// Destroy destroys the Spinbox. +func (s *Spinbox) Destroy() { + delete(spinboxes, s.s) + C.uiControlDestroy(s.c) +} + +// LibuiControl returns the libui uiControl pointer that backs +// the Window. This is only used by package ui itself and should +// not be called by programs. +func (s *Spinbox) LibuiControl() uintptr { + return uintptr(unsafe.Pointer(s.c)) +} + +// Handle returns the OS-level handle associated with this Spinbox. +// On Windows this is an HWND of a standard Windows API EDIT +// class (as provided by Common Controls version 6). Due to +// various limitations which affect the lifetime of the associated +// Common Controls version 6 UPDOWN_CLASS window that +// provides the buttons, there is no way to access it. +// On GTK+ this is a pointer to a GtkSpinButton. +// On OS X this is a pointer to a NSView that contains a NSTextField +// and a NSStepper as subviews. +func (s *Spinbox) Handle() uintptr { + return uintptr(C.uiControlHandle(s.c)) +} + +// Show shows the Spinbox. +func (s *Spinbox) Show() { + C.uiControlShow(s.c) +} + +// Hide hides the Spinbox. +func (s *Spinbox) Hide() { + C.uiControlHide(s.c) +} + +// Enable enables the Spinbox. +func (s *Spinbox) Enable() { + C.uiControlEnable(s.c) +} + +// Disable disables the Spinbox. +func (s *Spinbox) Disable() { + C.uiControlDisable(s.c) +} + +// Value returns the Spinbox's current value. +func (s *Spinbox) Value() int { + return int(C.uiSpinboxValue(s.s)) +} + +// SetText sets the Spinbox's current value to value. +func (s *Spinbox) SetValue(value int) { + C.uiSpinboxSetValue(s.s, C.intmax_t(value)) +} + +// OnChanged registers f to be run when the user changes the value +// of the Spinbox. Only one function can be registered at a time. +func (s *Spinbox) OnChanged(f func(*Spinbox)) { + s.onChanged = f +} + +//export doSpinboxOnChanged +func doSpinboxOnChanged(ss *C.uiSpinbox, data unsafe.Pointer) { + s := spinboxes[ss] + if s.onChanged != nil { + s.onChanged(s) + } +} diff --git a/AAA_GOFILES/stddialogs.go b/AAA_GOFILES/stddialogs.go new file mode 100644 index 0000000..0ff8a01 --- /dev/null +++ b/AAA_GOFILES/stddialogs.go @@ -0,0 +1,41 @@ +// 20 december 2015 + +package ui + +// #include "ui.h" +import "C" + +// TODO +func MsgBoxError(w *Window, title string, description string) { + ctitle := C.CString(title) + defer freestr(ctitle) + cdescription := C.CString(description) + defer freestr(cdescription) + C.uiMsgBoxError(w.w, ctitle, cdescription) +} + +func OpenFile(w *Window) string { + cname := C.uiOpenFile(w.w) + if cname == nil { + return "" + } + defer C.uiFreeText(cname) + return C.GoString(cname) +} + +func SaveFile(w *Window) string { + cname := C.uiSaveFile(w.w) + if cname == nil { + return "" + } + defer C.uiFreeText(cname) + return C.GoString(cname) +} + +func MsgBox(w *Window, title string, description string) { + ctitle := C.CString(title) + defer freestr(ctitle) + cdescription := C.CString(description) + defer freestr(cdescription) + C.uiMsgBox(w.w, ctitle, cdescription) +} diff --git a/AAA_GOFILES/tab.go b/AAA_GOFILES/tab.go new file mode 100644 index 0000000..d18cecd --- /dev/null +++ b/AAA_GOFILES/tab.go @@ -0,0 +1,127 @@ +// 12 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include "ui.h" +import "C" + +// Tab is a Control that holds tabbed pages of Controls. Each tab +// has a label. The user can click on the tabs themselves to switch +// pages. Individual pages can also have margins. +type Tab struct { + c *C.uiControl + t *C.uiTab + + children []Control +} + +// NewTab creates a new Tab. +func NewTab() *Tab { + t := new(Tab) + + t.t = C.uiNewTab() + t.c = (*C.uiControl)(unsafe.Pointer(t.t)) + + return t +} + +// Destroy destroys the Tab. If the Tab has pages, +// Destroy calls Destroy on the pages's Controls as well. +func (t *Tab) Destroy() { + for len(t.children) != 0 { + c := t.children[0] + t.Delete(0) + c.Destroy() + } + C.uiControlDestroy(t.c) +} + +// LibuiControl returns the libui uiControl pointer that backs +// the Tab. This is only used by package ui itself and should +// not be called by programs. +func (t *Tab) LibuiControl() uintptr { + return uintptr(unsafe.Pointer(t.c)) +} + +// Handle returns the OS-level handle associated with this Tab. +// On Windows this is an HWND of a standard Windows API +// WC_TABCONTROL class (as provided by Common Controls +// version 6). The pages are not children of this window and there +// currently is no way to directly access them. +// On GTK+ this is a pointer to a GtkNotebook. +// On OS X this is a pointer to a NSTabView. +func (t *Tab) Handle() uintptr { + return uintptr(C.uiControlHandle(t.c)) +} + +// Show shows the Tab. +func (t *Tab) Show() { + C.uiControlShow(t.c) +} + +// Hide hides the Tab. +func (t *Tab) Hide() { + C.uiControlHide(t.c) +} + +// Enable enables the Tab. +func (t *Tab) Enable() { + C.uiControlEnable(t.c) +} + +// Disable disables the Tab. +func (t *Tab) Disable() { + C.uiControlDisable(t.c) +} + +// Append adds the given page to the end of the Tab. +func (t *Tab) Append(name string, child Control) { + t.InsertAt(name, len(t.children), child) +} + +// InsertAt adds the given page to the Tab such that it is the +// nth page of the Tab (starting at 0). +func (t *Tab) InsertAt(name string, n int, child Control) { + c := (*C.uiControl)(nil) + if child != nil { + c = touiControl(child.LibuiControl()) + } + cname := C.CString(name) + // TODO why is this uintmax_t and not intmax_t + C.uiTabInsertAt(t.t, cname, C.uintmax_t(n), c) + freestr(cname) + ch := make([]Control, len(t.children) + 1) + // and insert into t.children at the right place + copy(ch[:n], t.children[:n]) + ch[n] = child + copy(ch[n + 1:], t.children[n:]) + t.children = ch +} + +// Delete deletes the nth page of the Tab. +func (t *Tab) Delete(n int) { + t.children = append(t.children[:n], t.children[n + 1:]...) + C.uiTabDelete(t.t, C.uintmax_t(n)) +} + +// NumPages returns the number of pages in the Tab. +func (t *Tab) NumPages() int { + return len(t.children) +} + +// Margined returns whether page n (starting at 0) of the Tab +// has margins around its child. +func (t *Tab) Margined(n int) bool { + return tobool(C.uiTabMargined(t.t, C.uintmax_t(n))) +} + +// SetMargined controls whether page n (starting at 0) of the Tab +// has margins around its child. The size of the margins are +// determined by the OS and its best practices. +func (t *Tab) SetMargined(n int, margined bool) { + C.uiTabSetMargined(t.t, C.uintmax_t(n), frombool(margined)) +} diff --git a/AAA_GOFILES/util.go b/AAA_GOFILES/util.go new file mode 100644 index 0000000..b097004 --- /dev/null +++ b/AAA_GOFILES/util.go @@ -0,0 +1,53 @@ +// 12 december 2015 + +package ui + +import ( + "unsafe" +) + +// #include <stdlib.h> +// // TODO remove when switching to Go 1.7 +// #include <string.h> +import "C" + +// TODO move this to C.CBytes() when switching to Go 1.7 + +// We want Go itself to complain when we're out of memory. +// The allocators in cgo *should* do this, but there isn't a +// C.CMalloc(). There *is* a C.CBytes(), however, for transferring +// binary blobs from Go to C. If we pass this an arbitrary slice +// of the desired length, we get our C.CMalloc(). Using a slice +// that's always initialized to zero gives us the ZeroMemory() +// for free. +var uimallocBytes = make([]byte, 1024) // 1024 bytes first + +//export uimalloc +func uimalloc(n C.size_t) unsafe.Pointer { + if n > C.size_t(len(uimallocBytes)) { + // TODO round n up to a multiple of a power of 2? + // for instance 0x1234 bytes -> 0x1800 bytes + uimallocBytes = make([]byte, n) + } + p := C.malloc(n) + if p == nil { + panic("out of memory in uimalloc()") + } + C.memset(p, 0, n) + return p +} + +func freestr(str *C.char) { + C.free(unsafe.Pointer(str)) +} + +func tobool(b C.int) bool { + return b != 0 +} + +func frombool(b bool) C.int { + if b { + return 1 + } + return 0 +} diff --git a/AAA_GOFILES/window.go b/AAA_GOFILES/window.go new file mode 100644 index 0000000..bdd230c --- /dev/null +++ b/AAA_GOFILES/window.go @@ -0,0 +1,159 @@ +// 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) +} + +// LibuiControl returns the libui uiControl pointer that backs +// the Window. This is only used by package ui itself and should +// not be called by programs. +func (w *Window) LibuiControl() uintptr { + return uintptr(unsafe.Pointer(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.LibuiControl()) + } + 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/AAA_GOFILES/zy_page1_test.go b/AAA_GOFILES/zy_page1_test.go new file mode 100644 index 0000000..6a8d4a4 --- /dev/null +++ b/AAA_GOFILES/zy_page1_test.go @@ -0,0 +1,155 @@ +// 12 december 2015 + +package ui + +var page1 *Box + +func makePage1(w *Window) { + var xbutton *Button + + page1 = newVerticalBox() + + entry := NewEntry() + page1.Append(entry, false) + + spaced := NewCheckbox("Spaced") + spaced.OnToggled(func(*Checkbox) { + setSpaced(spaced.Checked()) + }) + label := NewLabel("Label") + + hbox := newHorizontalBox() + getButton := NewButton("Get Window Text") + getButton.OnClicked(func(*Button) { + entry.SetText(w.Title()) + }) + setButton := NewButton("Set Window Text") + setButton.OnClicked(func(*Button) { + w.SetTitle(entry.Text()) + }) + hbox.Append(getButton, true) + hbox.Append(setButton, true) + page1.Append(hbox, false) + + hbox = newHorizontalBox() + getButton = NewButton("Get Button Text") + xbutton = getButton + getButton.OnClicked(func(*Button) { + entry.SetText(xbutton.Text()) + }) + setButton = NewButton("Set Button Text") + setButton.OnClicked(func(*Button) { + xbutton.SetText(entry.Text()) + }) + hbox.Append(getButton, true) + hbox.Append(setButton, true) + page1.Append(hbox, false) + + hbox = newHorizontalBox() + getButton = NewButton("Get Checkbox Text") + getButton.OnClicked(func(*Button) { + entry.SetText(spaced.Text()) + }) + setButton = NewButton("Set Checkbox Text") + setButton.OnClicked(func(*Button) { + spaced.SetText(entry.Text()) + }) + hbox.Append(getButton, true) + hbox.Append(setButton, true) + page1.Append(hbox, false) + + hbox = newHorizontalBox() + getButton = NewButton("Get Label Text") + getButton.OnClicked(func(*Button) { + entry.SetText(label.Text()) + }) + setButton = NewButton("Set Label Text") + setButton.OnClicked(func(*Button) { + label.SetText(entry.Text()) + }) + hbox.Append(getButton, true) + hbox.Append(setButton, true) + page1.Append(hbox, false) + + hbox = newHorizontalBox() + getButton = NewButton("Get Group Text") + getButton.OnClicked(func(*Button) { + entry.SetText(page2group.Title()) + }) + setButton = NewButton("Set Group Text") + setButton.OnClicked(func(*Button) { + page2group.SetTitle(entry.Text()) + }) + hbox.Append(getButton, true) + hbox.Append(setButton, true) + page1.Append(hbox, false) + + hbox = newHorizontalBox() + hbox.Append(spaced, true) + getButton = NewButton("On") + getButton.OnClicked(func(*Button) { + spaced.SetChecked(true) + }) + hbox.Append(getButton, false) + getButton = NewButton("Off") + getButton.OnClicked(func(*Button) { + spaced.SetChecked(false) + }) + hbox.Append(getButton, false) + getButton = NewButton("Show") + getButton.OnClicked(func(*Button) { + // TODO + }) + hbox.Append(getButton, false) + page1.Append(hbox, false) + + testBox := newHorizontalBox() + ybutton := NewButton("Button") + testBox.Append(ybutton, true) + getButton = NewButton("Show") + getButton.OnClicked(func(*Button) { + ybutton.Show() + }) + testBox.Append(getButton, false) + getButton = NewButton("Hide") + getButton.OnClicked(func(*Button) { + ybutton.Hide() + }) + testBox.Append(getButton, false) + getButton = NewButton("Enable") + getButton.OnClicked(func(*Button) { + ybutton.Enable() + }) + testBox.Append(getButton, false) + getButton = NewButton("Disable") + getButton.OnClicked(func(*Button) { + ybutton.Disable() + }) + testBox.Append(getButton, false) + page1.Append(testBox, false) + + hbox = newHorizontalBox() + getButton = NewButton("Show") + getButton.OnClicked(func(*Button) { + testBox.Show() + }) + hbox.Append(getButton, false) + getButton = NewButton("Hide") + getButton.OnClicked(func(*Button) { + testBox.Hide() + }) + hbox.Append(getButton, false) + getButton = NewButton("Enable") + getButton.OnClicked(func(*Button) { + testBox.Enable() + }) + hbox.Append(getButton, false) + getButton = NewButton("Disable") + getButton.OnClicked(func(*Button) { + testBox.Disable() + }) + hbox.Append(getButton, false) + page1.Append(hbox, false) + + page1.Append(label, false) +} diff --git a/AAA_GOFILES/zy_page2_test.go b/AAA_GOFILES/zy_page2_test.go new file mode 100644 index 0000000..dd4a6be --- /dev/null +++ b/AAA_GOFILES/zy_page2_test.go @@ -0,0 +1,187 @@ +// 12 december 2015 + +package ui + +var page2group *Group + +var ( + movingLabel *Label + movingBoxes [2]*Box + movingCurrent int +) + +func moveLabel(*Button) { + from := movingCurrent + to := 0 + if from == 0 { + to = 1 + } + movingBoxes[from].Delete(0) + movingBoxes[to].Append(movingLabel, false) + movingCurrent = to +} + +var moveBack bool +const ( + moveOutText = "Move Page 1 Out" + moveBackText = "Move Page 1 Back" +) + +func movePage1(b *Button) { + if moveBack { + mainbox.Delete(1) + mainTab.InsertAt("Page 1", 0, page1) + b.SetText(moveOutText) + moveBack = false + return + } + mainTab.Delete(0) + mainbox.Append(page1, true) + b.SetText(moveBackText) + moveBack = true +} + +func makePage2() *Box { + page2 := newVerticalBox() + + group := newGroup("Moving Label") + page2group = group + page2.Append(group, false) + vbox := newVerticalBox() + group.SetChild(vbox) + + hbox := newHorizontalBox() + button := NewButton("Move the Label!") + button.OnClicked(moveLabel) + hbox.Append(button, true) + hbox.Append(NewLabel(""), true) + vbox.Append(hbox, false) + + hbox = newHorizontalBox() + movingBoxes[0] = newVerticalBox() + hbox.Append(movingBoxes[0], true) + movingBoxes[1] = newVerticalBox() + hbox.Append(movingBoxes[1], true) + vbox.Append(hbox, false) + + movingCurrent = 0 + movingLabel = NewLabel("This label moves!") + movingBoxes[movingCurrent].Append(movingLabel, false) + + hbox = newHorizontalBox() + button = NewButton(moveOutText) + button.OnClicked(movePage1) + hbox.Append(button, false) + page2.Append(hbox, false) + moveBack = false + + hbox = newHorizontalBox() + hbox.Append(NewLabel("Label Alignment Test"), false) + button = NewButton("Open Menued Window") + button.OnClicked(func(*Button) { + w := NewWindow("Another Window", 100, 100, true) + b := NewVerticalBox() + b.Append(NewEntry(), false) + b.Append(NewButton("Button"), false) + b.SetPadded(true) + w.SetChild(b) + w.SetMargined(true) + w.Show() + }) + hbox.Append(button, false) + button = NewButton("Open Menuless Window") + button.OnClicked(func(*Button) { + w := NewWindow("Another Window", 100, 100, true) +//TODO w.SetChild(makePage6()) + w.SetMargined(true) + w.Show() + }) + hbox.Append(button, false) + button = NewButton("Disabled Menued") + button.OnClicked(func(*Button) { + w := NewWindow("Another Window", 100, 100, true) + w.Disable() + w.Show() + }) + hbox.Append(button, false) + button = NewButton("Disabled Menuless") + button.OnClicked(func(*Button) { + w := NewWindow("Another Window", 100, 100, false) + w.Disable() + w.Show() + }) + hbox.Append(button, false) + page2.Append(hbox, false) + + nestedBox := newHorizontalBox() + innerhbox := newHorizontalBox() + innerhbox.Append(NewButton("These"), false) + button = NewButton("buttons") + button.Disable() + innerhbox.Append(button, false) + nestedBox.Append(innerhbox, false) + innerhbox = newHorizontalBox() + innerhbox.Append(NewButton("are"), false) + innerhbox2 := newHorizontalBox() + button = NewButton("in") + button.Disable() + innerhbox2.Append(button, false) + innerhbox.Append(innerhbox2, false) + nestedBox.Append(innerhbox, false) + innerhbox = newHorizontalBox() + innerhbox2 = newHorizontalBox() + innerhbox2.Append(NewButton("nested"), false) + innerhbox3 := newHorizontalBox() + button = NewButton("boxes") + button.Disable() + innerhbox3.Append(button, false) + innerhbox2.Append(innerhbox3, false) + innerhbox.Append(innerhbox2, false) + nestedBox.Append(innerhbox, false) + page2.Append(nestedBox, false) + + hbox = newHorizontalBox() + button = NewButton("Enable Nested Box") + button.OnClicked(func(*Button) { + nestedBox.Enable() + }) + hbox.Append(button, false) + button = NewButton("Disable Nested Box") + button.OnClicked(func(*Button) { + nestedBox.Disable() + }) + hbox.Append(button, false) + page2.Append(hbox, false) + + disabledTab := newTab() + disabledTab.Append("Disabled", NewButton("Button")); + disabledTab.Append("Tab", NewLabel("Label")); + disabledTab.Disable() + page2.Append(disabledTab, true) + + entry := NewEntry() + readonly := NewEntry() + entry.OnChanged(func(*Entry) { + readonly.SetText(entry.Text()) + }) + readonly.SetText("If you can see this, uiEntryReadOnly() isn't working properly.") + readonly.SetReadOnly(true) + if readonly.ReadOnly() { + readonly.SetText("") + } + page2.Append(entry, false) + page2.Append(readonly, false) + + hbox = newHorizontalBox() + button = NewButton("Show Button 2") + button2 := NewButton("Button 2") + button.OnClicked(func(*Button) { + button2.Show() + }) + button2.Hide() + hbox.Append(button, true) + hbox.Append(button2, false) + page2.Append(hbox, false) + + return page2 +} diff --git a/AAA_GOFILES/zz_test.go b/AAA_GOFILES/zz_test.go new file mode 100644 index 0000000..83a8474 --- /dev/null +++ b/AAA_GOFILES/zz_test.go @@ -0,0 +1,129 @@ +// 11 december 2015 + +package ui + +import ( + "flag" + "testing" +) + +var ( + nomenus = flag.Bool("nomenus", false, "No menus") + startspaced = flag.Bool("startspaced", false, "Start with spacing") + swaphv = flag.Bool("swaphv", false, "Swap horizontal and vertical boxes") +) + +var mainbox *Box +var mainTab *Tab + +func xmain() { + if !*nomenus { + // TODO + } + + w := newWindow("Main Window", 320, 240, true) + w.OnClosing(func(*Window) bool { + Quit() + return true + }) + + OnShouldQuit(func() bool { + // TODO + return true + }) + + mainbox = newHorizontalBox() + w.SetChild(mainbox) + + outerTab := newTab() + mainbox.Append(outerTab, true) + + mainTab = newTab() + outerTab.Append("Original", mainTab) + + makePage1(w) + mainTab.Append("Page 1", page1) + + mainTab.Append("Page 2", makePage2()) + + // TODO + + if *startspaced { + setSpaced(true) + } + + w.Show() +} + +func TestIt(t *testing.T) { + err := Main(xmain) + if err != nil { + t.Fatal(err) + } +} + +var ( + spwindows []*Window + sptabs []*Tab + spgroups []*Group + spboxes []*Box +) + +func newWindow(title string, width int, height int, hasMenubar bool) *Window { + w := NewWindow(title, width, height, hasMenubar) + spwindows = append(spwindows, w) + return w +} + +func newTab() *Tab { + t := NewTab() + sptabs = append(sptabs, t) + return t +} + +func newGroup(title string) *Group { + g := NewGroup(title) + spgroups = append(spgroups, g) + return g +} + +func newHorizontalBox() *Box { + var b *Box + + if *swaphv { + b = NewVerticalBox() + } else { + b = NewHorizontalBox() + } + spboxes = append(spboxes, b) + return b +} + +func newVerticalBox() *Box { + var b *Box + + if *swaphv { + b = NewHorizontalBox() + } else { + b = NewVerticalBox() + } + spboxes = append(spboxes, b) + return b +} + +func setSpaced(spaced bool) { + for _, w := range spwindows { + w.SetMargined(spaced) + } + for _, t := range sptabs { + for i := 0; i < t.NumPages(); i++ { + t.SetMargined(i, spaced) + } + } + for _, g := range spgroups { + g.SetMargined(spaced) + } + for _, b := range spboxes { + b.SetPadded(spaced) + } +} |
