summaryrefslogtreecommitdiff
path: root/AAA_GOFILES
diff options
context:
space:
mode:
Diffstat (limited to 'AAA_GOFILES')
-rw-r--r--AAA_GOFILES/area.go156
-rw-r--r--AAA_GOFILES/areahandler.go321
-rw-r--r--AAA_GOFILES/box.go121
-rw-r--r--AAA_GOFILES/button.go114
-rw-r--r--AAA_GOFILES/checkbox.go124
-rw-r--r--AAA_GOFILES/combobox.go132
-rw-r--r--AAA_GOFILES/control.go107
-rw-r--r--AAA_GOFILES/datetimepicker.go92
-rw-r--r--AAA_GOFILES/draw.go834
-rw-r--r--AAA_GOFILES/entry.go125
-rw-r--r--AAA_GOFILES/fontbutton.go4
-rw-r--r--AAA_GOFILES/group.go117
-rw-r--r--AAA_GOFILES/label.go85
-rw-r--r--AAA_GOFILES/link_darwin_amd64.go7
-rw-r--r--AAA_GOFILES/link_linux_386.go10
-rw-r--r--AAA_GOFILES/link_linux_amd64.go10
-rw-r--r--AAA_GOFILES/link_windows_386.go8
-rw-r--r--AAA_GOFILES/link_windows_amd64.go8
-rw-r--r--AAA_GOFILES/main.go138
-rw-r--r--AAA_GOFILES/progressbar.go77
-rw-r--r--AAA_GOFILES/radiobuttons.go77
-rw-r--r--AAA_GOFILES/separator.go68
-rw-r--r--AAA_GOFILES/slider.go108
-rw-r--r--AAA_GOFILES/spinbox.go111
-rw-r--r--AAA_GOFILES/stddialogs.go41
-rw-r--r--AAA_GOFILES/tab.go127
-rw-r--r--AAA_GOFILES/util.go53
-rw-r--r--AAA_GOFILES/window.go159
-rw-r--r--AAA_GOFILES/zy_page1_test.go155
-rw-r--r--AAA_GOFILES/zy_page2_test.go187
-rw-r--r--AAA_GOFILES/zz_test.go129
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)
+ }
+}