diff --git a/.gitignore b/.gitignore index 33b4d57..47191be 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,10 @@ cmds/helloworld/helloworld cmds/textbox/textbox cmds/gui-demo/gui-demo cmds/consolemouse/consolemouse +cmds/console-hello-world/console-hello-world +cmds/gocli-as-plugin/gocli-as-plugin +cmds/buttonAsPlugin/buttonAsPlugin + +tookit/gocui/gocli + +toolkit/*.so diff --git a/Makefile b/Makefile index 356458b..306800f 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ +.PHONY: README.md + all: README.md @echo @echo "make examples # will run all the examples" @@ -33,3 +35,6 @@ doc: # GO111MODULE=on go install github.com/posener/goreadme/cmd/goreadme@latest (worked Oct 20 2022) README.md: doc.go goreadme -factories -types -functions -variabless > README.md + +plugins: + GO111MODULE="off" go build -buildmode=plugin -o toolkit/gocli.so toolkit/gocli/greeter.go diff --git a/README.md b/README.md index 50a359b..56358e0 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Principles: * It's ok to guess. We will return something close. * Hide complexity internally here * Isolate the GUI toolkit -* Function names should follow [Wikipedia Graphical widget] +* Try to use [Wikipedia Graphical widget] names ``` ## Quick Start @@ -161,15 +161,15 @@ This creates a window that shows how this package works `func GolangDebugWindow()` -### func [IndentPrintln](/structs.go#L190) +### func [IndentPrintln](/structs.go#L199) `func IndentPrintln(a ...interface{})` -### func [Main](/main.go#L34) +### func [Main](/main.go#L31) `func Main(f func())` -### func [Queue](/main.go#L45) +### func [Queue](/main.go#L42) `func Queue(f func())` @@ -194,7 +194,7 @@ For example: gui.Queue(NewWindow()) ## Types -### type [GuiConfig](/structs.go#L42) +### type [GuiConfig](/structs.go#L56) `type GuiConfig struct { ... }` @@ -204,7 +204,11 @@ For example: gui.Queue(NewWindow()) var Config GuiConfig ``` -### type [Node](/structs.go#L97) +### type [GuiOptions](/structs.go#L44) + +`type GuiOptions struct { ... }` + +### type [Node](/structs.go#L104) `type Node struct { ... }` @@ -262,7 +266,7 @@ func main() { You get a window ``` -### type [Widget](/structs.go#L67) +### type [Widget](/structs.go#L74) `type Widget int` diff --git a/cmds/buttonAsPlugin/Makefile b/cmds/buttonAsPlugin/Makefile new file mode 100644 index 0000000..431d956 --- /dev/null +++ b/cmds/buttonAsPlugin/Makefile @@ -0,0 +1,14 @@ +run: build + ./buttonAsPlugin + +build-release: + go get -v -u -x . + go build + ./buttonAsPlugin + +build: + GO111MODULE="off" go get -v -x . + GO111MODULE="off" go build + +update: + GO111MODULE="off" go get -v -u -x . diff --git a/cmds/buttonAsPlugin/main.go b/cmds/buttonAsPlugin/main.go new file mode 100644 index 0000000..1c2eeaf --- /dev/null +++ b/cmds/buttonAsPlugin/main.go @@ -0,0 +1,33 @@ +// This is a simple example +package main + +import ( + "log" + "git.wit.org/wit/gui" +) + +func main() { + gui.Main(buttonWindow) +} + +// This creates a window +func buttonWindow() { + var w, g *gui.Node + gui.Config.Title = "Demo Plugin Window" + gui.Config.Width = 640 + gui.Config.Height = 480 + +// gui.Config.Exit = gui.StandardClose +// gui.SetDebug(true) + + w = gui.NewWindow() + g = w.NewGroup("buttonGroup") + + g.NewButton("hello", func () { + log.Println("world") + }) + + g.NewButton("foo", func () { + log.Println("bar") + }) +} diff --git a/cmds/console-hello-world/Makefile b/cmds/console-hello-world/Makefile new file mode 100644 index 0000000..9c3540d --- /dev/null +++ b/cmds/console-hello-world/Makefile @@ -0,0 +1,15 @@ +run: build + ./console-hello-world + reset + ldd ./console-hello-world + +build-release: + go get -v -u -x . + go build + +build: + GO111MODULE="off" go get -v -x . + GO111MODULE="off" go build + +update: + GO111MODULE="off" go get -v -u -x . diff --git a/cmds/console-hello-world/keybindings.go b/cmds/console-hello-world/keybindings.go new file mode 100644 index 0000000..fdac1ff --- /dev/null +++ b/cmds/console-hello-world/keybindings.go @@ -0,0 +1,130 @@ +// Copyright 2014 The gocui Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( +// "errors" +// "fmt" + "log" +// "strings" + + "github.com/awesome-gocui/gocui" +) + +func initKeybindings(g *gocui.Gui) error { + log.Println("got to initKeybindings") + if err := g.SetKeybinding("", 'q', gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return gocui.ErrQuit + }); err != nil { + return err + } + if err := g.SetKeybinding("", 'Q', gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return gocui.ErrQuit + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return gocui.ErrQuit + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeySpace, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return newView(g) + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyBackspace, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return delView(g) + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyBackspace2, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return delView(g) + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + log.Println("tab", v.Name()) + return nextView(g, true) + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyArrowLeft, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return moveView(g, v, -delta, 0) + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyArrowRight, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return moveView(g, v, delta, 0) + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + log.Println("down", v.Name()) + return moveView(g, v, 0, delta) + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + log.Println("up", v.Name()) + return moveView(g, v, 0, -delta) + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyEnter, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + log.Println("enter", v.Name()) + return nil + }); err != nil { + return err + } + if err := g.SetKeybinding("", 't', gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + _, err := g.SetViewOnTop(views[curView]) + return err + }); err != nil { + return err + } + if err := g.SetKeybinding("", 'b', gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + _, err := g.SetViewOnBottom(views[curView]) + return err + }); err != nil { + return err + } + if err := g.SetKeybinding("", 'j', gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return newJ(g) + }); err != nil { + return err + } + if err := g.SetKeybinding("", 'h', gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + log.Println("help", v.Name()) + tmp, _ := g.SetViewOnTop("help") + log.Println("help 2", tmp.Name(), "blah") +// g.SetView("help", 2, 2, 30, 15, 0); + g.SetCurrentView("help") +// moveView(g, tmp, 0, -delta) + if err := g.DeleteView("help"); err != nil { + panic(err) + } + return nil + }); err != nil { + return err + } + return nil +} diff --git a/cmds/console-hello-world/log.go b/cmds/console-hello-world/log.go new file mode 100644 index 0000000..b05beaf --- /dev/null +++ b/cmds/console-hello-world/log.go @@ -0,0 +1,35 @@ +// This creates a simple hello world window +package main + +import ( + "log" + "fmt" + "os" + arg "github.com/alexflint/go-arg" +) + + +var args struct { + Foo string + Bar bool + User string `arg:"env:USER"` + Demo bool `help:"run a demo"` +} + +var f *os.File +var err error + +func init() { + arg.MustParse(&args) + fmt.Println(args.Foo, args.Bar, args.User) + + f, err = os.OpenFile("/tmp/guilogfile", os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666) + if err != nil { + log.Fatalf("error opening file: %v", err) + } + // hmm. is there a trick here or must this be in main() + // defer f.Close() + + log.SetOutput(f) + log.Println("This is a test log entry") +} diff --git a/cmds/console-hello-world/main.go b/cmds/console-hello-world/main.go new file mode 100644 index 0000000..dd360d5 --- /dev/null +++ b/cmds/console-hello-world/main.go @@ -0,0 +1,91 @@ +// Copyright 2014 The gocui Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "errors" + "fmt" + "log" + + "github.com/awesome-gocui/gocui" +) + +const delta = 1 + +var ( + views = []string{} + curView = -1 + idxView = 0 + currentX = 5 + currentY = 2 + groupSize = 0 + baseGui *gocui.Gui +) + +var helpLabel *gocui.View + +func main() { + // setup log to write to a file +// logInit() + + g, err := gocui.NewGui(gocui.OutputNormal, true) + baseGui = g + if err != nil { + log.Panicln(err) + } + defer g.Close() + + g.Highlight = true + g.SelFgColor = gocui.ColorRed + g.SelFrameColor = gocui.ColorRed + + g.SetManagerFunc(layout) + + if err := initKeybindings(g); err != nil { + log.Panicln(err) + } + if err := newView(g); err != nil { + log.Panicln(err) + } + + addButton("hello") + addButton("world") + addButton("foo") + + addGroup("blank") + addButton("bar") + addButton("bar none") + addButton("bar going") + + addGroup("te") + addButton("world 2") + addButton("foo 2") + + if err := g.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) { + log.Panicln(err) + } +} + +func layout(g *gocui.Gui) error { + var err error + maxX, _ := g.Size() + helpLabel, err = g.SetView("help", maxX-32, 0, maxX-1, 11, 0) + if err != nil { + if !errors.Is(err, gocui.ErrUnknownView) { + return err + } + fmt.Fprintln(helpLabel, "KEYBINDINGS") + fmt.Fprintln(helpLabel, "Enter: Click Button") + fmt.Fprintln(helpLabel, "Tab/Space: Switch Buttons") + fmt.Fprintln(helpLabel, "") + fmt.Fprintln(helpLabel, "h: Help") + fmt.Fprintln(helpLabel, "Backspace: Delete Button") + fmt.Fprintln(helpLabel, "Arrow keys: Move Button") + fmt.Fprintln(helpLabel, "t: Move Button to the top") + fmt.Fprintln(helpLabel, "b: Move Button to the button") + fmt.Fprintln(helpLabel, "Ctrl-C or Q: Exit") + } + return nil +} diff --git a/cmds/console-hello-world/newJ.go b/cmds/console-hello-world/newJ.go new file mode 100644 index 0000000..47c7439 --- /dev/null +++ b/cmds/console-hello-world/newJ.go @@ -0,0 +1,46 @@ +// Copyright 2014 The gocui Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "errors" + "fmt" + "log" + "strings" + + "github.com/awesome-gocui/gocui" +) + +var topX int = 2 +var bottomX int = 20 +var topY int = 2 +var bottomY int = 7 + +func newJ(g *gocui.Gui) error { + // maxX, maxY := g.Size() + name := fmt.Sprintf("jcarr %v test ", idxView) + v, err := g.SetView(name, topX, topY, bottomX, bottomY, 0) + if err == nil { + return err + } + if !errors.Is(err, gocui.ErrUnknownView) { + return err + } + + v.Wrap = true + fmt.Fprintln(v, name) + fmt.Fprintln(v, strings.Repeat("foo\n", 2)) + // fmt.Fprintln(v, strings.Repeat(name+" ", 30)) + log.Println("newJ added a new view", v.Name()) + + if _, err := g.SetCurrentView(name); err != nil { + return err + } + + views = append(views, name) + curView = len(views) - 1 + idxView += 1 + return nil +} diff --git a/cmds/console-hello-world/views.go b/cmds/console-hello-world/views.go new file mode 100644 index 0000000..50287c2 --- /dev/null +++ b/cmds/console-hello-world/views.go @@ -0,0 +1,114 @@ +// Copyright 2014 The gocui Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "errors" + "fmt" + "log" + "strings" + + "github.com/awesome-gocui/gocui" +) + +func addGroup(name string) { + log.Println("addGroup()", name) + currentY = 2 + currentX += groupSize + 6 +} + +func addButton(name string) error { + t := len(name) + v, err := baseGui.SetView(name, currentX, currentY, currentX+t+3, currentY+2, 0) + if err == nil { + return err + } + if !errors.Is(err, gocui.ErrUnknownView) { + return err + } + + v.Wrap = true + fmt.Fprintln(v, " " + name) + fmt.Fprintln(v, strings.Repeat("foo\n", 2)) + + if _, err := baseGui.SetCurrentView(name); err != nil { + return err + } + + views = append(views, name) + curView = len(views) - 1 + idxView += 1 + currentY += 3 + if (groupSize < len(views)) { + groupSize = len(views) + } + return nil +} + +func newView(g *gocui.Gui) error { + maxX, maxY := g.Size() + name := fmt.Sprintf("v%v", idxView) + v, err := g.SetView(name, maxX/2-5, maxY/2-5, maxX/2+5, maxY/2+5, 0) + if err == nil { + return err + } + if !errors.Is(err, gocui.ErrUnknownView) { + return err + } + + v.Wrap = true + fmt.Fprintln(v, strings.Repeat(name+" ", 30)) + + if _, err := g.SetCurrentView(name); err != nil { + return err + } + + views = append(views, name) + curView = len(views) - 1 + idxView += 1 + return nil +} + +func delView(g *gocui.Gui) error { + if len(views) <= 1 { + return nil + } + + if err := g.DeleteView(views[curView]); err != nil { + return err + } + views = append(views[:curView], views[curView+1:]...) + + return nextView(g, false) +} + +func nextView(g *gocui.Gui, disableCurrent bool) error { + next := curView + 1 + if next > len(views)-1 { + next = 0 + } + + if _, err := g.SetCurrentView(views[next]); err != nil { + return err + } + + curView = next + return nil +} + +func moveView(g *gocui.Gui, v *gocui.View, dx, dy int) error { + name := v.Name() + x0, y0, x1, y1, err := g.ViewPosition(name) + if err != nil { + return err + } + log.Println(x0, y0, x1, y1) + if _, err := g.SetView(name, x0+dx, y0+dy, x1+dx, y1+dy, 0); err != nil { + return err + } + x0, y0, x1, y1, err = g.ViewPosition(name) + log.Println(x0, y0, x1, y1) + return nil +} diff --git a/cmds/gocli-as-plugin/Makefile b/cmds/gocli-as-plugin/Makefile new file mode 100644 index 0000000..277d737 --- /dev/null +++ b/cmds/gocli-as-plugin/Makefile @@ -0,0 +1,14 @@ +run: build + ./gocli-as-plugin + # ldd ./gocli-as-plugin + +build-release: + go get -v -u -x . + go build + +build: + GO111MODULE="off" go get -v -x . + GO111MODULE="off" go build + +update: + GO111MODULE="off" go get -v -u -x . diff --git a/cmds/gocli-as-plugin/log.go b/cmds/gocli-as-plugin/log.go new file mode 100644 index 0000000..b05beaf --- /dev/null +++ b/cmds/gocli-as-plugin/log.go @@ -0,0 +1,35 @@ +// This creates a simple hello world window +package main + +import ( + "log" + "fmt" + "os" + arg "github.com/alexflint/go-arg" +) + + +var args struct { + Foo string + Bar bool + User string `arg:"env:USER"` + Demo bool `help:"run a demo"` +} + +var f *os.File +var err error + +func init() { + arg.MustParse(&args) + fmt.Println(args.Foo, args.Bar, args.User) + + f, err = os.OpenFile("/tmp/guilogfile", os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666) + if err != nil { + log.Fatalf("error opening file: %v", err) + } + // hmm. is there a trick here or must this be in main() + // defer f.Close() + + log.SetOutput(f) + log.Println("This is a test log entry") +} diff --git a/cmds/gocli-as-plugin/main.go b/cmds/gocli-as-plugin/main.go new file mode 100644 index 0000000..a9bd632 --- /dev/null +++ b/cmds/gocli-as-plugin/main.go @@ -0,0 +1,59 @@ +// Copyright 2014 The gocui Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "log" + "os" + + "plugin" +// "github.com/awesome-gocui/gocui" +) + +type Greeter interface { + Greet() +} + +var plugGocli *plugin.Plugin +var plugHello *plugin.Plugin + +func main() { + log.Println("attempt plugin") + + go loadPlugin(plugHello, "../../toolkit/hello.so") + loadPlugin(plugGocli, "../../toolkit/gocli.so") +} + +func loadPlugin(plug *plugin.Plugin, name string) { + // load module + // 1. open the so file to load the symbols + plug, err = plugin.Open(name) + if err != nil { + log.Println(err) + os.Exit(1) + } + + // 2. look up a symbol (an exported function or variable) + // in this case, variable Greeter + symGreeter, err := plug.Lookup("Greeter") + if err != nil { + log.Println(err) + os.Exit(1) + } + + log.Println("symGreater", symGreeter) + + // 3. Assert that loaded symbol is of a desired type + // in this case interface type Greeter (defined above) + // var greeter Greeter + greeter, ok := symGreeter.(Greeter) + if !ok { + log.Println("unexpected type from module symbol") + os.Exit(1) + } + + // 4. use the module + greeter.Greet() +} diff --git a/cmds/helloworld/main.go b/cmds/helloworld/main.go index 3ae3b07..552ecb5 100644 --- a/cmds/helloworld/main.go +++ b/cmds/helloworld/main.go @@ -1,44 +1,24 @@ -// This creates a simple hello world window +// This is a simple example package main import ( - "os" "log" "git.wit.org/wit/gui" ) func main() { - gui.Main(myGUI) + gui.Main(helloworld) } -// This initializes the first window -func myGUI() { +// This creates a window +func helloworld() { var w *gui.Node - gui.Config.Title = "Hello World golang wit/gui Window" + gui.Config.Title = "helloworld golang wit/gui window" gui.Config.Width = 640 gui.Config.Height = 480 - gui.Config.Exit = myExit w = gui.NewWindow() - addHelloWorld(w, "A Simple Tab") + w.NewButton("hello", func () { + log.Println("world") + }) } - -func addHelloWorld(window *gui.Node, title string) { - var newNode, g, tb *gui.Node - - newNode = window.NewTab(title) - - g = newNode.NewGroup("hello") - tb = g.NewTextbox("hello world box") // when debugging, this string will be used - tb.OnChanged = func(*gui.Node) { - s := tb.GetText() - log.Println("text box =", s) - } - tb.SetText("world") -} - -func myExit(n *gui.Node) { - log.Println("exit() here") - os.Exit(0) -} - diff --git a/doc.go b/doc.go index ffb13e2..541cdc1 100644 --- a/doc.go +++ b/doc.go @@ -18,8 +18,7 @@ Principles: * It's ok to guess. We will return something close. * Hide complexity internally here * Isolate the GUI toolkit - * Function names should follow [Wikipedia Graphical widget] - + * Try to use [Wikipedia Graphical widget] names Quick Start diff --git a/main.go b/main.go index e87f6c2..30eb6a7 100644 --- a/main.go +++ b/main.go @@ -2,33 +2,33 @@ package gui import ( "log" + "os" ) import toolkit "git.wit.org/wit/gui/toolkit/andlabs" - -// the _ means we only need this for the init() - -const Xaxis = 0 // box that is horizontal -const Yaxis = 1 // box that is vertical +const Xaxis = 0 // stack things horizontally +const Yaxis = 1 // stack things vertically func init() { log.Println("gui.init() has been run") Config.counter = 0 Config.prefix = "wit" - Config.Options.DebugNode = false - Config.Options.DebugTabs = false - title := "master" + // Config.Options.Debug = true + // Config.Options.DebugNode = true + // Config.Options.DebugTabs = true + + title := "guiBinaryTree" w := 640 h := 480 - // f := StandardClose + // Populates the top of the binary tree Config.master = addNode(title, w, h) - // Config.master.custom = f - - Config.master.Dump() + if (Config.Options.Debug) { + Config.master.Dump() + } } func Main(f func()) { @@ -46,3 +46,19 @@ func Queue(f func()) { log.Println("Sending function to gui.Main() (using gtk via andlabs/ui)") toolkit.Queue(f) } + +// The window is destroyed but the application does not quit +func StandardClose(n *Node) { + if (Config.Options.Debug) { + log.Println("wit/gui Standard Window Close. name =", n.Name) + } +} + + +// The window is destroyed but the application does not quit +func StandardExit(n *Node) { + if (Config.Options.Debug) { + log.Println("wit/gui Standard Window Exit. running os.Exit()") + } + os.Exit(0) +} diff --git a/node.go b/node.go index d1b172a..57ad65d 100644 --- a/node.go +++ b/node.go @@ -1,8 +1,5 @@ package gui -import "strconv" -// import "fmt" - /* generic function to create a new node on the binary tree */ @@ -25,9 +22,12 @@ func addNode(title string, w int, h int) *Node { n.Width = w n.Height = h - id := Config.prefix + strconv.Itoa(Config.counter) + // no longer a string + // id := Config.prefix + strconv.Itoa(Config.counter) + // n.id = id + + n.id = Config.counter Config.counter += 1 - n.id = id return &n } diff --git a/structs.go b/structs.go index e72e28a..2a0493e 100644 --- a/structs.go +++ b/structs.go @@ -21,7 +21,15 @@ import toolkit "git.wit.org/wit/gui/toolkit/andlabs" var Config GuiConfig -func SetDebugToolkit (s bool) { +func GetDebug () bool { + return Config.Options.Debug +} + +func SetDebug (s bool) { + Config.Options.Debug = s + // also set these + Config.Options.DebugDump = s + Config.Options.DebugNode = s toolkit.DebugToolkit = s } @@ -29,6 +37,10 @@ func GetDebugToolkit () bool { return toolkit.DebugToolkit } +func SetDebugToolkit (s bool) { + toolkit.DebugToolkit = s +} + func ShowDebugValues() { log.Println("\t wit/gui Debug =", Config.Options.Debug) log.Println("\t wit/gui DebugDump =", Config.Options.DebugDump) @@ -102,23 +114,27 @@ func (s Widget) String() string { // The Node is simply the name and the size of whatever GUI element exists type Node struct { - id string + id int Name string Width int Height int + // this function is run when there are mouse or keyboard events + OnChanged func(*Node) + parent *Node - // TODO: make children a double linked list since some toolkits require order + // TODO: make children a double linked list since some toolkits require order (?) children []*Node + // hmm. how do you handle this when the toolkits are plugins? + toolkit *toolkit.Toolkit + // things that may not really be needed (?) custom func() - OnChanged func(*Node) checked bool text string - toolkit *toolkit.Toolkit } func (n *Node) Parent() *Node { @@ -159,12 +175,12 @@ func (n *Node) Dump() { IndentPrintln("toolkit = ", reflect.ValueOf(n.toolkit).Kind()) n.toolkit.Dump() } - if (n.id == "") { - // 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") - } +// if (n.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") +// } IndentPrintln("NODE DUMP END") } diff --git a/toolkit/andlabs/box.go b/toolkit/andlabs/box.go index ac31f0d..8347bab 100644 --- a/toolkit/andlabs/box.go +++ b/toolkit/andlabs/box.go @@ -37,6 +37,19 @@ func (t *Toolkit) NewBox() *Toolkit { return &newTK } + if (t.uiWindow != nil) { + log.Println("\tgui.Toolbox.NewBox() is a Window") + var newT Toolkit + + vbox := ui.NewVerticalBox() + vbox.SetPadded(padded) + t.uiWindow.SetChild(vbox) + newT.uiBox = vbox + newT.Name = t.Name + + // panic("WTF") + return &newT + } log.Println("\tgui.Toolbox.NewBox() FAILED. Couldn't figure out where to make a box") t.Dump() return nil diff --git a/toolkit/andlabs/button.go b/toolkit/andlabs/button.go index 4e6ff52..e0b7d97 100644 --- a/toolkit/andlabs/button.go +++ b/toolkit/andlabs/button.go @@ -1,7 +1,7 @@ package toolkit import "log" -import "os" +// import "os" import "github.com/andlabs/ui" import _ "github.com/andlabs/ui/winmanifest" @@ -11,15 +11,12 @@ func (t Toolkit) NewButton(name string) *Toolkit { var newt Toolkit var b *ui.Button - if (t.uiBox == nil) { - log.Println("gui.ToolboxNode.NewButton() node.UiBox == nil. I can't add a range UI element without a place to put it") - log.Println("probably could just make a box here?") - os.Exit(0) + if t.broken() { return nil } if (DebugToolkit) { - log.Println("gui.Toolbox.NewGroup() create", name) + log.Println("gui.Toolbox.NewButton() create", name) } b = ui.NewButton(name) newt.uiButton = b @@ -40,6 +37,7 @@ func (t Toolkit) NewButton(name string) *Toolkit { log.Println("wit/gui/toolkit NewButton() toolkit.Custom() START") } newt.Custom() + return if (DebugToolkit) { log.Println("wit/gui/toolkit NewButton() toolkit.Custom() END") } @@ -53,14 +51,29 @@ func (t Toolkit) NewButton(name string) *Toolkit { log.Println("wit/gui/toolkit NewButton() running parent toolkit.Custom() START (IS THIS A BAD IDEA?)") } t.Custom() + return if (DebugToolkit) { log.Println("wit/gui/toolkit NewButton() running parent toolkit.Custom() END (IS THIS A BAD IDEA?)") } } - log.Println("TODO: LEFT TOOLKIT GOROUTINE button name =", name) + log.Println("TODO: LEFT TOOLKIT GOROUTINE WITH NOTHING TO DO button name =", name) }) - t.uiBox.Append(b, stretchy) + if (DebugToolkit) { + log.Println("gui.Toolbox.NewButton() about to append to Box parent t:", name) + t.Dump() + log.Println("gui.Toolbox.NewButton() about to append to Box new t:", name) + newt.Dump() + } + if (t.uiBox != nil) { + t.uiBox.Append(b, stretchy) + } else if (t.uiWindow != nil) { + t.uiWindow.SetChild(b) + } else { + log.Println("ERROR: wit/gui andlabs couldn't place this button in a box or a window") + log.Println("ERROR: wit/gui andlabs couldn't place this button in a box or a window") + return &t + } return &newt } diff --git a/toolkit/andlabs/common.go b/toolkit/andlabs/common.go index 569621d..e997aca 100644 --- a/toolkit/andlabs/common.go +++ b/toolkit/andlabs/common.go @@ -2,9 +2,6 @@ package toolkit import "log" -// import "github.com/andlabs/ui" -// import _ "github.com/andlabs/ui/winmanifest" - func init() { log.Println("gui/toolkit init() Setting defaultBehavior = true") setDefaultBehavior(true) @@ -37,8 +34,13 @@ func (t Toolkit) commonChange(widget string) { // does some sanity checks on the internal structs of the binary tree // TODO: probably this should not panic unless it's running in devel mode (?) -func (t Toolkit) broken() bool { +func (t *Toolkit) broken() bool { if (t.uiBox == nil) { + if (t.uiWindow != nil) { + log.Println("gui.Toolkit.UiBox == nil. This is an empty window. Try to add a box") + t.NewBox() + return false + } log.Println("gui.Toolkit.UiBox == nil. I can't add a widget without a place to put it") // log.Println("probably could just make a box here?") // corruption or something horrible? @@ -48,6 +50,7 @@ func (t Toolkit) broken() bool { } if (t.uiWindow == nil) { log.Println("gui.Toolkit.UiWindow == nil. I can't add a widget without a place to put it (IGNORING FOR NOW)") + forceDump(t) return false } return false diff --git a/toolkit/andlabs/group.go b/toolkit/andlabs/group.go index f48a78d..5b315a4 100644 --- a/toolkit/andlabs/group.go +++ b/toolkit/andlabs/group.go @@ -10,19 +10,21 @@ import _ "github.com/andlabs/ui/winmanifest" func (t Toolkit) NewGroup(title string) *Toolkit { var newt Toolkit - if (t.uiBox == nil) { - log.Println("gui.ToolboxNode.NewGroup() node.UiBox == nil. I can't add a range UI element without a place to put it") - log.Println("probably could just make a box here?") - os.Exit(0) - return nil - } - if (DebugToolkit) { log.Println("gui.Toolbox.NewGroup() create", title) } g := ui.NewGroup(title) g.SetMargined(margin) - t.uiBox.Append(g, stretchy) + + if (t.uiBox != nil) { + t.uiBox.Append(g, stretchy) + } else if (t.uiWindow != nil) { + t.uiWindow.SetChild(g) + } else { + log.Println("gui.ToolboxNode.NewGroup() node.UiBox == nil. I can't add a range UI element without a place to put it") + log.Println("probably could just make a box here?") + os.Exit(0) + } hbox := ui.NewVerticalBox() hbox.SetPadded(padded) @@ -30,6 +32,7 @@ func (t Toolkit) NewGroup(title string) *Toolkit { newt.uiGroup = g newt.uiBox = hbox + newt.uiWindow = t.uiWindow newt.Name = title t.Dump() diff --git a/toolkit/andlabs/main.go b/toolkit/andlabs/main.go index cea0486..d1dc7e6 100644 --- a/toolkit/andlabs/main.go +++ b/toolkit/andlabs/main.go @@ -4,6 +4,7 @@ import ( "log" "github.com/andlabs/ui" + // the _ means we only need this for the init() _ "github.com/andlabs/ui/winmanifest" ) diff --git a/toolkit/andlabs/structs.go b/toolkit/andlabs/structs.go index 90f9409..374627c 100644 --- a/toolkit/andlabs/structs.go +++ b/toolkit/andlabs/structs.go @@ -87,9 +87,10 @@ func (t *Toolkit) String() string { } func forceDump(t *Toolkit) { + tmp := DebugToolkit DebugToolkit = true t.Dump() - DebugToolkit = false + DebugToolkit = tmp } func (t *Toolkit) GetText() string { diff --git a/toolkit/gocui/Makefile b/toolkit/gocui/Makefile new file mode 100644 index 0000000..42798cc --- /dev/null +++ b/toolkit/gocui/Makefile @@ -0,0 +1,8 @@ +all: plugin + ldd ../gocli.so + +build: + GO111MODULE="off" go build + +plugin: + GO111MODULE="off" go build -buildmode=plugin -o ../gocli.so diff --git a/toolkit/gocui/greeter.go b/toolkit/gocui/greeter.go new file mode 100644 index 0000000..9d546a7 --- /dev/null +++ b/toolkit/gocui/greeter.go @@ -0,0 +1,57 @@ +package main + +import ( + "errors" + "fmt" + "log" + "strings" + + "github.com/awesome-gocui/gocui" +) + +type greeting string + + +// func main() { +func (g greeting) Greet() { + fmt.Println("Hello Universe") + Init() + // ToolkitMain() +} + +// this is exported +var Greeter greeting + +func AddGroup(name string) { + log.Println("addGroup()", name) + currentY = 2 + currentX += groupSize + 6 +} + +func AddButton(name string) error { + t := len(name) + v, err := baseGui.SetView(name, currentX, currentY, currentX+t+3, currentY+2, 0) + if err == nil { + return err + } + if !errors.Is(err, gocui.ErrUnknownView) { + return err + } + + v.Wrap = true + fmt.Fprintln(v, " " + name) + fmt.Fprintln(v, strings.Repeat("foo\n", 2)) + + if _, err := baseGui.SetCurrentView(name); err != nil { + return err + } + + views = append(views, name) + curView = len(views) - 1 + idxView += 1 + currentY += 3 + if (groupSize < len(views)) { + groupSize = len(views) + } + return nil +} diff --git a/toolkit/gocui/keybindings.go b/toolkit/gocui/keybindings.go new file mode 100644 index 0000000..fdac1ff --- /dev/null +++ b/toolkit/gocui/keybindings.go @@ -0,0 +1,130 @@ +// Copyright 2014 The gocui Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( +// "errors" +// "fmt" + "log" +// "strings" + + "github.com/awesome-gocui/gocui" +) + +func initKeybindings(g *gocui.Gui) error { + log.Println("got to initKeybindings") + if err := g.SetKeybinding("", 'q', gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return gocui.ErrQuit + }); err != nil { + return err + } + if err := g.SetKeybinding("", 'Q', gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return gocui.ErrQuit + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return gocui.ErrQuit + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeySpace, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return newView(g) + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyBackspace, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return delView(g) + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyBackspace2, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return delView(g) + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + log.Println("tab", v.Name()) + return nextView(g, true) + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyArrowLeft, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return moveView(g, v, -delta, 0) + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyArrowRight, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return moveView(g, v, delta, 0) + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + log.Println("down", v.Name()) + return moveView(g, v, 0, delta) + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + log.Println("up", v.Name()) + return moveView(g, v, 0, -delta) + }); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.KeyEnter, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + log.Println("enter", v.Name()) + return nil + }); err != nil { + return err + } + if err := g.SetKeybinding("", 't', gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + _, err := g.SetViewOnTop(views[curView]) + return err + }); err != nil { + return err + } + if err := g.SetKeybinding("", 'b', gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + _, err := g.SetViewOnBottom(views[curView]) + return err + }); err != nil { + return err + } + if err := g.SetKeybinding("", 'j', gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + return newJ(g) + }); err != nil { + return err + } + if err := g.SetKeybinding("", 'h', gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + log.Println("help", v.Name()) + tmp, _ := g.SetViewOnTop("help") + log.Println("help 2", tmp.Name(), "blah") +// g.SetView("help", 2, 2, 30, 15, 0); + g.SetCurrentView("help") +// moveView(g, tmp, 0, -delta) + if err := g.DeleteView("help"); err != nil { + panic(err) + } + return nil + }); err != nil { + return err + } + return nil +} diff --git a/toolkit/gocui/main.go b/toolkit/gocui/main.go new file mode 100644 index 0000000..2439a3a --- /dev/null +++ b/toolkit/gocui/main.go @@ -0,0 +1,97 @@ +// Copyright 2014 The gocui Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "errors" + "fmt" + "log" + + "github.com/awesome-gocui/gocui" +) + +const delta = 1 + +var ( + views = []string{} + curView = -1 + idxView = 0 + currentX = 5 + currentY = 2 + groupSize = 0 + baseGui *gocui.Gui +) + +var helpLabel *gocui.View + +func Init() { + // setup log to write to a file +// logInit() + + g, err := gocui.NewGui(gocui.OutputNormal, true) + baseGui = g + if err != nil { + log.Panicln(err) + } + defer g.Close() + + g.Highlight = true + g.SelFgColor = gocui.ColorRed + g.SelFrameColor = gocui.ColorRed + + g.SetManagerFunc(layout) + + if err := initKeybindings(g); err != nil { + log.Panicln(err) + } + if err := newView(g); err != nil { + log.Panicln(err) + } + + AddButton("hello") + AddButton("world") + AddButton("foo") + + AddGroup("blank") + AddButton("bar") + AddButton("bar none") + AddButton("bar going") + + AddGroup("te") + AddButton("world 2") + AddButton("foo 2") + + if err := baseGui.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) { + log.Panicln(err) + } +} + +func ToolkitMain() { + if err := baseGui.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) { + log.Panicln(err) + } +} + +func layout(g *gocui.Gui) error { + var err error + maxX, _ := g.Size() + helpLabel, err = g.SetView("help", maxX-32, 0, maxX-1, 11, 0) + if err != nil { + if !errors.Is(err, gocui.ErrUnknownView) { + return err + } + fmt.Fprintln(helpLabel, "KEYBINDINGS") + fmt.Fprintln(helpLabel, "Enter: Click Button") + fmt.Fprintln(helpLabel, "Tab/Space: Switch Buttons") + fmt.Fprintln(helpLabel, "") + fmt.Fprintln(helpLabel, "h: Help") + fmt.Fprintln(helpLabel, "Backspace: Delete Button") + fmt.Fprintln(helpLabel, "Arrow keys: Move Button") + fmt.Fprintln(helpLabel, "t: Move Button to the top") + fmt.Fprintln(helpLabel, "b: Move Button to the button") + fmt.Fprintln(helpLabel, "Ctrl-C or Q: Exit") + } + return nil +} diff --git a/toolkit/gocui/newJ.go b/toolkit/gocui/newJ.go new file mode 100644 index 0000000..47c7439 --- /dev/null +++ b/toolkit/gocui/newJ.go @@ -0,0 +1,46 @@ +// Copyright 2014 The gocui Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "errors" + "fmt" + "log" + "strings" + + "github.com/awesome-gocui/gocui" +) + +var topX int = 2 +var bottomX int = 20 +var topY int = 2 +var bottomY int = 7 + +func newJ(g *gocui.Gui) error { + // maxX, maxY := g.Size() + name := fmt.Sprintf("jcarr %v test ", idxView) + v, err := g.SetView(name, topX, topY, bottomX, bottomY, 0) + if err == nil { + return err + } + if !errors.Is(err, gocui.ErrUnknownView) { + return err + } + + v.Wrap = true + fmt.Fprintln(v, name) + fmt.Fprintln(v, strings.Repeat("foo\n", 2)) + // fmt.Fprintln(v, strings.Repeat(name+" ", 30)) + log.Println("newJ added a new view", v.Name()) + + if _, err := g.SetCurrentView(name); err != nil { + return err + } + + views = append(views, name) + curView = len(views) - 1 + idxView += 1 + return nil +} diff --git a/toolkit/gocui/mouse.go b/toolkit/gocui/orig/mouse.go similarity index 100% rename from toolkit/gocui/mouse.go rename to toolkit/gocui/orig/mouse.go diff --git a/toolkit/gocui/views.go b/toolkit/gocui/views.go new file mode 100644 index 0000000..6c9c65d --- /dev/null +++ b/toolkit/gocui/views.go @@ -0,0 +1,80 @@ +// Copyright 2014 The gocui Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "errors" + "fmt" + "log" + "strings" + + "github.com/awesome-gocui/gocui" +) + +func newView(g *gocui.Gui) error { + maxX, maxY := g.Size() + name := fmt.Sprintf("v%v", idxView) + v, err := g.SetView(name, maxX/2-5, maxY/2-5, maxX/2+5, maxY/2+5, 0) + if err == nil { + return err + } + if !errors.Is(err, gocui.ErrUnknownView) { + return err + } + + v.Wrap = true + fmt.Fprintln(v, strings.Repeat(name+" ", 30)) + + if _, err := g.SetCurrentView(name); err != nil { + return err + } + + views = append(views, name) + curView = len(views) - 1 + idxView += 1 + return nil +} + +func delView(g *gocui.Gui) error { + if len(views) <= 1 { + return nil + } + + if err := g.DeleteView(views[curView]); err != nil { + return err + } + views = append(views[:curView], views[curView+1:]...) + + return nextView(g, false) +} + +func nextView(g *gocui.Gui, disableCurrent bool) error { + next := curView + 1 + if next > len(views)-1 { + next = 0 + } + + if _, err := g.SetCurrentView(views[next]); err != nil { + return err + } + + curView = next + return nil +} + +func moveView(g *gocui.Gui, v *gocui.View, dx, dy int) error { + name := v.Name() + x0, y0, x1, y1, err := g.ViewPosition(name) + if err != nil { + return err + } + log.Println(x0, y0, x1, y1) + if _, err := g.SetView(name, x0+dx, y0+dy, x1+dx, y1+dy, 0); err != nil { + return err + } + x0, y0, x1, y1, err = g.ViewPosition(name) + log.Println(x0, y0, x1, y1) + return nil +} diff --git a/toolkit/hello/Makefile b/toolkit/hello/Makefile new file mode 100644 index 0000000..8271843 --- /dev/null +++ b/toolkit/hello/Makefile @@ -0,0 +1,8 @@ +all: plugin + ldd ../hello.so + +build: + GO111MODULE="off" go build + +plugin: + GO111MODULE="off" go build -buildmode=plugin -o ../hello.so diff --git a/toolkit/hello/greeter.go b/toolkit/hello/greeter.go new file mode 100644 index 0000000..1eb3307 --- /dev/null +++ b/toolkit/hello/greeter.go @@ -0,0 +1,23 @@ +package main + +import ( +// "errors" + "fmt" +// "log" +// "strings" + +// "github.com/awesome-gocui/gocui" +) + +type greeting string + + +// func main() { +func (g greeting) Greet() { + fmt.Println("Hello Universe") + Init() + // ToolkitMain() +} + +// this is exported +var Greeter greeting diff --git a/toolkit/hello/main.go b/toolkit/hello/main.go new file mode 100644 index 0000000..3a72d1f --- /dev/null +++ b/toolkit/hello/main.go @@ -0,0 +1,44 @@ +// This creates a simple hello world window +package main + +import ( + "os" + "log" + "git.wit.org/wit/gui" +) + +func Init() { + gui.Main(myGUI) +} + +// This initializes the first window +func myGUI() { + var w *gui.Node + gui.Config.Title = "Hello World golang wit/gui Window" + gui.Config.Width = 640 + gui.Config.Height = 480 + gui.Config.Exit = myExit + + w = gui.NewWindow() + addHelloWorld(w, "A Simple Tab") +} + +func addHelloWorld(window *gui.Node, title string) { + var newNode, g, tb *gui.Node + + newNode = window.NewTab(title) + + g = newNode.NewGroup("hello") + tb = g.NewTextbox("hello world box") // when debugging, this string will be used + tb.OnChanged = func(*gui.Node) { + s := tb.GetText() + log.Println("text box =", s) + } + tb.SetText("world") +} + +func myExit(n *gui.Node) { + log.Println("exit() here") + os.Exit(0) +} + diff --git a/window-golang-debug.go b/window-golang-debug.go index 1624793..464ab09 100644 --- a/window-golang-debug.go +++ b/window-golang-debug.go @@ -9,14 +9,6 @@ import ( "runtime/pprof" ) -func StandardClose(n *Node) { - // origlog.Println("Should Exit Here") - // closed = true - log.Println("") - log.Println("STANDARD WINDOW CLOSE. Should not exit app.") - log.Println("") -} - func GolangDebugWindow() { var w, t *Node diff --git a/window.go b/window.go index 538e0fe..06db292 100644 --- a/window.go +++ b/window.go @@ -23,19 +23,23 @@ func NewWindow() *Node { // Windows are created off of the master node of the Binary Tree n = Config.master.New(title) - // n.custom = f + + n.OnChanged = Config.Exit t = toolkit.NewWindow(title, w, h) t.Custom = func () { - log.Println("Got to wit/gui Window Close START user defined close()") - if (n.custom == nil) { - log.Println("Got to wit/gui Window Close SKIP node.custom() == nil") + if (Config.Options.Debug) { + log.Println("Got to wit/gui Window Close START user defined close()") + } + if (n.OnChanged != nil) { + if (Config.Options.Debug) { + log.Println("Got to wit/gui Window Close SKIP node.custom() == nil") + } + n.OnChanged(n) return } - n.custom() - log.Println("Got to wit/gui Window Close END user defined close()") + StandardExit(n) } n.toolkit = t - return n }