Add slander and spinbox in toolkit/andlabs

fix the helloworld demo
    move slider into toolkit/
    move more into the toolkit directory
    add spinbox()
    fix example
    minor update
    fix examples
    Fix andlabs.ui.Slider() to work again
    correctly implement custom OnChange() callback

Signed-off-by: Jeff Carr <jcarr@wit.com>
This commit is contained in:
Jeff Carr 2022-10-16 08:07:13 -05:00
parent 9076499b30
commit fbf97443d5
19 changed files with 331 additions and 160 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
*.swp
cmds/gui-example/gui-example
cmds/gui-demo/gui-demo
cmds/helloworld/helloworld

View File

@ -10,4 +10,9 @@ update:
go get -v -t -u ./...
examples:
make -C gui-example
make -C cmds/helloworld
make -C cmds/gui-example
make -C cmds/gui-demo
doc:
godoc -v

18
box.go
View File

@ -192,6 +192,7 @@ func (n *Node) AddComboBox(title string, s ...string) *Node {
return n
}
newNode := n.AddNode(title)
ecbox := ui.NewEditableCombobox()
for id, name := range s {
@ -202,18 +203,27 @@ func (n *Node) AddComboBox(title string, s ...string) *Node {
ecbox.OnChanged(func(*ui.EditableCombobox) {
test := ecbox.Text()
log.Println("node.Name = '" + n.Name + "' text for '" + title + "' is now: '" + test + "'")
log.Println("need to call node.OnChanged() here")
if (newNode.OnChanged == nil) {
log.Println("node.OnChanged() is nil")
log.Println("need to call node.OnChanged() here", newNode.OnChanged)
newNode.Dump()
} else {
log.Println("need to call node.OnChanged() here", newNode.OnChanged)
newNode.OnChanged(newNode)
}
})
box.Append(ecbox, false)
newNode := n.AddNode(title)
newNode.uiText = ecbox
return newNode
}
func (n *Node) OnChanged(f func()) {
f()
}
//func (n *Node) OnChanged(f func()) {
// log.Println("not doing shit here in Node.OnChanged()")
// f()
//}
func (n *Node) GetText() string {
if (n.uiText == nil) {

View File

@ -2,5 +2,5 @@ run: build
./gui-example
build:
GO111MODULE="off" go -v get .
GO111MODULE="off" go get -v .
GO111MODULE="off" go build

View File

@ -33,9 +33,9 @@ func addDemoTab(n *gui.Node, title string) {
groupNode1 := newNode.AddGroup("group 1")
cbNode := groupNode1.AddComboBox("username", "root", "jcarr", "hugo")
cbNode.OnChanged(func () {
cbNode.OnChanged = func (cbNode *gui.Node) {
username = cbNode.GetText()
})
}
groupNode1.AddComboBox("demoCombo3", "foo 3", "bar", "stuff")
groupNode1.Dump()

14
cmds/helloworld/Makefile Normal file
View File

@ -0,0 +1,14 @@
run: build
./helloworld
build:
go get -v -u -x .
go build
build-master:
GO111MODULE="off" go get -v -x .
GO111MODULE="off" go build
./helloworld
update:
GO111MODULE="off" go get -v -u -x .

View File

@ -2,6 +2,8 @@
package main
import (
"os"
"log"
"git.wit.org/wit/gui"
)
@ -14,6 +16,8 @@ func initGUI() {
gui.Config.Title = "Hello World golang wit/gui Window"
gui.Config.Width = 640
gui.Config.Height = 480
gui.Config.Exit = myDefaultExit
node1 := gui.NewWindow()
addDemoTab(node1, "A Simple Tab Demo")
addDemoTab(node1, "A Second Tab")
@ -25,3 +29,9 @@ func addDemoTab(n *gui.Node, title string) {
groupNode1 := newNode.AddGroup("group 1")
groupNode1.AddComboBox("demoCombo2", "more 1", "more 2", "more 3")
}
func myDefaultExit(n *gui.Node) {
log.Println("You can Do exit() things here")
os.Exit(0)
}

20
doc.go
View File

@ -42,28 +42,14 @@ sections below for further details on formatting and configuration options.
groupNode1.AddComboBox("demoCombo2", "more 1", "more 2", "more 3")
}
Configuration Options
Configuration of the GUI is handled by fields in the ConfigType type. For
convenience, all of the top-level functions use a global state available
via the gui.Config global.
The following configuration options are available:
* Width
When creating a new window, this is the width
* Height
When creating a new window, this is the height
* Debug
When 'true' log more output
Toolkit Usage
Right now, this abstraction is built on top of the go package 'andlabs/ui'
which does the cross platform support.
The next step is to intent is to allow this to work directly against GTK and QT.
It should be able to add Fyne, WASM, native macos & windows, android, etc.
It should be able to add Fyne, WASM, native macos & windows, android and
hopefully also things like libSDL, faiface/pixel, slint
Errors

111
entry.go
View File

@ -1,7 +1,7 @@
package gui
import "log"
import "fmt"
// import "fmt"
import "github.com/andlabs/ui"
import _ "github.com/andlabs/ui/winmanifest"
@ -15,10 +15,8 @@ func NewLabel(box *GuiBox, text string) {
func (n *Node) NewLabel(text string) *Node {
// make new node here
// n.Append(ui.NewLabel(text), false)
newNode := makeNode(n, text, 333, 334)
newNode.Dump()
// panic("node.NewLabel()")
n.Append(newNode)
return newNode
@ -52,110 +50,3 @@ func (n *Node) SetText(value string) error {
}
return nil
}
func SetText(box *GuiBox, name string, value string) error {
if (box == nil) {
return fmt.Errorf("gui.SetText() ERROR box == nil")
}
if (box.Window.EntryMap == nil) {
return fmt.Errorf("gui.SetText() ERROR b.Box.Window.EntryMap == nil")
}
spew.Dump(box.Window.EntryMap)
if (box.Window.EntryMap[name] == nil) {
return fmt.Errorf("gui.SetText() ERROR box.Window.EntryMap[" + name + "] == nil ")
}
e := box.Window.EntryMap[name]
log.Println("gui.SetText() box.Window.EntryMap[", name, "] = ", e.UiEntry.Text())
e.UiEntry.SetText(value)
log.Println("gui.SetText() box.Window.EntryMap[", name, "] = ", e.UiEntry.Text())
log.Println("gui.SetText() END")
return nil
}
// makeEntryBox(box, "hostname:", "blah.foo.org") {
func MakeEntryVbox(box *GuiBox, a string, startValue string, edit bool, action string) *GuiEntry {
// Start 'Nickname' vertical box
vboxN := ui.NewVerticalBox()
vboxN.SetPadded(true)
vboxN.Append(ui.NewLabel(a), false)
e := defaultMakeEntry(startValue, edit, action)
vboxN.Append(e.UiEntry, false)
box.UiBox.Append(vboxN, false)
// End 'Nickname' vertical box
return e
}
func MakeEntryHbox(box *GuiBox, a string, startValue string, edit bool, action string) *GuiEntry {
hboxN := ui.NewHorizontalBox()
hboxN.SetPadded(true)
hboxN.Append(ui.NewLabel(a), false)
e := defaultMakeEntry(startValue, edit, action)
hboxN.Append(e.UiEntry, true)
box.UiBox.Append(hboxN, true)
return e
}
func AddEntry(box *GuiBox, name string) *GuiEntry {
var ge *GuiEntry
ge = new(GuiEntry)
ue := ui.NewEntry()
ue.SetReadOnly(false)
ue.OnChanged(func(*ui.Entry) {
log.Println("gui.AddEntry() OK. ue.Text() =", ue.Text())
})
box.UiBox.Append(ue, false)
ge.UiEntry = ue
box.Window.EntryMap[name] = ge
return ge
}
func defaultEntryChange(e *ui.Entry) {
for key, em := range Data.AllEntries {
if (Config.Debug) {
log.Println("\tdefaultEntryChange() Data.AllEntries =", key, em)
}
if Data.AllEntries[key].UiEntry == e {
log.Println("defaultEntryChange() FOUND",
"Name =", Data.AllEntries[key].Name,
"Last =", Data.AllEntries[key].Last,
"e.Text() =", e.Text())
Data.AllEntries[key].Last = e.Text()
if Data.AllEntries[key].Normalize != nil {
fixed := Data.AllEntries[key].Normalize(e.Text())
e.SetText(fixed)
}
return
}
}
log.Println("defaultEntryChange() ERROR. MISSING ENTRY MAP. e.Text() =", e.Text())
}
func defaultMakeEntry(startValue string, edit bool, action string) *GuiEntry {
e := ui.NewEntry()
e.SetText(startValue)
if (edit == false) {
e.SetReadOnly(true)
}
e.OnChanged(defaultEntryChange)
// add the entry field to the global map
var newEntry GuiEntry
newEntry.UiEntry = e
newEntry.Edit = edit
newEntry.Name = action
if (action == "INT") {
newEntry.Normalize = normalizeInt
}
Data.AllEntries = append(Data.AllEntries, &newEntry)
return &newEntry
}

2
go.mod
View File

@ -5,5 +5,5 @@ go 1.17
require (
github.com/andlabs/ui v0.0.0-20200610043537-70a69d6ae31e
github.com/davecgh/go-spew v1.1.1
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69
)

3
go.sum
View File

@ -4,5 +4,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

28
int.go Normal file
View File

@ -0,0 +1,28 @@
package gui
import "log"
import "github.com/davecgh/go-spew/spew"
// import toolkit "git.wit.org/wit/gui/toolkit/andlabs"
// Get the int from the gui toolkit
// TODO: instead store the int in the Node structure? (this is probably a better idea)
// because eventually this gui package should become it's own seperate go routine and never interact from the
// gui subroutine back into the upstream application using the gui package
func (n *Node) Int() int {
if (Config.DebugToolkit) {
log.Println("gui.Node.Int() for node name =", n.Name)
scs := spew.ConfigState{MaxDepth: 1}
scs.Dump(n)
}
if (n.Toolkit == nil) {
log.Println("gui.Node.Int() for toolkit struct = nil")
return 0
}
i := n.Toolkit.Value()
return i
}

View File

@ -9,8 +9,11 @@ import (
"github.com/andlabs/ui"
_ "github.com/andlabs/ui/winmanifest"
)
import toolkit "git.wit.org/wit/gui/toolkit/andlabs"
type Element int
// https://ieftimov.com/post/golang-datastructures-trees/
@ -39,8 +42,10 @@ func (s Element) String() string {
return "unknown"
}
// The Node is simply the name and the size of whatever GUI element exists
type Node struct {
id string
Name string
Width int
Height int
@ -50,10 +55,15 @@ type Node struct {
window *GuiWindow
box *GuiBox
custom func(*Node)
custom func(*Node)
OnChanged func(*Node)
Toolkit *toolkit.Toolkit
uiControl *ui.Control
uiButton *ui.Button
uiSlider *ui.Slider
uiSpinbox *ui.Spinbox
uiWindow *ui.Window
uiTab *ui.Tab
uiBox *ui.Box
@ -69,28 +79,53 @@ func (n *Node) Window() *Node {
}
func (n *Node) Dump() {
log.Println("gui.Node.Dump() id = ", n.id)
log.Println("gui.Node.Dump() Name = ", n.Name)
log.Println("gui.Node.Dump() Width = ", n.Width)
log.Println("gui.Node.Dump() Height = ", n.Height)
IndentPrintln("id = ", n.id)
IndentPrintln("Name = ", n.Name)
IndentPrintln("Width = ", n.Width)
IndentPrintln("Height = ", n.Height)
if (n.parent == nil) {
log.Println("gui.Node.Dump() parent = nil")
IndentPrintln("parent = nil")
} else {
log.Println("gui.Node.Dump() parent = ", n.parent.id)
IndentPrintln("parent =", n.parent.id)
}
if (n.children != nil) {
IndentPrintln("children = ", n.children)
}
log.Println("gui.Node.Dump() children = ", n.children)
log.Println("gui.Node.Dump() window = ", n.window)
log.Println("gui.Node.Dump() box = ", n.box)
if (n.window != nil) {
IndentPrintln("window = ", n.window)
}
if (n.window != nil) {
IndentPrintln("box = ", n.box)
}
log.Println("gui.Node.Dump() uiWindow = ", n.uiWindow)
log.Println("gui.Node.Dump() uiTab = ", n.uiTab)
log.Println("gui.Node.Dump() uiBox = ", n.uiBox)
log.Println("gui.Node.Dump() uiControl = ", n.uiControl)
log.Println("gui.Node.Dump() uiButton = ", n.uiButton)
if (n.window != nil) {
IndentPrintln("uiWindow = ", n.uiWindow)
}
if (n.uiTab != nil) {
IndentPrintln("uiTab = ", n.uiTab)
}
if (n.uiBox != nil) {
IndentPrintln("uiBox = ", n.uiBox)
}
if (n.uiControl != nil) {
IndentPrintln("uiControl = ", n.uiControl)
}
if (n.uiButton != nil) {
IndentPrintln("uiButton = ", n.uiButton)
}
if (n.custom != nil) {
IndentPrintln("custom = ", n.custom)
}
if (n.OnChanged != nil) {
IndentPrintln("OnChanged = ", n.OnChanged)
}
if (n.id == "") {
panic("gui.Node.Dump() id == nil")
// Node structs should never have a nil id.
// I probably shouldn't panic here, but this is just to check the sanity of
// the gui package to make sure it's not exiting
panic("gui.Node.Dump() id == nil TODO: make a unigue id here in the golang gui library")
}
}
@ -130,11 +165,16 @@ func (n *Node) List() {
var listChildrenParent *Node
var listChildrenDepth int = 0
var defaultPadding = " "
func IndentPrintln(a ...interface{}) {
indentPrintln(listChildrenDepth, defaultPadding, a)
}
func indentPrintln(depth int, format string, a ...interface{}) {
var tabs string
for i := 0; i < depth; i++ {
tabs = tabs + "\t"
tabs = tabs + format
}
// newFormat := tabs + strconv.Itoa(depth) + " " + format
@ -143,7 +183,7 @@ func indentPrintln(depth int, format string, a ...interface{}) {
}
func (n *Node) ListChildren(dump bool) {
indentPrintln(listChildrenDepth, "\t", n.id, n.Width, n.Height, n.Name)
indentPrintln(listChildrenDepth, defaultPadding, n.id, n.Width, n.Height, n.Name)
if (dump == true) {
n.Dump()

24
slider.go Normal file
View File

@ -0,0 +1,24 @@
package gui
import "log"
import toolkit "git.wit.org/wit/gui/toolkit/andlabs"
func (n *Node) NewSlider(name string, x int, y int) *Node {
// make new node here
log.Println("toolkit.NewSlider", x, y)
newNode := n.makeNode(name, 767, 676 + Config.counter)
newNode.Name = name
t := toolkit.NewSlider(n.uiBox, name, x, y)
t.OnChanged = func(t *toolkit.Toolkit) {
log.Println("toolkit.NewSlider() value =", t.Value())
if (newNode.OnChanged != nil) {
newNode.OnChanged(newNode)
}
}
newNode.Toolkit = t
return newNode
}

24
spinbox.go Normal file
View File

@ -0,0 +1,24 @@
package gui
import "log"
import toolkit "git.wit.org/wit/gui/toolkit/andlabs"
func (n *Node) NewSpinbox(name string, x int, y int) *Node {
// make new node here
log.Println("toolkit.NewSpinbox", x, y)
newNode := n.makeNode(name, 767, 676 + Config.counter)
newNode.Name = name
t := toolkit.NewSpinbox(n.uiBox, name, x, y)
t.OnChanged = func(t *toolkit.Toolkit) {
log.Println("toolkit.NewSpinbox() value =", t.Value())
if (newNode.OnChanged != nil) {
newNode.OnChanged(newNode)
}
}
newNode.Toolkit = t
return newNode
}

View File

@ -7,7 +7,9 @@ import (
"github.com/andlabs/ui"
"golang.org/x/image/font"
_ "github.com/andlabs/ui/winmanifest"
// "github.com/davecgh/go-spew/spew"
// _ "github.com/andlabs/ui/winmanifest"
)
//
@ -24,11 +26,12 @@ type GuiConfig struct {
Height int
Exit func(*Node)
Debug bool
DebugNode bool
DebugTabs bool
DebugTable bool
DebugWindow bool
Debug bool
DebugNode bool
DebugTabs bool
DebugTable bool
DebugWindow bool
DebugToolkit bool
depth int
counter int // used to make unique ID's
@ -171,6 +174,8 @@ func (b *GuiBox) SetNode(n *Node) {
func (b *GuiBox) Append(child ui.Control, x bool) {
if b.UiBox == nil {
// spew.Dump(b)
b.Dump()
panic("GuiBox.Append() can't work. UiBox == nil")
return
}

38
toolkit/andlabs/slider.go Normal file
View File

@ -0,0 +1,38 @@
package toolkit
import "log"
import "github.com/andlabs/ui"
import _ "github.com/andlabs/ui/winmanifest"
import "github.com/davecgh/go-spew/spew"
func NewSlider(b *ui.Box, name string, x int, y int) *Toolkit {
// make new node here
log.Println("gui.Toolbox.NewSpinbox()", x, y)
var t Toolkit
if (b == nil) {
log.Println("gui.ToolboxNode.NewSpinbox() node.UiBox == nil. I can't add a range UI element without a place to put it")
return &t
}
s := ui.NewSlider(x, y)
t.uiSlider = s
t.uiBox = b
t.uiBox.Append(s, false)
s.OnChanged(func(spin *ui.Slider) {
i := spin.Value()
if (DebugToolkit) {
log.Println("gui.Toolbox.ui.OnChanged() val =", i)
scs := spew.ConfigState{MaxDepth: 1}
scs.Dump(t)
}
if (t.OnChanged != nil) {
log.Println("gui.Toolbox.OnChanged() entered val =", i)
t.OnChanged(&t)
}
})
return &t
}

View File

@ -0,0 +1,86 @@
package toolkit
import "log"
import "github.com/andlabs/ui"
import _ "github.com/andlabs/ui/winmanifest"
import "github.com/davecgh/go-spew/spew"
var DebugToolkit bool = false
// stores the raw toolkit internals
type Toolkit struct {
id string
Name string
Width int
Height int
OnChanged func(*Toolkit)
uiControl *ui.Control
uiButton *ui.Button
uiSlider *ui.Slider
uiSpinbox *ui.Spinbox
uiWindow *ui.Window
uiTab *ui.Tab
uiBox *ui.Box
uiText *ui.EditableCombobox
}
func (t *Toolkit) Value() int {
if (DebugToolkit) {
log.Println("gui.Toolkit.Value() Enter")
scs := spew.ConfigState{MaxDepth: 1}
scs.Dump(t)
}
if (t == nil) {
log.Println("gui.Toolkit.Value() can not get value t == nil")
return 0
}
if (t.uiSlider != nil) {
if (DebugToolkit) {
log.Println("gui.Toolkit.Value() =", t.uiSlider.Value)
}
return t.uiSlider.Value()
}
if (t.uiSpinbox != nil) {
if (DebugToolkit) {
log.Println("gui.Toolkit.Value() =", t.uiSpinbox.Value)
}
return t.uiSpinbox.Value()
}
log.Println("gui.Toolkit.Value() Could not find a ui element to get a value from")
return 0
}
func NewSpinbox(b *ui.Box, name string, x int, y int) *Toolkit {
// make new node here
log.Println("gui.Toolbox.NewSpinbox()", x, y)
var t Toolkit
if (b == nil) {
log.Println("gui.ToolboxNode.NewSpinbox() node.UiBox == nil. I can't add a range UI element without a place to put it")
return &t
}
spin := ui.NewSpinbox(x, y)
t.uiSpinbox = spin
t.uiBox = b
t.uiBox.Append(spin, false)
spin.OnChanged(func(spin *ui.Spinbox) {
i := spin.Value()
if (DebugToolkit) {
log.Println("gui.Toolbox.ui.OnChanged() val =", i)
scs := spew.ConfigState{MaxDepth: 1}
scs.Dump(t)
}
if (t.OnChanged != nil) {
log.Println("gui.Toolbox.OnChanged() entered val =", i)
t.OnChanged(&t)
}
})
return &t
}

View File

@ -58,6 +58,12 @@ func DeleteWindow(name string) {
}
}
/*
generic function to create a newnode structure on the tree of nodes
If the parent is null, it tries to find out where it should go otherwise
it creates a new window or new tab depending on the toolkit or OS
*/
func makeNode(parent *Node, title string, x int, y int) *Node {
var node Node
node.Name = title