From 308e253e0f7873710bf77312d7a12c576aaa9781 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Aug 2018 16:18:01 -0400 Subject: Moved the existing .go files out of the way and replaced ui.h with the alpha4 ui.h. --- AAA_GOFILES/area.go | 156 ++++++ AAA_GOFILES/areahandler.go | 321 ++++++++++++ AAA_GOFILES/box.go | 121 +++++ AAA_GOFILES/button.go | 114 ++++ AAA_GOFILES/checkbox.go | 124 +++++ AAA_GOFILES/combobox.go | 132 +++++ AAA_GOFILES/control.go | 107 ++++ AAA_GOFILES/datetimepicker.go | 92 ++++ AAA_GOFILES/draw.go | 834 +++++++++++++++++++++++++++++ AAA_GOFILES/entry.go | 125 +++++ AAA_GOFILES/fontbutton.go | 4 + AAA_GOFILES/group.go | 117 +++++ AAA_GOFILES/label.go | 85 +++ AAA_GOFILES/link_darwin_amd64.go | 7 + AAA_GOFILES/link_linux_386.go | 10 + AAA_GOFILES/link_linux_amd64.go | 10 + AAA_GOFILES/link_windows_386.go | 8 + AAA_GOFILES/link_windows_amd64.go | 8 + AAA_GOFILES/main.go | 138 +++++ AAA_GOFILES/progressbar.go | 77 +++ AAA_GOFILES/radiobuttons.go | 77 +++ AAA_GOFILES/separator.go | 68 +++ AAA_GOFILES/slider.go | 108 ++++ AAA_GOFILES/spinbox.go | 111 ++++ AAA_GOFILES/stddialogs.go | 41 ++ AAA_GOFILES/tab.go | 127 +++++ AAA_GOFILES/util.go | 53 ++ AAA_GOFILES/window.go | 159 ++++++ AAA_GOFILES/zy_page1_test.go | 155 ++++++ AAA_GOFILES/zy_page2_test.go | 187 +++++++ AAA_GOFILES/zz_test.go | 129 +++++ area.go | 156 ------ areahandler.go | 321 ------------ bindTODO | 1 - box.go | 121 ----- button.go | 114 ---- checkbox.go | 124 ----- combobox.go | 132 ----- control.go | 107 ---- datetimepicker.go | 92 ---- draw.go | 834 ----------------------------- entry.go | 125 ----- fontbutton.go | 4 - group.go | 117 ----- label.go | 85 --- link_darwin_amd64.go | 7 - link_linux_386.go | 10 - link_linux_amd64.go | 10 - link_windows_386.go | 8 - link_windows_amd64.go | 8 - main.go | 138 ----- progressbar.go | 77 --- radiobuttons.go | 77 --- separator.go | 68 --- slider.go | 108 ---- spinbox.go | 111 ---- stddialogs.go | 41 -- tab.go | 127 ----- ui.h | 1044 +++++++++++++++++++++++++++++++++---- util.go | 53 -- window.go | 159 ------ zy_page1_test.go | 155 ------ zy_page2_test.go | 187 ------- zz_test.go | 129 ----- 64 files changed, 4747 insertions(+), 3908 deletions(-) create mode 100644 AAA_GOFILES/area.go create mode 100644 AAA_GOFILES/areahandler.go create mode 100644 AAA_GOFILES/box.go create mode 100644 AAA_GOFILES/button.go create mode 100644 AAA_GOFILES/checkbox.go create mode 100644 AAA_GOFILES/combobox.go create mode 100644 AAA_GOFILES/control.go create mode 100644 AAA_GOFILES/datetimepicker.go create mode 100644 AAA_GOFILES/draw.go create mode 100644 AAA_GOFILES/entry.go create mode 100644 AAA_GOFILES/fontbutton.go create mode 100644 AAA_GOFILES/group.go create mode 100644 AAA_GOFILES/label.go create mode 100644 AAA_GOFILES/link_darwin_amd64.go create mode 100644 AAA_GOFILES/link_linux_386.go create mode 100644 AAA_GOFILES/link_linux_amd64.go create mode 100644 AAA_GOFILES/link_windows_386.go create mode 100644 AAA_GOFILES/link_windows_amd64.go create mode 100644 AAA_GOFILES/main.go create mode 100644 AAA_GOFILES/progressbar.go create mode 100644 AAA_GOFILES/radiobuttons.go create mode 100644 AAA_GOFILES/separator.go create mode 100644 AAA_GOFILES/slider.go create mode 100644 AAA_GOFILES/spinbox.go create mode 100644 AAA_GOFILES/stddialogs.go create mode 100644 AAA_GOFILES/tab.go create mode 100644 AAA_GOFILES/util.go create mode 100644 AAA_GOFILES/window.go create mode 100644 AAA_GOFILES/zy_page1_test.go create mode 100644 AAA_GOFILES/zy_page2_test.go create mode 100644 AAA_GOFILES/zz_test.go delete mode 100644 area.go delete mode 100644 areahandler.go delete mode 100644 bindTODO delete mode 100644 box.go delete mode 100644 button.go delete mode 100644 checkbox.go delete mode 100644 combobox.go delete mode 100644 control.go delete mode 100644 datetimepicker.go delete mode 100644 draw.go delete mode 100644 entry.go delete mode 100644 fontbutton.go delete mode 100644 group.go delete mode 100644 label.go delete mode 100644 link_darwin_amd64.go delete mode 100644 link_linux_386.go delete mode 100644 link_linux_amd64.go delete mode 100644 link_windows_386.go delete mode 100644 link_windows_amd64.go delete mode 100644 main.go delete mode 100644 progressbar.go delete mode 100644 radiobuttons.go delete mode 100644 separator.go delete mode 100644 slider.go delete mode 100644 spinbox.go delete mode 100644 stddialogs.go delete mode 100644 tab.go delete mode 100644 util.go delete mode 100644 window.go delete mode 100644 zy_page1_test.go delete mode 100644 zy_page2_test.go delete mode 100644 zz_test.go 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 +// #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 +// #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 +// // TODO remove when switching to Go 1.7 +// #include +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) + } +} diff --git a/area.go b/area.go deleted file mode 100644 index 358a391..0000000 --- a/area.go +++ /dev/null @@ -1,156 +0,0 @@ -// 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/areahandler.go b/areahandler.go deleted file mode 100644 index 74cbe56..0000000 --- a/areahandler.go +++ /dev/null @@ -1,321 +0,0 @@ -// 13 december 2015 - -package ui - -// #include -// #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/bindTODO b/bindTODO deleted file mode 100644 index 7efa25e..0000000 --- a/bindTODO +++ /dev/null @@ -1 +0,0 @@ -uiFontButton diff --git a/box.go b/box.go deleted file mode 100644 index caf49e3..0000000 --- a/box.go +++ /dev/null @@ -1,121 +0,0 @@ -// 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/button.go b/button.go deleted file mode 100644 index 2696f82..0000000 --- a/button.go +++ /dev/null @@ -1,114 +0,0 @@ -// 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/checkbox.go b/checkbox.go deleted file mode 100644 index b518b29..0000000 --- a/checkbox.go +++ /dev/null @@ -1,124 +0,0 @@ -// 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/combobox.go b/combobox.go deleted file mode 100644 index 2afa294..0000000 --- a/combobox.go +++ /dev/null @@ -1,132 +0,0 @@ -// 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/control.go b/control.go deleted file mode 100644 index 94ea34f..0000000 --- a/control.go +++ /dev/null @@ -1,107 +0,0 @@ -// 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/datetimepicker.go b/datetimepicker.go deleted file mode 100644 index 216b8b7..0000000 --- a/datetimepicker.go +++ /dev/null @@ -1,92 +0,0 @@ -// 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/draw.go b/draw.go deleted file mode 100644 index eb4158d..0000000 --- a/draw.go +++ /dev/null @@ -1,834 +0,0 @@ -// 13 december 2015 - -package ui - -// #include -// #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/entry.go b/entry.go deleted file mode 100644 index ee14fc8..0000000 --- a/entry.go +++ /dev/null @@ -1,125 +0,0 @@ -// 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/fontbutton.go b/fontbutton.go deleted file mode 100644 index 4087794..0000000 --- a/fontbutton.go +++ /dev/null @@ -1,4 +0,0 @@ -// 20 april 2016 -package ui - -// TODO diff --git a/group.go b/group.go deleted file mode 100644 index 0f9b99a..0000000 --- a/group.go +++ /dev/null @@ -1,117 +0,0 @@ -// 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/label.go b/label.go deleted file mode 100644 index e143d50..0000000 --- a/label.go +++ /dev/null @@ -1,85 +0,0 @@ -// 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/link_darwin_amd64.go b/link_darwin_amd64.go deleted file mode 100644 index 687a62a..0000000 --- a/link_darwin_amd64.go +++ /dev/null @@ -1,7 +0,0 @@ -// 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/link_linux_386.go b/link_linux_386.go deleted file mode 100644 index 00a0598..0000000 --- a/link_linux_386.go +++ /dev/null @@ -1,10 +0,0 @@ -// +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/link_linux_amd64.go b/link_linux_amd64.go deleted file mode 100644 index a1b2b3b..0000000 --- a/link_linux_amd64.go +++ /dev/null @@ -1,10 +0,0 @@ -// +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/link_windows_386.go b/link_windows_386.go deleted file mode 100644 index d617a20..0000000 --- a/link_windows_386.go +++ /dev/null @@ -1,8 +0,0 @@ -// 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/link_windows_amd64.go b/link_windows_amd64.go deleted file mode 100644 index 73c94af..0000000 --- a/link_windows_amd64.go +++ /dev/null @@ -1,8 +0,0 @@ -// 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/main.go b/main.go deleted file mode 100644 index dd1443a..0000000 --- a/main.go +++ /dev/null @@ -1,138 +0,0 @@ -// 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/progressbar.go b/progressbar.go deleted file mode 100644 index ef5116c..0000000 --- a/progressbar.go +++ /dev/null @@ -1,77 +0,0 @@ -// 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/radiobuttons.go b/radiobuttons.go deleted file mode 100644 index b8399ea..0000000 --- a/radiobuttons.go +++ /dev/null @@ -1,77 +0,0 @@ -// 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/separator.go b/separator.go deleted file mode 100644 index 5f41891..0000000 --- a/separator.go +++ /dev/null @@ -1,68 +0,0 @@ -// 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/slider.go b/slider.go deleted file mode 100644 index 99b8d2c..0000000 --- a/slider.go +++ /dev/null @@ -1,108 +0,0 @@ -// 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/spinbox.go b/spinbox.go deleted file mode 100644 index 5e5e43f..0000000 --- a/spinbox.go +++ /dev/null @@ -1,111 +0,0 @@ -// 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/stddialogs.go b/stddialogs.go deleted file mode 100644 index 0ff8a01..0000000 --- a/stddialogs.go +++ /dev/null @@ -1,41 +0,0 @@ -// 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/tab.go b/tab.go deleted file mode 100644 index d18cecd..0000000 --- a/tab.go +++ /dev/null @@ -1,127 +0,0 @@ -// 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/ui.h b/ui.h index c7a8858..253cd2f 100644 --- a/ui.h +++ b/ui.h @@ -2,6 +2,11 @@ // TODO add a uiVerifyControlType() function that can be used by control implementations to verify controls +// TODOs +// - make getters that return whether something exists accept a NULL pointer to discard the value (and thus only return that the thing exists?) +// - const-correct everything +// - normalize documentation between typedefs and structs + #ifndef __LIBUI_UI_H__ #define __LIBUI_UI_H__ @@ -32,6 +37,14 @@ extern "C" { // This comes from Go's math.Pi, which in turn comes from http://oeis.org/A000796. #define uiPi 3.14159265358979323846264338327950288419716939937510582097494459 +// TODO uiBool? + +// uiForEach represents the return value from one of libui's various ForEach functions. +_UI_ENUM(uiForEach) { + uiForEachContinue, + uiForEachStop, +}; + typedef struct uiInitOptions uiInitOptions; struct uiInitOptions { @@ -43,11 +56,20 @@ _UI_EXTERN void uiUninit(void); _UI_EXTERN void uiFreeInitError(const char *err); _UI_EXTERN void uiMain(void); +_UI_EXTERN void uiMainSteps(void); _UI_EXTERN int uiMainStep(int wait); _UI_EXTERN void uiQuit(void); _UI_EXTERN void uiQueueMain(void (*f)(void *data), void *data); +// TODO standardize the looping behavior return type, either with some enum or something, and the test expressions throughout the code +// TODO figure out what to do about looping and the exact point that the timer is rescheduled so we can document it; see https://github.com/andlabs/libui/pull/277 +// TODO (also in the above link) document that this cannot be called from any thread, unlike uiQueueMain() +// TODO document that the minimum exact timing, either accuracy (timer burst, etc.) or granularity (15ms on Windows, etc.), is OS-defined +// TODO also figure out how long until the initial tick is registered on all platforms to document +// TODO also add a comment about how useful this could be in bindings, depending on the language being bound to +_UI_EXTERN void uiTimer(int milliseconds, int (*f)(void *data), void *data); + _UI_EXTERN void uiOnShouldQuit(int (*f)(void *data), void *data); _UI_EXTERN void uiFreeText(char *text); @@ -97,7 +119,14 @@ typedef struct uiWindow uiWindow; #define uiWindow(this) ((uiWindow *) (this)) _UI_EXTERN char *uiWindowTitle(uiWindow *w); _UI_EXTERN void uiWindowSetTitle(uiWindow *w, const char *title); +_UI_EXTERN void uiWindowContentSize(uiWindow *w, int *width, int *height); +_UI_EXTERN void uiWindowSetContentSize(uiWindow *w, int width, int height); +_UI_EXTERN int uiWindowFullscreen(uiWindow *w); +_UI_EXTERN void uiWindowSetFullscreen(uiWindow *w, int fullscreen); +_UI_EXTERN void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data); _UI_EXTERN void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *w, void *data), void *data); +_UI_EXTERN int uiWindowBorderless(uiWindow *w); +_UI_EXTERN void uiWindowSetBorderless(uiWindow *w, int borderless); _UI_EXTERN void uiWindowSetChild(uiWindow *w, uiControl *child); _UI_EXTERN int uiWindowMargined(uiWindow *w); _UI_EXTERN void uiWindowSetMargined(uiWindow *w, int margined); @@ -113,7 +142,7 @@ _UI_EXTERN uiButton *uiNewButton(const char *text); typedef struct uiBox uiBox; #define uiBox(this) ((uiBox *) (this)) _UI_EXTERN void uiBoxAppend(uiBox *b, uiControl *child, int stretchy); -_UI_EXTERN void uiBoxDelete(uiBox *b, uintmax_t index); +_UI_EXTERN void uiBoxDelete(uiBox *b, int index); _UI_EXTERN int uiBoxPadded(uiBox *b); _UI_EXTERN void uiBoxSetPadded(uiBox *b, int padded); _UI_EXTERN uiBox *uiNewHorizontalBox(void); @@ -136,6 +165,8 @@ _UI_EXTERN void uiEntryOnChanged(uiEntry *e, void (*f)(uiEntry *e, void *data), _UI_EXTERN int uiEntryReadOnly(uiEntry *e); _UI_EXTERN void uiEntrySetReadOnly(uiEntry *e, int readonly); _UI_EXTERN uiEntry *uiNewEntry(void); +_UI_EXTERN uiEntry *uiNewPasswordEntry(void); +_UI_EXTERN uiEntry *uiNewSearchEntry(void); typedef struct uiLabel uiLabel; #define uiLabel(this) ((uiLabel *) (this)) @@ -146,11 +177,11 @@ _UI_EXTERN uiLabel *uiNewLabel(const char *text); typedef struct uiTab uiTab; #define uiTab(this) ((uiTab *) (this)) _UI_EXTERN void uiTabAppend(uiTab *t, const char *name, uiControl *c); -_UI_EXTERN void uiTabInsertAt(uiTab *t, const char *name, uintmax_t before, uiControl *c); -_UI_EXTERN void uiTabDelete(uiTab *t, uintmax_t index); -_UI_EXTERN uintmax_t uiTabNumPages(uiTab *t); -_UI_EXTERN int uiTabMargined(uiTab *t, uintmax_t page); -_UI_EXTERN void uiTabSetMargined(uiTab *t, uintmax_t page, int margined); +_UI_EXTERN void uiTabInsertAt(uiTab *t, const char *name, int before, uiControl *c); +_UI_EXTERN void uiTabDelete(uiTab *t, int index); +_UI_EXTERN int uiTabNumPages(uiTab *t); +_UI_EXTERN int uiTabMargined(uiTab *t, int page); +_UI_EXTERN void uiTabSetMargined(uiTab *t, int page, int margined); _UI_EXTERN uiTab *uiNewTab(void); typedef struct uiGroup uiGroup; @@ -169,33 +200,34 @@ _UI_EXTERN uiGroup *uiNewGroup(const char *title); typedef struct uiSpinbox uiSpinbox; #define uiSpinbox(this) ((uiSpinbox *) (this)) -_UI_EXTERN intmax_t uiSpinboxValue(uiSpinbox *s); -_UI_EXTERN void uiSpinboxSetValue(uiSpinbox *s, intmax_t value); +_UI_EXTERN int uiSpinboxValue(uiSpinbox *s); +_UI_EXTERN void uiSpinboxSetValue(uiSpinbox *s, int value); _UI_EXTERN void uiSpinboxOnChanged(uiSpinbox *s, void (*f)(uiSpinbox *s, void *data), void *data); -_UI_EXTERN uiSpinbox *uiNewSpinbox(intmax_t min, intmax_t max); +_UI_EXTERN uiSpinbox *uiNewSpinbox(int min, int max); typedef struct uiSlider uiSlider; #define uiSlider(this) ((uiSlider *) (this)) -_UI_EXTERN intmax_t uiSliderValue(uiSlider *s); -_UI_EXTERN void uiSliderSetValue(uiSlider *s, intmax_t value); +_UI_EXTERN int uiSliderValue(uiSlider *s); +_UI_EXTERN void uiSliderSetValue(uiSlider *s, int value); _UI_EXTERN void uiSliderOnChanged(uiSlider *s, void (*f)(uiSlider *s, void *data), void *data); -_UI_EXTERN uiSlider *uiNewSlider(intmax_t min, intmax_t max); +_UI_EXTERN uiSlider *uiNewSlider(int min, int max); typedef struct uiProgressBar uiProgressBar; #define uiProgressBar(this) ((uiProgressBar *) (this)) -// TODO uiProgressBarValue() +_UI_EXTERN int uiProgressBarValue(uiProgressBar *p); _UI_EXTERN void uiProgressBarSetValue(uiProgressBar *p, int n); _UI_EXTERN uiProgressBar *uiNewProgressBar(void); typedef struct uiSeparator uiSeparator; #define uiSeparator(this) ((uiSeparator *) (this)) _UI_EXTERN uiSeparator *uiNewHorizontalSeparator(void); +_UI_EXTERN uiSeparator *uiNewVerticalSeparator(void); typedef struct uiCombobox uiCombobox; #define uiCombobox(this) ((uiCombobox *) (this)) _UI_EXTERN void uiComboboxAppend(uiCombobox *c, const char *text); -_UI_EXTERN intmax_t uiComboboxSelected(uiCombobox *c); -_UI_EXTERN void uiComboboxSetSelected(uiCombobox *c, intmax_t n); +_UI_EXTERN int uiComboboxSelected(uiCombobox *c); +_UI_EXTERN void uiComboboxSetSelected(uiCombobox *c, int n); _UI_EXTERN void uiComboboxOnSelected(uiCombobox *c, void (*f)(uiCombobox *c, void *data), void *data); _UI_EXTERN uiCombobox *uiNewCombobox(void); @@ -211,10 +243,18 @@ _UI_EXTERN uiEditableCombobox *uiNewEditableCombobox(void); typedef struct uiRadioButtons uiRadioButtons; #define uiRadioButtons(this) ((uiRadioButtons *) (this)) _UI_EXTERN void uiRadioButtonsAppend(uiRadioButtons *r, const char *text); +_UI_EXTERN int uiRadioButtonsSelected(uiRadioButtons *r); +_UI_EXTERN void uiRadioButtonsSetSelected(uiRadioButtons *r, int n); +_UI_EXTERN void uiRadioButtonsOnSelected(uiRadioButtons *r, void (*f)(uiRadioButtons *, void *), void *data); _UI_EXTERN uiRadioButtons *uiNewRadioButtons(void); +struct tm; typedef struct uiDateTimePicker uiDateTimePicker; #define uiDateTimePicker(this) ((uiDateTimePicker *) (this)) +// TODO document that tm_wday and tm_yday are undefined, and tm_isdst should be -1 +_UI_EXTERN void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time); +_UI_EXTERN void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time); +_UI_EXTERN void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data); _UI_EXTERN uiDateTimePicker *uiNewDateTimePicker(void); _UI_EXTERN uiDateTimePicker *uiNewDatePicker(void); _UI_EXTERN uiDateTimePicker *uiNewTimePicker(void); @@ -273,15 +313,38 @@ struct uiAreaHandler { int (*KeyEvent)(uiAreaHandler *, uiArea *, uiAreaKeyEvent *); }; +// TODO RTL layouts? +// TODO reconcile edge and corner naming +_UI_ENUM(uiWindowResizeEdge) { + uiWindowResizeEdgeLeft, + uiWindowResizeEdgeTop, + uiWindowResizeEdgeRight, + uiWindowResizeEdgeBottom, + uiWindowResizeEdgeTopLeft, + uiWindowResizeEdgeTopRight, + uiWindowResizeEdgeBottomLeft, + uiWindowResizeEdgeBottomRight, + // TODO have one for keyboard resizes? + // TODO GDK doesn't seem to have any others, including for keyboards... + // TODO way to bring up the system menu instead? +}; + #define uiArea(this) ((uiArea *) (this)) // TODO give a better name // TODO document the types of width and height -_UI_EXTERN void uiAreaSetSize(uiArea *a, intmax_t width, intmax_t height); +_UI_EXTERN void uiAreaSetSize(uiArea *a, int width, int height); // TODO uiAreaQueueRedraw() _UI_EXTERN void uiAreaQueueRedrawAll(uiArea *a); _UI_EXTERN void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height); +// TODO document these can only be called within Mouse() handlers +// TODO should these be allowed on scrolling areas? +// TODO decide which mouse events should be accepted; Down is the only one guaranteed to work right now +// TODO what happens to events after calling this up to and including the next mouse up? +// TODO release capture? +_UI_EXTERN void uiAreaBeginUserWindowMove(uiArea *a); +_UI_EXTERN void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge); _UI_EXTERN uiArea *uiNewArea(uiAreaHandler *ah); -_UI_EXTERN uiArea *uiNewScrollingArea(uiAreaHandler *ah, intmax_t width, intmax_t height); +_UI_EXTERN uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height); struct uiAreaDrawParams { uiDrawContext *Context; @@ -436,90 +499,514 @@ _UI_EXTERN void uiDrawClip(uiDrawContext *c, uiDrawPath *path); _UI_EXTERN void uiDrawSave(uiDrawContext *c); _UI_EXTERN void uiDrawRestore(uiDrawContext *c); -// TODO manage the use of Text, Font, and TextFont, and of the uiDrawText prefix in general +// uiAttribute stores information about an attribute in a +// uiAttributedString. +// +// You do not create uiAttributes directly; instead, you create a +// uiAttribute of a given type using the specialized constructor +// functions. For every Unicode codepoint in the uiAttributedString, +// at most one value of each attribute type can be applied. +// +// uiAttributes are immutable and the uiAttributedString takes +// ownership of the uiAttribute object once assigned, copying its +// contents as necessary. +typedef struct uiAttribute uiAttribute; + +// @role uiAttribute destructor +// uiFreeAttribute() frees a uiAttribute. You generally do not need to +// call this yourself, as uiAttributedString does this for you. In fact, +// it is an error to call this function on a uiAttribute that has been +// given to a uiAttributedString. You can call this, however, if you +// created a uiAttribute that you aren't going to use later. +_UI_EXTERN void uiFreeAttribute(uiAttribute *a); + +// uiAttributeType holds the possible uiAttribute types that may be +// returned by uiAttributeGetType(). Refer to the documentation for +// each type's constructor function for details on each type. +_UI_ENUM(uiAttributeType) { + uiAttributeTypeFamily, + uiAttributeTypeSize, + uiAttributeTypeWeight, + uiAttributeTypeItalic, + uiAttributeTypeStretch, + uiAttributeTypeColor, + uiAttributeTypeBackground, + uiAttributeTypeUnderline, + uiAttributeTypeUnderlineColor, + uiAttributeTypeFeatures, +}; -///// TODO -typedef struct uiDrawFontFamilies uiDrawFontFamilies; +// uiAttributeGetType() returns the type of a. +// TODO I don't like this name +_UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); + +// uiNewFamilyAttribute() creates a new uiAttribute that changes the +// font family of the text it is applied to. family is copied; you do not +// need to keep it alive after uiNewFamilyAttribute() returns. Font +// family names are case-insensitive. +_UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); + +// uiAttributeFamily() returns the font family stored in a. The +// returned string is owned by a. It is an error to call this on a +// uiAttribute that does not hold a font family. +_UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); + +// uiNewSizeAttribute() creates a new uiAttribute that changes the +// size of the text it is applied to, in typographical points. +_UI_EXTERN uiAttribute *uiNewSizeAttribute(double size); + +// uiAttributeSize() returns the font size stored in a. It is an error to +// call this on a uiAttribute that does not hold a font size. +_UI_EXTERN double uiAttributeSize(const uiAttribute *a); + +// uiTextWeight represents possible text weights. These roughly +// map to the OSx2 text weight field of TrueType and OpenType +// fonts, or to CSS weight numbers. The named constants are +// nominal values; the actual values may vary by font and by OS, +// though this isn't particularly likely. Any value between +// uiTextWeightMinimum and uiDrawTextWeightMaximum, +// inclusive, is allowed. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" weights be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Black. libui does not do this, even on Windows (because the +// DirectWrite API libui uses on Windows does not do this); to +// specify Arial Black, use family Arial and weight uiTextWeightBlack. +_UI_ENUM(uiTextWeight) { + uiTextWeightMinimum = 0, + uiTextWeightThin = 100, + uiTextWeightUltraLight = 200, + uiTextWeightLight = 300, + uiTextWeightBook = 350, + uiTextWeightNormal = 400, + uiTextWeightMedium = 500, + uiTextWeightSemiBold = 600, + uiTextWeightBold = 700, + uiTextWeightUltraBold = 800, + uiTextWeightHeavy = 900, + uiTextWeightUltraHeavy = 950, + uiTextWeightMaximum = 1000, +}; -_UI_EXTERN uiDrawFontFamilies *uiDrawListFontFamilies(void); -_UI_EXTERN uintmax_t uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff); -_UI_EXTERN char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, uintmax_t n); -_UI_EXTERN void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff); -///// END TODO +// uiNewWeightAttribute() creates a new uiAttribute that changes the +// weight of the text it is applied to. It is an error to specify a weight +// outside the range [uiTextWeightMinimum, +// uiTextWeightMaximum]. +_UI_EXTERN uiAttribute *uiNewWeightAttribute(uiTextWeight weight); + +// uiAttributeWeight() returns the font weight stored in a. It is an error +// to call this on a uiAttribute that does not hold a font weight. +_UI_EXTERN uiTextWeight uiAttributeWeight(const uiAttribute *a); + +// uiTextItalic represents possible italic modes for a font. Italic +// represents "true" italics where the slanted glyphs have custom +// shapes, whereas oblique represents italics that are merely slanted +// versions of the normal glyphs. Most fonts usually have one or the +// other. +_UI_ENUM(uiTextItalic) { + uiTextItalicNormal, + uiTextItalicOblique, + uiTextItalicItalic, +}; -typedef struct uiDrawTextLayout uiDrawTextLayout; -typedef struct uiDrawTextFont uiDrawTextFont; -typedef struct uiDrawTextFontDescriptor uiDrawTextFontDescriptor; -typedef struct uiDrawTextFontMetrics uiDrawTextFontMetrics; - -_UI_ENUM(uiDrawTextWeight) { - uiDrawTextWeightThin, - uiDrawTextWeightUltraLight, - uiDrawTextWeightLight, - uiDrawTextWeightBook, - uiDrawTextWeightNormal, - uiDrawTextWeightMedium, - uiDrawTextWeightSemiBold, - uiDrawTextWeightBold, - uiDrawTextWeightUtraBold, - uiDrawTextWeightHeavy, - uiDrawTextWeightUltraHeavy, -}; - -_UI_ENUM(uiDrawTextItalic) { - uiDrawTextItalicNormal, - uiDrawTextItalicOblique, - uiDrawTextItalicItalic, -}; - -_UI_ENUM(uiDrawTextStretch) { - uiDrawTextStretchUltraCondensed, - uiDrawTextStretchExtraCondensed, - uiDrawTextStretchCondensed, - uiDrawTextStretchSemiCondensed, - uiDrawTextStretchNormal, - uiDrawTextStretchSemiExpanded, - uiDrawTextStretchExpanded, - uiDrawTextStretchExtraExpanded, - uiDrawTextStretchUltraExpanded, -}; - -struct uiDrawTextFontDescriptor { - const char *Family; - double Size; - uiDrawTextWeight Weight; - uiDrawTextItalic Italic; - uiDrawTextStretch Stretch; +// uiNewItalicAttribute() creates a new uiAttribute that changes the +// italic mode of the text it is applied to. It is an error to specify an +// italic mode not specified in uiTextItalic. +_UI_EXTERN uiAttribute *uiNewItalicAttribute(uiTextItalic italic); + +// uiAttributeItalic() returns the font italic mode stored in a. It is an +// error to call this on a uiAttribute that does not hold a font italic +// mode. +_UI_EXTERN uiTextItalic uiAttributeItalic(const uiAttribute *a); + +// uiTextStretch represents possible stretches (also called "widths") +// of a font. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" stretches be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Condensed. libui does not do this, even on Windows (because +// the DirectWrite API libui uses on Windows does not do this); to +// specify Arial Condensed, use family Arial and stretch +// uiTextStretchCondensed. +_UI_ENUM(uiTextStretch) { + uiTextStretchUltraCondensed, + uiTextStretchExtraCondensed, + uiTextStretchCondensed, + uiTextStretchSemiCondensed, + uiTextStretchNormal, + uiTextStretchSemiExpanded, + uiTextStretchExpanded, + uiTextStretchExtraExpanded, + uiTextStretchUltraExpanded, }; -struct uiDrawTextFontMetrics { - double Ascent; - double Descent; - double Leading; - // TODO do these two mean the same across all platforms? - double UnderlinePos; - double UnderlineThickness; +// uiNewStretchAttribute() creates a new uiAttribute that changes the +// stretch of the text it is applied to. It is an error to specify a strech +// not specified in uiTextStretch. +_UI_EXTERN uiAttribute *uiNewStretchAttribute(uiTextStretch stretch); + +// uiAttributeStretch() returns the font stretch stored in a. It is an +// error to call this on a uiAttribute that does not hold a font stretch. +_UI_EXTERN uiTextStretch uiAttributeStretch(const uiAttribute *a); + +// uiNewColorAttribute() creates a new uiAttribute that changes the +// color of the text it is applied to. It is an error to specify an invalid +// color. +_UI_EXTERN uiAttribute *uiNewColorAttribute(double r, double g, double b, double a); + +// uiAttributeColor() returns the text color stored in a. It is an +// error to call this on a uiAttribute that does not hold a text color. +_UI_EXTERN void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha); + +// uiNewBackgroundAttribute() creates a new uiAttribute that +// changes the background color of the text it is applied to. It is an +// error to specify an invalid color. +_UI_EXTERN uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a); + +// TODO reuse uiAttributeColor() for background colors, or make a new function... + +// uiUnderline specifies a type of underline to use on text. +_UI_ENUM(uiUnderline) { + uiUnderlineNone, + uiUnderlineSingle, + uiUnderlineDouble, + uiUnderlineSuggestion, // wavy or dotted underlines used for spelling/grammar checkers }; -_UI_EXTERN uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc); -_UI_EXTERN void uiDrawFreeTextFont(uiDrawTextFont *font); -_UI_EXTERN uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font); -_UI_EXTERN void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc); -// TODO make copy with given attributes methods? -// TODO yuck this name -_UI_EXTERN void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics); +// uiNewUnderlineAttribute() creates a new uiAttribute that changes +// the type of underline on the text it is applied to. It is an error to +// specify an underline type not specified in uiUnderline. +_UI_EXTERN uiAttribute *uiNewUnderlineAttribute(uiUnderline u); + +// uiAttributeUnderline() returns the underline type stored in a. It is +// an error to call this on a uiAttribute that does not hold an underline +// style. +_UI_EXTERN uiUnderline uiAttributeUnderline(const uiAttribute *a); + +// uiUnderlineColor specifies the color of any underline on the text it +// is applied to, regardless of the type of underline. In addition to +// being able to specify a custom color, you can explicitly specify +// platform-specific colors for suggestion underlines; to use them +// correctly, pair them with uiUnderlineSuggestion (though they can +// be used on other types of underline as well). +// +// If an underline type is applied but no underline color is +// specified, the text color is used instead. If an underline color +// is specified without an underline type, the underline color +// attribute is ignored, but not removed from the uiAttributedString. +_UI_ENUM(uiUnderlineColor) { + uiUnderlineColorCustom, + uiUnderlineColorSpelling, + uiUnderlineColorGrammar, + uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on macOS or in Microsoft Office +}; -// TODO initial line spacing? and what about leading? -_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width); -_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *layout); -// TODO get width -_UI_EXTERN void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width); -_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height); +// uiNewUnderlineColorAttribute() creates a new uiAttribute that +// changes the color of the underline on the text it is applied to. +// It is an error to specify an underline color not specified in +// uiUnderlineColor. +// +// If the specified color type is uiUnderlineColorCustom, it is an +// error to specify an invalid color value. Otherwise, the color values +// are ignored and should be specified as zero. +_UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a); + +// uiAttributeUnderlineColor() returns the underline color stored in +// a. It is an error to call this on a uiAttribute that does not hold an +// underline color. +_UI_EXTERN void uiAttributeUnderlineColor(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); + +// uiOpenTypeFeatures represents a set of OpenType feature +// tag-value pairs, for applying OpenType features to text. +// OpenType feature tags are four-character codes defined by +// OpenType that cover things from design features like small +// caps and swashes to language-specific glyph shapes and +// beyond. Each tag may only appear once in any given +// uiOpenTypeFeatures instance. Each value is a 32-bit integer, +// often used as a Boolean flag, but sometimes as an index to choose +// a glyph shape to use. +// +// If a font does not support a certain feature, that feature will be +// ignored. (TODO verify this on all OSs) +// +// See the OpenType specification at +// https://www.microsoft.com/typography/otspec/featuretags.htm +// for the complete list of available features, information on specific +// features, and how to use them. +// TODO invalid features +typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; + +// uiOpenTypeFeaturesForEachFunc is the type of the function +// invoked by uiOpenTypeFeaturesForEach() for every OpenType +// feature in otf. Refer to that function's documentation for more +// details. +typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); + +// @role uiOpenTypeFeatures constructor +// uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures +// instance, with no tags yet added. +_UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); + +// @role uiOpenTypeFeatures destructor +// uiFreeOpenTypeFeatures() frees otf. +_UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); + +// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. +// Changing one will not affect the other. +_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); + +// uiOpenTypeFeaturesAdd() adds the given feature tag and value +// to otf. The feature tag is specified by a, b, c, and d. If there is +// already a value associated with the specified tag in otf, the old +// value is removed. +_UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); + +// uiOpenTypeFeaturesRemove() removes the given feature tag +// and value from otf. If the tag is not present in otf, +// uiOpenTypeFeaturesRemove() does nothing. +_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); + +// uiOpenTypeFeaturesGet() determines whether the given feature +// tag is present in otf. If it is, *value is set to the tag's value and +// nonzero is returned. Otherwise, zero is returned. +// +// Note that if uiOpenTypeFeaturesGet() returns zero, value isn't +// changed. This is important: if a feature is not present in a +// uiOpenTypeFeatures, the feature is NOT treated as if its +// value was zero anyway. Script-specific font shaping rules and +// font-specific feature settings may use a different default value +// for a feature. You should likewise not treat a missing feature as +// having a value of zero either. Instead, a missing feature should +// be treated as having some unspecified default value. +_UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); + +// uiOpenTypeFeaturesForEach() executes f for every tag-value +// pair in otf. The enumeration order is unspecified. You cannot +// modify otf while uiOpenTypeFeaturesForEach() is running. +_UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); + +// uiNewFeaturesAttribute() creates a new uiAttribute that changes +// the font family of the text it is applied to. otf is copied; you may +// free it after uiNewFeaturesAttribute() returns. +_UI_EXTERN uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf); + +// uiAttributeFeatures() returns the OpenType features stored in a. +// The returned uiOpenTypeFeatures object is owned by a. It is an +// error to call this on a uiAttribute that does not hold OpenType +// features. +_UI_EXTERN const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a); + +// uiAttributedString represents a string of UTF-8 text that can +// optionally be embellished with formatting attributes. libui +// provides the list of formatting attributes, which cover common +// formatting traits like boldface and color as well as advanced +// typographical features provided by OpenType like superscripts +// and small caps. These attributes can be combined in a variety of +// ways. +// +// Attributes are applied to runs of Unicode codepoints in the string. +// Zero-length runs are elided. Consecutive runs that have the same +// attribute type and value are merged. Each attribute is independent +// of each other attribute; overlapping attributes of different types +// do not split each other apart, but different values of the same +// attribute type do. +// +// The empty string can also be represented by uiAttributedString, +// but because of the no-zero-length-attribute rule, it will not have +// attributes. +// +// A uiAttributedString takes ownership of all attributes given to +// it, as it may need to duplicate or delete uiAttribute objects at +// any time. By extension, when you free a uiAttributedString, +// all uiAttributes within will also be freed. Each method will +// describe its own rules in more details. +// +// In addition, uiAttributedString provides facilities for moving +// between grapheme clusters, which represent a character +// from the point of view of the end user. The cursor of a text editor +// is always placed on a grapheme boundary, so you can use these +// features to move the cursor left or right by one "character". +// TODO does uiAttributedString itself need this +// +// uiAttributedString does not provide enough information to be able +// to draw itself onto a uiDrawContext or respond to user actions. +// In order to do that, you'll need to use a uiDrawTextLayout, which +// is built from the combination of a uiAttributedString and a set of +// layout-specific properties. +typedef struct uiAttributedString uiAttributedString; + +// uiAttributedStringForEachAttributeFunc is the type of the function +// invoked by uiAttributedStringForEachAttribute() for every +// attribute in s. Refer to that function's documentation for more +// details. +typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttribute *a, size_t start, size_t end, void *data); + +// @role uiAttributedString constructor +// uiNewAttributedString() creates a new uiAttributedString from +// initialString. The string will be entirely unattributed. +_UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); + +// @role uiAttributedString destructor +// uiFreeAttributedString() destroys the uiAttributedString s. +// It will also free all uiAttributes within. +_UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); + +// uiAttributedStringString() returns the textual content of s as a +// '\0'-terminated UTF-8 string. The returned pointer is valid until +// the next change to the textual content of s. +_UI_EXTERN const char *uiAttributedStringString(const uiAttributedString *s); + +// uiAttributedStringLength() returns the number of UTF-8 bytes in +// the textual content of s, excluding the terminating '\0'. +_UI_EXTERN size_t uiAttributedStringLen(const uiAttributedString *s); + +// uiAttributedStringAppendUnattributed() adds the '\0'-terminated +// UTF-8 string str to the end of s. The new substring will be +// unattributed. +_UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); + +// uiAttributedStringInsertAtUnattributed() adds the '\0'-terminated +// UTF-8 string str to s at the byte position specified by at. The new +// substring will be unattributed; existing attributes will be moved +// along with their text. +_UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); + +// TODO add the Append and InsertAtExtendingAttributes functions +// TODO and add functions that take a string + length + +// uiAttributedStringDelete() deletes the characters and attributes of +// s in the byte range [start, end). +_UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); + +// TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit + +// uiAttributedStringSetAttribute() sets a in the byte range [start, end) +// of s. Any existing attributes in that byte range of the same type are +// removed. s takes ownership of a; you should not use it after +// uiAttributedStringSetAttribute() returns. +_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute *a, size_t start, size_t end); + +// uiAttributedStringForEachAttribute() enumerates all the +// uiAttributes in s. It is an error to modify s in f. Within f, s still +// owns the attribute; you can neither free it nor save it for later +// use. +// TODO reword the above for consistency (TODO and find out what I meant by that) +// TODO define an enumeration order (or mark it as undefined); also define how consecutive runs of identical attributes are handled here and sync with the definition of uiAttributedString itself +_UI_EXTERN void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); + +// uiFontDescriptor provides a complete description of a font where +// one is needed. Currently, this means as the default font of a +// uiDrawTextLayout and as the data returned by uiFontButton. +// All the members operate like the respective uiAttributes. +typedef struct uiFontDescriptor uiFontDescriptor; + +struct uiFontDescriptor { + // TODO const-correct this or figure out how to deal with this when getting a value + char *Family; + double Size; + uiTextWeight Weight; + uiTextItalic Italic; + uiTextStretch Stretch; +}; -// and the attributes that you can set on a text layout -_UI_EXTERN void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, intmax_t startChar, intmax_t endChar, double r, double g, double b, double a); +// uiDrawTextLayout is a concrete representation of a +// uiAttributedString that can be displayed in a uiDrawContext. +// It includes information important for the drawing of a block of +// text, including the bounding box to wrap the text within, the +// alignment of lines of text within that box, areas to mark as +// being selected, and other things. +// +// Unlike uiAttributedString, the content of a uiDrawTextLayout is +// immutable once it has been created. +// +// TODO talk about OS-specific differences with text drawing that libui can't account for... +typedef struct uiDrawTextLayout uiDrawTextLayout; -_UI_EXTERN void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout); +// uiDrawTextAlign specifies the alignment of lines of text in a +// uiDrawTextLayout. +// TODO should this really have Draw in the name? +_UI_ENUM(uiDrawTextAlign) { + uiDrawTextAlignLeft, + uiDrawTextAlignCenter, + uiDrawTextAlignRight, +}; + +// uiDrawTextLayoutParams describes a uiDrawTextLayout. +// DefaultFont is used to render any text that is not attributed +// sufficiently in String. Width determines the width of the bounding +// box of the text; the height is determined automatically. +typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; + +// TODO const-correct this somehow +struct uiDrawTextLayoutParams { + uiAttributedString *String; + uiFontDescriptor *DefaultFont; + double Width; + uiDrawTextAlign Align; +}; + +// @role uiDrawTextLayout constructor +// uiDrawNewTextLayout() creates a new uiDrawTextLayout from +// the given parameters. +// +// TODO +// - allow creating a layout out of a substring +// - allow marking compositon strings +// - allow marking selections, even after creation +// - add the following functions: +// - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) +// - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) +// - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) +// - some function to fix up a range (for text editing) +_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *params); + +// @role uiDrawFreeTextLayout destructor +// uiDrawFreeTextLayout() frees tl. The underlying +// uiAttributedString is not freed. +_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); + +// uiDrawText() draws tl in c with the top-left point of tl at (x, y). +_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); + +// uiDrawTextLayoutExtents() returns the width and height of tl +// in width and height. The returned width may be smaller than +// the width passed into uiDrawNewTextLayout() depending on +// how the text in tl is wrapped. Therefore, you can use this +// function to get the actual size of the text layout. +_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); + +// TODO metrics functions + +// TODO number of lines visible for clipping rect, range visible for clipping rect? + +// uiFontButton is a button that allows users to choose a font when they click on it. +typedef struct uiFontButton uiFontButton; +#define uiFontButton(this) ((uiFontButton *) (this)) +// uiFontButtonFont() returns the font currently selected in the uiFontButton in desc. +// uiFontButtonFont() allocates resources in desc; when you are done with the font, call uiFreeFontButtonFont() to release them. +// uiFontButtonFont() does not allocate desc itself; you must do so. +// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? +_UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc); +// TOOD SetFont, mechanics +// uiFontButtonOnChanged() sets the function that is called when the font in the uiFontButton is changed. +_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); +// uiNewFontButton() creates a new uiFontButton. The default font selected into the uiFontButton is OS-defined. +_UI_EXTERN uiFontButton *uiNewFontButton(void); +// uiFreeFontButtonFont() frees resources allocated in desc by uiFontButtonFont(). +// After calling uiFreeFontButtonFont(), the contents of desc should be assumed to be undefined (though since you allocate desc itself, you can safely reuse desc for other font descriptors). +// Calling uiFreeFontButtonFont() on a uiFontDescriptor not returned by uiFontButtonFont() results in undefined behavior. +_UI_EXTERN void uiFreeFontButtonFont(uiFontDescriptor *desc); _UI_ENUM(uiModifiers) { uiModifierCtrl = 1 << 0, @@ -538,10 +1025,10 @@ struct uiAreaMouseEvent { double AreaWidth; double AreaHeight; - uintmax_t Down; - uintmax_t Up; + int Down; + int Up; - uintmax_t Count; + int Count; uiModifiers Modifiers; @@ -600,14 +1087,6 @@ struct uiAreaKeyEvent { int Up; }; -typedef struct uiFontButton uiFontButton; -#define uiFontButton(this) ((uiFontButton *) (this)) -// TODO document this returns a new font -_UI_EXTERN uiDrawTextFont *uiFontButtonFont(uiFontButton *b); -// TOOD SetFont, mechanics -_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); -_UI_EXTERN uiFontButton *uiNewFontButton(void); - typedef struct uiColorButton uiColorButton; #define uiColorButton(this) ((uiColorButton *) (this)) _UI_EXTERN void uiColorButtonColor(uiColorButton *b, double *r, double *g, double *bl, double *a); @@ -615,6 +1094,367 @@ _UI_EXTERN void uiColorButtonSetColor(uiColorButton *b, double r, double g, doub _UI_EXTERN void uiColorButtonOnChanged(uiColorButton *b, void (*f)(uiColorButton *, void *), void *data); _UI_EXTERN uiColorButton *uiNewColorButton(void); +typedef struct uiForm uiForm; +#define uiForm(this) ((uiForm *) (this)) +_UI_EXTERN void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy); +_UI_EXTERN void uiFormDelete(uiForm *f, int index); +_UI_EXTERN int uiFormPadded(uiForm *f); +_UI_EXTERN void uiFormSetPadded(uiForm *f, int padded); +_UI_EXTERN uiForm *uiNewForm(void); + +_UI_ENUM(uiAlign) { + uiAlignFill, + uiAlignStart, + uiAlignCenter, + uiAlignEnd, +}; + +_UI_ENUM(uiAt) { + uiAtLeading, + uiAtTop, + uiAtTrailing, + uiAtBottom, +}; + +typedef struct uiGrid uiGrid; +#define uiGrid(this) ((uiGrid *) (this)) +_UI_EXTERN void uiGridAppend(uiGrid *g, uiControl *c, int left, int top, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign); +_UI_EXTERN void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign); +_UI_EXTERN int uiGridPadded(uiGrid *g); +_UI_EXTERN void uiGridSetPadded(uiGrid *g, int padded); +_UI_EXTERN uiGrid *uiNewGrid(void); + +// uiImage stores an image for display on screen. +// +// Images are built from one or more representations, each with the +// same aspect ratio but a different pixel size. libui automatically +// selects the most appropriate representation for drawing the image +// when it comes time to draw the image; what this means depends +// on the pixel density of the target context. Therefore, one can use +// uiImage to draw higher-detailed images on higher-density +// displays. The typical use cases are either: +// +// - have just a single representation, at which point all screens +// use the same image, and thus uiImage acts like a simple +// bitmap image, or +// - have two images, one at normal resolution and one at 2x +// resolution; this matches the current expectations of some +// desktop systems at the time of writing (mid-2018) +// +// uiImage is very simple: it only supports non-premultiplied 32-bit +// RGBA images, and libui does not provide any image file loading +// or image format conversion utilities on top of that. +typedef struct uiImage uiImage; + +// @role uiImage constructor +// uiNewImage creates a new uiImage with the given width and +// height. This width and height should be the size in points of the +// image in the device-independent case; typically this is the 1x size. +// TODO for all uiImage functions: use const void * for const correctness +_UI_EXTERN uiImage *uiNewImage(double width, double height); + +// @role uiImage destructor +// uiFreeImage frees the given image and all associated resources. +_UI_EXTERN void uiFreeImage(uiImage *i); + +// uiImageAppend adds a representation to the uiImage. +// pixels should point to a byte array of non-premultiplied pixels +// stored in [R G B A] order (so ((uint8_t *) pixels)[0] is the R of the +// first pixel and [3] is the A of the first pixel). pixelWidth and +// pixelHeight is the size *in pixels* of the image, and pixelStride is +// the number *of bytes* per row of the pixels array. Therefore, +// pixels itself must be at least byteStride * pixelHeight bytes long. +// TODO see if we either need the stride or can provide a way to get the OS-preferred stride (in cairo we do) +_UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride); + +// uiTableValue stores a value to be passed along uiTable and +// uiTableModel. +// +// You do not create uiTableValues directly; instead, you create a +// uiTableValue of a given type using the specialized constructor +// functions. +// +// uiTableValues are immutable and the uiTableModel and uiTable +// take ownership of the uiTableValue object once returned, copying +// its contents as necessary. +typedef struct uiTableValue uiTableValue; + +// @role uiTableValue destructor +// uiFreeTableValue() frees a uiTableValue. You generally do not +// need to call this yourself, as uiTable and uiTableModel do this +// for you. In fact, it is an error to call this function on a uiTableValue +// that has been given to a uiTable or uiTableModel. You can call this, +// however, if you created a uiTableValue that you aren't going to +// use later, or if you called a uiTableModelHandler method directly +// and thus never transferred ownership of the uiTableValue. +_UI_EXTERN void uiFreeTableValue(uiTableValue *v); + +// uiTableValueType holds the possible uiTableValue types that may +// be returned by uiTableValueGetType(). Refer to the documentation +// for each type's constructor function for details on each type. +// TODO actually validate these +_UI_ENUM(uiTableValueType) { + uiTableValueTypeString, + uiTableValueTypeImage, + uiTableValueTypeInt, + uiTableValueTypeColor, +}; + +// uiTableValueGetType() returns the type of v. +// TODO I don't like this name +_UI_EXTERN uiTableValueType uiTableValueGetType(const uiTableValue *v); + +// uiNewTableValueString() returns a new uiTableValue that contains +// str. str is copied; you do not need to keep it alive after +// uiNewTableValueString() returns. +_UI_EXTERN uiTableValue *uiNewTableValueString(const char *str); + +// uiTableValueString() returns the string stored in v. The returned +// string is owned by v. It is an error to call this on a uiTableValue +// that does not hold a string. +_UI_EXTERN const char *uiTableValueString(const uiTableValue *v); + +// uiNewTableValueImage() returns a new uiTableValue that contains +// the given uiImage. +// +// Unlike other similar constructors, uiNewTableValueImage() does +// NOT copy the image. This is because images are comparatively +// larger than the other objects in question. Therefore, you MUST +// keep the image alive as long as the returned uiTableValue is alive. +// As a general rule, if libui calls a uiTableModelHandler method, the +// uiImage is safe to free once any of your code is once again +// executed. +_UI_EXTERN uiTableValue *uiNewTableValueImage(uiImage *img); + +// uiTableValueImage() returns the uiImage stored in v. As these +// images are not owned by v, you should not assume anything +// about the lifetime of the image (unless you created the image, +// and thus control its lifetime). It is an error to call this on a +// uiTableValue that does not hold an image. +_UI_EXTERN uiImage *uiTableValueImage(const uiTableValue *v); + +// uiNewTableValueInt() returns a uiTableValue that stores the given +// int. This can be used both for boolean values (nonzero is true, as +// in C) or progresses (in which case the valid range is -1..100 +// inclusive). +_UI_EXTERN uiTableValue *uiNewTableValueInt(int i); + +// uiTableValueInt() returns the int stored in v. It is an error to call +// this on a uiTableValue that does not store an int. +_UI_EXTERN int uiTableValueInt(const uiTableValue *v); + +// uiNewTableValueColor() returns a uiTableValue that stores the +// given color. +_UI_EXTERN uiTableValue *uiNewTableValueColor(double r, double g, double b, double a); + +// uiTableValueColor() returns the color stored in v. It is an error to +// call this on a uiTableValue that does not store a color. +// TODO define whether all this, for both uiTableValue and uiAttribute, is undefined behavior or a caught error +_UI_EXTERN void uiTableValueColor(const uiTableValue *v, double *r, double *g, double *b, double *a); + +// uiTableModel is an object that provides the data for a uiTable. +// This data is returned via methods you provide in the +// uiTableModelHandler struct. +// +// uiTableModel represents data using a table, but this table does +// not map directly to uiTable itself. Instead, you can have data +// columns which provide instructions for how to render a given +// uiTable's column — for instance, one model column can be used +// to give certain rows of a uiTable a different background color. +// Row numbers DO match with uiTable row numbers. +// +// Once created, the number and data types of columns of a +// uiTableModel cannot change. +// +// Row and column numbers start at 0. A uiTableModel can be +// associated with more than one uiTable at a time. +typedef struct uiTableModel uiTableModel; + +// uiTableModelHandler defines the methods that uiTableModel +// calls when it needs data. Once a uiTableModel is created, these +// methods cannot change. +typedef struct uiTableModelHandler uiTableModelHandler; + +// TODO validate ranges; validate types on each getter/setter call (? table columns only?) +struct uiTableModelHandler { + // NumColumns returns the number of model columns in the + // uiTableModel. This value must remain constant through the + // lifetime of the uiTableModel. This method is not guaranteed + // to be called depending on the system. + // TODO strongly check column numbers and types on all platforms so these clauses can go away + int (*NumColumns)(uiTableModelHandler *, uiTableModel *); + // ColumnType returns the value type of the data stored in + // the given model column of the uiTableModel. The returned + // values must remain constant through the lifetime of the + // uiTableModel. This method is not guaranteed to be called + // depending on the system. + uiTableValueType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int); + // NumRows returns the number or rows in the uiTableModel. + // This value must be non-negative. + int (*NumRows)(uiTableModelHandler *, uiTableModel *); + // CellValue returns a uiTableValue corresponding to the model + // cell at (row, column). The type of the returned uiTableValue + // must match column's value type. Under some circumstances, + // NULL may be returned; refer to the various methods that add + // columns to uiTable for details. Once returned, the uiTable + // that calls CellValue will free the uiTableValue returned. + uiTableValue *(*CellValue)(uiTableModelHandler *mh, uiTableModel *m, int row, int column); + // SetCellValue changes the model cell value at (row, column) + // in the uiTableModel. Within this function, either do nothing + // to keep the current cell value or save the new cell value as + // appropriate. After SetCellValue is called, the uiTable will + // itself reload the table cell. Under certain conditions, the + // uiTableValue passed in can be NULL; refer to the various + // methods that add columns to uiTable for details. Once + // returned, the uiTable that called SetCellValue will free the + // uiTableValue passed in. + void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const uiTableValue *); +}; + +// @role uiTableModel constructor +// uiNewTableModel() creates a new uiTableModel with the given +// handler methods. +_UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh); + +// @role uiTableModel destructor +// uiFreeTableModel() frees the given table model. It is an error to +// free table models currently associated with a uiTable. +_UI_EXTERN void uiFreeTableModel(uiTableModel *m); + +// uiTableModelRowInserted() tells any uiTable associated with m +// that a new row has been added to m at index index. You call +// this function when the number of rows in your model has +// changed; after calling it, NumRows() should returm the new row +// count. +_UI_EXTERN void uiTableModelRowInserted(uiTableModel *m, int newIndex); + +// uiTableModelRowChanged() tells any uiTable associated with m +// that the data in the row at index has changed. You do not need to +// call this in your SetCellValue() handlers, but you do need to call +// this if your data changes at some other point. +_UI_EXTERN void uiTableModelRowChanged(uiTableModel *m, int index); + +// uiTableModelRowDeleted() tells any uiTable associated with m +// that the row at index index has been deleted. You call this +// function when the number of rows in your model has changed; +// after calling it, NumRows() should returm the new row +// count. +// TODO for this and Inserted: make sure the "after" part is right; clarify if it's after returning or after calling +_UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex); +// TODO reordering/moving + +// uiTableModelColumnNeverEditable and +// uiTableModelColumnAlwaysEditable are the value of an editable +// model column parameter to one of the uiTable create column +// functions; if used, that jparticular uiTable colum is not editable +// by the user and always editable by the user, respectively. +#define uiTableModelColumnNeverEditable (-1) +#define uiTableModelColumnAlwaysEditable (-2) + +// uiTableTextColumnOptionalParams are the optional parameters +// that control the appearance of the text column of a uiTable. +typedef struct uiTableTextColumnOptionalParams uiTableTextColumnOptionalParams; + +// uiTableParams defines the parameters passed to uiNewTable(). +typedef struct uiTableParams uiTableParams; + +struct uiTableTextColumnOptionalParams { + // ColorModelColumn is the model column containing the + // text color of this uiTable column's text, or -1 to use the + // default color. + // + // If CellValue() for this column for any cell returns NULL, that + // cell will also use the default text color. + int ColorModelColumn; +}; + +struct uiTableParams { + // Model is the uiTableModel to use for this uiTable. + // This parameter cannot be NULL. + uiTableModel *Model; + // RowBackgroundColorModelColumn is a model column + // number that defines the background color used for the + // entire row in the uiTable, or -1 to use the default color for + // all rows. + // + // If CellValue() for this column for any row returns NULL, that + // row will also use the default background color. + int RowBackgroundColorModelColumn; +}; + +// uiTable is a uiControl that shows tabular data, allowing users to +// manipulate rows of such data at a time. +typedef struct uiTable uiTable; +#define uiTable(this) ((uiTable *) (this)) + +// uiTableAppendTextColumn() appends a text column to t. +// name is displayed in the table header. +// textModelColumn is where the text comes from. +// If a row is editable according to textEditableModelColumn, +// SetCellValue() is called with textModelColumn as the column. +_UI_EXTERN void uiTableAppendTextColumn(uiTable *t, + const char *name, + int textModelColumn, + int textEditableModelColumn, + uiTableTextColumnOptionalParams *textParams); + +// uiTableAppendImageColumn() appends an image column to t. +// Images are drawn at icon size, appropriate to the pixel density +// of the screen showing the uiTable. +_UI_EXTERN void uiTableAppendImageColumn(uiTable *t, + const char *name, + int imageModelColumn); + +// uiTableAppendImageTextColumn() appends a column to t that +// shows both an image and text. +_UI_EXTERN void uiTableAppendImageTextColumn(uiTable *t, + const char *name, + int imageModelColumn, + int textModelColumn, + int textEditableModelColumn, + uiTableTextColumnOptionalParams *textParams); + +// uiTableAppendCheckboxColumn appends a column to t that +// contains a checkbox that the user can interact with (assuming the +// checkbox is editable). SetCellValue() will be called with +// checkboxModelColumn as the column in this case. +_UI_EXTERN void uiTableAppendCheckboxColumn(uiTable *t, + const char *name, + int checkboxModelColumn, + int checkboxEditableModelColumn); + +// uiTableAppendCheckboxTextColumn() appends a column to t +// that contains both a checkbox and text. +_UI_EXTERN void uiTableAppendCheckboxTextColumn(uiTable *t, + const char *name, + int checkboxModelColumn, + int checkboxEditableModelColumn, + int textModelColumn, + int textEditableModelColumn, + uiTableTextColumnOptionalParams *textParams); + +// uiTableAppendProgressBarColumn() appends a column to t +// that displays a progress bar. These columns work like +// uiProgressBar: a cell value of 0..100 displays that percentage, and +// a cell value of -1 displays an indeterminate progress bar. +_UI_EXTERN void uiTableAppendProgressBarColumn(uiTable *t, + const char *name, + int progressModelColumn); + +// uiTableAppendButtonColumn() appends a column to t +// that shows a button that the user can click on. When the user +// does click on the button, SetCellValue() is called with a NULL +// value and buttonModelColumn as the column. +// CellValue() on buttonModelColumn should return the text to show +// in the button. +_UI_EXTERN void uiTableAppendButtonColumn(uiTable *t, + const char *name, + int buttonModelColumn, + int buttonClickableModelColumn); + +// uiNewTable() creates a new uiTable with the specified parameters. +_UI_EXTERN uiTable *uiNewTable(uiTableParams *params); + #ifdef __cplusplus } #endif diff --git a/util.go b/util.go deleted file mode 100644 index b097004..0000000 --- a/util.go +++ /dev/null @@ -1,53 +0,0 @@ -// 12 december 2015 - -package ui - -import ( - "unsafe" -) - -// #include -// // TODO remove when switching to Go 1.7 -// #include -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/window.go b/window.go deleted file mode 100644 index bdd230c..0000000 --- a/window.go +++ /dev/null @@ -1,159 +0,0 @@ -// 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/zy_page1_test.go b/zy_page1_test.go deleted file mode 100644 index 6a8d4a4..0000000 --- a/zy_page1_test.go +++ /dev/null @@ -1,155 +0,0 @@ -// 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/zy_page2_test.go b/zy_page2_test.go deleted file mode 100644 index dd4a6be..0000000 --- a/zy_page2_test.go +++ /dev/null @@ -1,187 +0,0 @@ -// 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/zz_test.go b/zz_test.go deleted file mode 100644 index 83a8474..0000000 --- a/zz_test.go +++ /dev/null @@ -1,129 +0,0 @@ -// 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) - } -} -- cgit v1.2.3