gocui plugin refactor to a *node binary tree

rename arg '--gui <toolkit>'
    add a cloudflare example
    fixes since go v1.21 didn't compile anymore due to argv order
    more place() changes
    recursive size computation

    gocui: Major refactor to use the *node binary tree
    gocui: refactor place() and size()
    gocui: better place() and spacing (tab, buttons, etc)
    gocui: better mouse click handling
    gocui: switch to using tk.gocuiSize & tk.size
    gocui: event handling cleanups
    gocui: add window labels work
    gocui: struct cleanups
    gocui: duplicate binary tree structs removed
    gocui: deprecate old children

Signed-off-by: Jeff Carr <jcarr@wit.com>
This commit is contained in:
Jeff Carr 2023-12-02 19:02:51 -06:00
parent 603d5ba7de
commit fe6a8dd969
32 changed files with 1358 additions and 936 deletions

1
.gitignore vendored
View File

@ -8,6 +8,7 @@ cmds/console-ui-helloworld/console-ui-helloworld
cmds/debug/debug
cmds/helloworld/helloworld
cmds/textbox/textbox
cmds/cloudflare/cloudflare
cmds/*/helloconsole
# temporary files when building debian packages

View File

@ -8,6 +8,7 @@ all: README.md
@echo
make clean
make plugins
make cmds-buttonplugin
build-dep:
apt install -f libgtk-3-dev
@ -82,11 +83,11 @@ clean:
plugins: plugins-gocui plugins-andlabs
plugins-gocui:
GO111MODULE="off" go build -v -x -C toolkit/gocui -buildmode=plugin -o ../gocui.so
GO111MODULE="off" go build -v -x -C toolkit/nocui -buildmode=plugin -o ../nocui.so
GO111MODULE="off" go build -C toolkit/gocui -v -buildmode=plugin -o ../gocui.so
GO111MODULE="off" go build -C toolkit/nocui -v -buildmode=plugin -o ../nocui.so
plugins-andlabs:
GO111MODULE="off" go build -v -x -C toolkit/andlabs -buildmode=plugin -o ../andlabs.so
GO111MODULE="off" go build -C toolkit/andlabs -v -buildmode=plugin -o ../andlabs.so
objdump:
objdump -t toolkit/andlabs.so |less

View File

@ -139,7 +139,7 @@ Creates a window helpful for debugging this package
`func ShowDebugValues()`
### func [StandardExit](/main.go#L149)
### func [StandardExit](/main.go#L153)
`func StandardExit()`
@ -158,13 +158,19 @@ This goroutine can be used like a watchdog timer
## Types
### type [GuiArgs](/structs.go#L27)
### type [GuiArgs](/structs.go#L29)
`type GuiArgs struct { ... }`
This struct can be used with the go-arg package
### type [Node](/structs.go#L57)
#### Variables
```golang
var GuiArg GuiArgs
```
### type [Node](/structs.go#L59)
`type Node struct { ... }`

View File

@ -7,7 +7,7 @@
#
run: build
./buttonplugin --gui-toolkit gocui >/tmp/witgui.log.stderr 2>&1
./buttonplugin --gui gocui >/tmp/witgui.log.stderr 2>&1
build-release:
go get -v -u -x .

View File

@ -5,6 +5,7 @@ import (
"fmt"
arg "github.com/alexflint/go-arg"
"git.wit.org/wit/gui"
log "git.wit.org/wit/gui/log"
)
@ -14,6 +15,7 @@ var args struct {
User string `arg:"env:USER"`
Demo bool `help:"run a demo"`
gui.GuiArgs
log.LogArgs
}
/*
@ -26,6 +28,11 @@ func init() {
arg.MustParse(&args)
fmt.Println(args.Foo, args.Bar, args.User)
if (args.Gui != "") {
gui.GuiArg.Gui = args.Gui
}
log.Log(true, "INIT() args.GuiArg.Gui =", gui.GuiArg.Gui)
/*
log.Println()
log.Println("STDOUT is now at /tmp/guilogfile")

View File

@ -20,14 +20,7 @@ func main() {
// This will turn on all debugging
// gui.SetDebug(true)
// myGui = gui.New().LoadToolkit("gocui")
// myGui = gui.New().LoadToolkit("andlabs")
// myGui = gui.New().Default()
if (args.GuiToolkit == nil) {
myGui = gui.New().Default()
} else {
myGui = gui.New().LoadToolkit(args.GuiToolkit[0])
}
myGui = gui.New().Default()
buttonWindow()
// This is just a optional goroutine to watch that things are alive

18
cmds/cloudflare/Makefile Normal file
View File

@ -0,0 +1,18 @@
run: build
./cloudflare
build-release:
go get -v -u -x .
go build
./cloudflare
build:
GO111MODULE="off" go get -v -x .
GO111MODULE="off" go build
update:
GO111MODULE="off" go get -v -u -x .
log:
reset
tail -f /tmp/witgui.* /tmp/guilogfile

30
cmds/cloudflare/argv.go Normal file
View File

@ -0,0 +1,30 @@
// This creates a simple hello world window
package main
import (
"fmt"
arg "github.com/alexflint/go-arg"
"git.wit.org/wit/gui"
log "git.wit.org/wit/gui/log"
)
var args struct {
Foo string
Bar bool
User string `arg:"env:USER"`
Demo bool `help:"run a demo"`
gui.GuiArgs
log.LogArgs
}
func init() {
arg.MustParse(&args)
fmt.Println(args.Foo, args.Bar, args.User)
if (args.Gui != "") {
gui.GuiArg.Gui = args.Gui
}
log.Log(true, "INIT() args.GuiArg.Gui =", gui.GuiArg.Gui)
}

115
cmds/cloudflare/dns.go Normal file
View File

@ -0,0 +1,115 @@
// This is a simple example
package main
import (
"os"
"log"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strconv"
)
// Define a struct to match the JSON structure of the response.
// This structure should be adjusted based on the actual format of the response.
type DNSRecords struct {
Result []struct {
ID string `json:"id"`
Type string `json:"type"`
Name string `json:"name"`
Content string `json:"content"`
Proxied bool `json:"proxied"`
Proxiable bool `json:"proxiable"`
TTL int `json:"ttl"`
} `json:"result"`
}
// var domain string = "wit.org"
// var os.Getenv("CLOUDFLARE_DOMAIN")
func loadDNS(hostname string) {
log.Println("adding DNS record")
// more2.NewButton(name, func () {
// log.Println(name, "ip =", ip)
// })
newt := mainWindow.NewTab(hostname)
newg := newt.NewGroup("more")
more2 := newg.NewGrid("gridnuts", 5, gridH)
records := getRecords()
for _, record := range records.Result {
more2.NewLabel(record.Type)
more2.NewLabel(record.Name)
if (record.Proxied) {
more2.NewLabel("Proxied")
} else {
more2.NewLabel("DNS")
}
var ttl, short string
if (record.TTL == 1) {
ttl = "Auto"
} else {
ttl = strconv.Itoa(record.TTL)
}
more2.NewLabel(ttl)
// short = fmt.Sprintf("%80s", record.Content)
short = record.Content
if len(short) > 40 {
short = short[:40] // Slice the first 20 characters
}
more2.NewLabel(short)
fmt.Printf("ID: %s, Type: %s, Name: %s, short Content: %s\n", record.ID, record.Type, record.Name, short)
fmt.Printf("\tproxied: %b, %b, string TTL: %i\n", record.Proxied, record.Proxiable, ttl)
}
}
func getRecords() *DNSRecords {
var url string = os.Getenv("CLOUDFLARE_URL")
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println(err)
return nil
}
var authKey string = os.Getenv("CLOUDFLARE_AUTHKEY")
var email string = os.Getenv("CLOUDFLARE_EMAIL")
// Set headers
req.Header.Set("X-Auth-Key", authKey)
req.Header.Set("X-Auth-Email", email)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println(err)
return nil
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return nil
}
var records DNSRecords
if err := json.Unmarshal(body, &records); err != nil {
fmt.Println(err)
return nil
}
// Process the records as needed
/*
for _, record := range records.Result {
fmt.Printf("ID: %s, Type: %s, Name: %s, Content: %s\n", record.ID, record.Type, record.Name, record.Content)
fmt.Printf("\tproxied: %b, %b, TTL: %i\n", record.Proxied, record.Proxiable, record.TTL)
}
*/
return &records
}

122
cmds/cloudflare/main.go Normal file
View File

@ -0,0 +1,122 @@
// This is a simple example
package main
import (
"os"
"fmt"
"log"
"strconv"
"git.wit.org/wit/gui"
)
var title string = "Cloudflare DNS Control Panel"
var outfile string = "/tmp/guilogfile"
var myGui *gui.Node
var buttonCounter int = 5
var gridW int = 5
var gridH int = 3
var mainWindow, more, more2 *gui.Node
func main() {
myGui = gui.New().Default()
buttonWindow()
// This is just a optional goroutine to watch that things are alive
gui.Watchdog()
gui.StandardExit()
}
// This creates a window
func buttonWindow() {
var t, g *gui.Node
log.Println("buttonWindow() START")
mainWindow = myGui.NewWindow(title).SetText(title)
t = mainWindow.NewTab("Cloudflare")
g = t.NewGroup("buttons")
g1 := t.NewGroup("buttonGroup 2")
more = g1.NewGroup("more")
showCloudflareCredentials(more)
g1.NewButton("hello", func () {
log.Println("world")
})
more2 = g1.NewGrid("gridnuts", gridW, gridH)
var domain string = os.Getenv("CLOUDFLARE_DOMAIN")
if (domain == "") {
domain = "example.org"
}
g.NewButton("Load " + domain + " DNS", func () {
loadDNS(domain)
})
g.NewButton("Load 'gocui'", func () {
// this set the xterm and mate-terminal window title. maybe works generally?
fmt.Println("\033]0;" + title + "blah \007")
myGui.LoadToolkit("gocui")
})
g.NewButton("Load 'andlabs'", func () {
myGui.LoadToolkit("andlabs")
})
g.NewButton("NewButton(more)", func () {
name := "foobar " + strconv.Itoa(buttonCounter)
log.Println("NewButton(more) Adding button", name)
buttonCounter += 1
more.NewButton(name, func () {
log.Println("Got all the way to main() name =", name)
})
})
g.NewButton("NewButton(more2)", func () {
name := "foobar " + strconv.Itoa(buttonCounter)
log.Println("NewButton(more2) Adding button", name)
buttonCounter += 1
more2.NewButton(name, func () {
log.Println("Got all the way to main() name =", name)
})
})
g.NewButton("NewButton(more2 d)", func () {
name := "d" + strconv.Itoa(buttonCounter)
log.Println("NewButton(more2 d) Adding button", name)
buttonCounter += 1
more2.NewButton(name, func () {
log.Println("Got all the way to main() name =", name)
})
})
g.NewButton("NewGroup()", func () {
name := "neat " + strconv.Itoa(buttonCounter)
log.Println("NewGroup() Adding button", name)
buttonCounter += 1
more.NewGroup(name)
})
g.NewButton("gui.DebugWindow()", func () {
gui.DebugWindow()
})
}
func showCloudflareCredentials(box *gui.Node) {
grid := box.NewGrid("credsGrid", 2, 4) // width = 2
grid.NewLabel("Domain")
grid.NewLabel(os.Getenv("CLOUDFLARE_DOMAIN"))
grid.NewLabel("Auth Key")
grid.NewLabel(os.Getenv("CLOUDFLARE_AUTHKEY"))
grid.NewLabel("Email")
grid.NewLabel(os.Getenv("CLOUDFLARE_EMAIL"))
grid.NewLabel("URL")
grid.NewLabel(os.Getenv("CLOUDFLARE_URL"))
}

13
log/structs.go Normal file
View File

@ -0,0 +1,13 @@
package witlog
import (
)
//
// Attempt to switch logging to syslog on linux
//
// This struct can be used with the go-arg package
type LogArgs struct {
Log []string `arg:"--log" help:"Where to log [syslog,stdout]"`
}

View File

@ -123,6 +123,10 @@ func New() *Node {
// try to load andlabs, if that doesn't work, fall back to the console
func (n *Node) Default() *Node {
if (GuiArg.Gui != "") {
log(logError, "New.Default() try toolkit =", GuiArg.Gui)
return n.LoadToolkit(GuiArg.Gui)
}
// if DISPLAY isn't set, return since gtk can't load
// TODO: figure out how to check what to do in macos and mswindows
if (os.Getenv("DISPLAY") == "") {

View File

@ -119,8 +119,15 @@ func searchPaths(name string) *aplug {
filename = "plugins/" + name + ".so"
pfile, err = me.resFS.ReadFile(filename)
if (err == nil) {
filename = "/tmp/" + name + ".so"
log(logError, "write out file here", name, filename, len(pfile))
exit()
f, _ := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0600)
f.Write(pfile)
f.Close()
p := initToolkit(name, filename)
if (p != nil) {
return p
}
} else {
log(logError, filename, "was not embedded. Error:", err)
}

View File

@ -23,9 +23,11 @@ import (
var me guiConfig
var GuiArg GuiArgs
// This struct can be used with the go-arg package
type GuiArgs struct {
GuiToolkit []string `arg:"--gui-toolkit" help:"The order to attempt loading plugins [gocui,andlabs,gtk,qt]"`
Gui string `arg:"--gui" help:"Use this gui toolkit [andlabs,gocui,nocui]"`
GuiDebug bool `arg:"--gui-debug" help:"open the GUI debugger"`
GuiVerbose bool `arg:"--gui-verbose" help:"enable all logging"`
}

View File

@ -1,88 +1,67 @@
package main
import (
// "github.com/awesome-gocui/gocui"
"git.wit.org/wit/gui/toolkit"
)
// TODO: make these defaults in config struct definition
var fakeStartWidth int = me.DevelOffsetW
var fakeStartWidth int = me.FakeW
var fakeStartHeight int = me.TabH + me.FramePadH
func (w *cuiWidget) setFake() {
// setup fake labels for non-visible things off screen
func (n *node) setFake() {
w := n.tk
w.isFake = true
t := len(w.name)
// setup fake labels for non-visable things off screen
w.gocuiSize.w0 = fakeStartWidth
w.gocuiSize.h0 = fakeStartHeight
w.gocuiSize.w1 = w.gocuiSize.w0 + t + me.PadW
w.gocuiSize.h1 = w.gocuiSize.h0 + me.DefaultHeight + me.PadH
n.gocuiSetWH(fakeStartWidth, fakeStartHeight)
w.realWidth = w.gocuiSize.Width() + me.FramePadW
w.realHeight = w.gocuiSize.Height() + me.FramePadH
fakeStartHeight += w.realHeight
fakeStartHeight += w.gocuiSize.Height()
// TODO: use the actual max hight of the terminal window
if (fakeStartHeight > 24) {
fakeStartHeight = me.TabH + me.FramePadH
fakeStartWidth += me.DevelOffsetW
fakeStartHeight = me.TabH
fakeStartWidth += me.FakeW
}
if (logInfo) {
w.showView()
n.showView()
}
}
// set the widget start width & height
func (w *cuiWidget) addWidget() {
log(logInfo, "setStartWH() w.id =", w.id, "w.name", w.name)
switch w.widgetType {
func (n *node) addWidget() {
nw := n.tk
log(logInfo, "setStartWH() w.id =", n.WidgetId, "n.name", n.Name)
switch n.WidgetType {
case toolkit.Root:
log(logInfo, "setStartWH() rootNode w.id =", w.id, "w.name", w.name)
w.setFake()
log(logInfo, "setStartWH() rootNode w.id =", n.WidgetId, "w.name", n.Name)
n.setFake()
return
case toolkit.Flag:
w.setFake()
n.setFake()
return
case toolkit.Window:
me.current = w
updateCurrentTabs()
setCurrentWindow(w)
nw.frame = false
redoWindows(0,0)
return
case toolkit.Tab:
// if this is the first tab, set it to the current one and stay here
if (me.current != nil) {
// there is already a current tab. just redraw the tabs
updateCurrentTabs()
return
}
setCurrentTab(w)
return
case toolkit.Box:
w.isFake = true
w.setFake()
w.startW = w.parent.startW
w.startH = w.parent.startH
nw.isFake = true
n.setFake()
return
case toolkit.Grid:
w.isFake = true
w.setFake()
w.startW = w.parent.startW
w.startH = w.parent.startH
nw.isFake = true
n.setFake()
return
case toolkit.Group:
w.startW = w.parent.startW + 4
w.startH = w.parent.startH + me.DefaultHeight + me.FramePadH
t := len(w.text)
w.gocuiSize.w1 = w.gocuiSize.w0 + t + me.FramePadW
w.gocuiSize.h1 = w.gocuiSize.h0 + me.DefaultHeight + me.FramePadH
nw.frame = false
return
case toolkit.Label:
nw.frame = false
return
default:
w.startW = w.parent.startW
w.startH = w.parent.startH
if w.IsCurrent() {
w.updateCurrent()
/*
if n.IsCurrent() {
n.updateCurrent()
}
*/
}
w.showWidgetPlacement(logInfo, "addWidget()")
n.showWidgetPlacement(logInfo, "addWidget()")
}

View File

@ -5,28 +5,29 @@ import (
"git.wit.org/wit/gui/toolkit"
)
func (w *cuiWidget) setCheckbox(b bool) {
if (w.widgetType != toolkit.Checkbox) {
func (n *node) setCheckbox(b bool) {
w := n.tk
if (n.WidgetType != toolkit.Checkbox) {
return
}
if (b) {
w.b = b
w.text = "X " + w.name
n.B = b
n.Text = "X " + n.Name
} else {
w.b = b
w.text = " " + w.name
n.B = b
n.Text = " " + n.Name
}
t := len(w.text) + 1
t := len(n.Text) + 1
w.gocuiSize.w1 = w.gocuiSize.w0 + t
w.realWidth = w.gocuiSize.Width() + me.PadW
w.realHeight = w.gocuiSize.Height() + me.PadH
// w.realWidth = w.gocuiSize.Width() + me.PadW
// w.realHeight = w.gocuiSize.Height() + me.PadH
if w.frame {
w.realWidth += me.FramePadW
w.realHeight += me.FramePadH
}
// if w.frame {
// w.realWidth += me.FramePadW
// w.realHeight += me.FramePadH
// }
w.deleteView()
w.showView()
n.deleteView()
n.showView()
}

View File

@ -1,278 +1,275 @@
package main
import (
// "fmt"
// "errors"
"strconv"
"strings"
"github.com/awesome-gocui/gocui"
"git.wit.org/wit/gui/toolkit"
)
// set isCurrent = false everywhere
func UnsetCurrent(w *cuiWidget) {
func UnsetCurrent(n *node) {
w := n.tk
w.isCurrent = false
for _, child := range w.children {
for _, child := range n.children {
UnsetCurrent(child)
}
}
func updateCurrentTabs() {
me.rootNode.nextW = 0
me.rootNode.nextH = 0
me.rootNode.redoTabs(true)
}
// when adding a new widget, this will update the display
// of the current widgets if that widget is supposed
// to be in current display
func (w *cuiWidget) updateCurrent() {
if w.widgetType == toolkit.Tab {
if w.IsCurrent() {
setCurrentTab(w)
func (n *node) updateCurrent() {
log("updateCurrent()", n.Name)
if n.WidgetType == toolkit.Tab {
if n.IsCurrent() {
setCurrentTab(n)
}
return
}
if w.widgetType == toolkit.Window {
if w.IsCurrent() {
setCurrentWindow(w)
if n.WidgetType == toolkit.Window {
if n.IsCurrent() {
// setCurrentWindow(n)
}
return
}
if w.widgetType == toolkit.Root {
if n.WidgetType == toolkit.Root {
return
}
w.parent.updateCurrent()
n.parent.updateCurrent()
}
// shows the widgets in a window
func setCurrentWindow(w *cuiWidget) {
if w.widgetType != toolkit.Window {
func setCurrentWindow(n *node) {
if n.IsCurrent() {
return
}
w := n.tk
if n.WidgetType != toolkit.Window {
return
}
UnsetCurrent(me.rootNode)
me.rootNode.hideWidgets()
// THIS IS THE BEGINING OF THE LAYOUT
me.rootNode.nextW = 0
me.rootNode.nextH = 0
w.isCurrent = true
if w.hasTabs {
if n.hasTabs {
// set isCurrent = true on the first tab
for _, child := range w.children {
child.isCurrent = true
for _, child := range n.children {
child.tk.isCurrent = true
break
}
} else {
w.isCurrent = true
}
me.rootNode.redoTabs(true)
w.placeWidgets()
w.showWidgets()
}
// shows the widgets in a tab
func setCurrentTab(w *cuiWidget) {
if w.widgetType != toolkit.Tab {
func setCurrentTab(n *node) {
w := n.tk
if n.WidgetType != toolkit.Tab {
return
}
me.current = w
UnsetCurrent(me.rootNode)
me.rootNode.hideWidgets()
w.isCurrent = true
w.parent.isCurrent = true
updateCurrentTabs()
w.placeWidgets()
w.showWidgets()
p := n.parent.tk
p.isCurrent = true
log("setCurrent()", n.Name)
}
func (w *cuiWidget) doWidgetClick() {
switch w.widgetType {
func (n *node) doWidgetClick() {
switch n.WidgetType {
case toolkit.Root:
// THIS IS THE BEGINING OF THE LAYOUT
me.rootNode.nextW = 0
me.rootNode.nextH = 0
me.rootNode.redoTabs(true)
log("doWidgetClick()", n.Name)
redoWindows(0,0)
case toolkit.Flag:
// me.rootNode.redoColor(true)
me.rootNode.dumpTree(true)
case toolkit.Window:
setCurrentWindow(w)
case toolkit.Tab:
setCurrentTab(w)
case toolkit.Group:
w.placeWidgets()
w.toggleTree()
case toolkit.Checkbox:
if (w.b) {
w.setCheckbox(false)
} else {
w.setCheckbox(true)
}
w.doUserEvent()
case toolkit.Grid:
me.rootNode.hideWidgets()
w.placeGrid()
w.showWidgets()
n.redoTabs(me.TabW, me.TabH)
if ! n.hasTabs {
setCurrentWindow(n)
n.placeWidgets(me.RawW, me.RawH)
n.showWidgets()
}
case toolkit.Tab:
setCurrentTab(n)
n.placeWidgets(me.RawW, me.RawH)
n.showWidgets()
case toolkit.Group:
// n.placeWidgets(p.tk.startH, newH)
n.toggleTree()
case toolkit.Checkbox:
if (n.B) {
n.setCheckbox(false)
} else {
n.setCheckbox(true)
}
n.doUserEvent()
case toolkit.Grid:
n.placeGrid(n.tk.size.w0, n.tk.size.h0)
n.showWidgets()
case toolkit.Box:
// w.showWidgetPlacement(logNow, "drawTree()")
if (w.horizontal) {
log("BOX IS HORIZONTAL", w.name)
if (n.horizontal) {
log("BOX IS HORIZONTAL", n.Name)
} else {
log("BOX IS VERTICAL", w.name)
log("BOX IS VERTICAL", n.Name)
}
w.placeWidgets()
w.toggleTree()
// n.placeWidgets()
n.toggleTree()
case toolkit.Button:
w.doUserEvent()
n.doUserEvent()
default:
}
}
// this passes the user event back from the plugin
func (w *cuiWidget) doUserEvent() {
if (me.callback == nil) {
log(logError, "doUserEvent() no callback channel was configured")
return
}
var a toolkit.Action
a.WidgetId = w.id
a.Name = w.name
a.Text = w.text
a.B = w.b
a.ActionType = toolkit.User
me.callback <- a
log(logNow, "END: sent a button click callback()")
}
var toggle bool = true
func (w *cuiWidget) toggleTree() {
func (n *node) toggleTree() {
if (toggle) {
w.drawTree(toggle)
n.drawTree(toggle)
toggle = false
} else {
w.hideWidgets()
n.hideWidgets()
toggle = true
}
}
// display the widgets in the binary tree
func (w *cuiWidget) drawTree(draw bool) {
func (n *node) drawTree(draw bool) {
w := n.tk
if (w == nil) {
return
}
w.showWidgetPlacement(logNow, "drawTree()")
n.showWidgetPlacement(logNow, "drawTree()")
if (draw) {
// w.textResize()
w.showView()
n.showView()
} else {
w.deleteView()
n.deleteView()
}
for _, child := range w.children {
for _, child := range n.children {
child.drawTree(draw)
}
}
func click(g *gocui.Gui, v *gocui.View) error {
// var l string
var err error
// var err error
log(logNow, "click() START", v.Name())
log(logVerbose, "click() START", v.Name())
// n := me.rootNode.findWidgetName(v.Name())
n := findUnderMouse()
if (n != nil) {
log(logNow, "click() Found widget =", n.WidgetId, n.Name, ",", n.Text)
n.doWidgetClick()
} else {
log(logNow, "click() could not find node name =", v.Name())
}
/*
i, err := strconv.Atoi(v.Name())
if (err != nil) {
log(logNow, "click() Can't find widget. error =", err)
log(logError, "click() Can't find widget. error =", err)
} else {
log(logNow, "click() ok v.Name() =", v.Name())
w := findWidget(i, me.rootNode)
if (w == nil) {
log(logVerbose, "click() ok v.Name() =", v.Name())
n := me.rootNode.findWidgetId(i)
if (n == nil) {
log(logError, "click() CANT FIND VIEW in binary tree. v.Name =", v.Name())
return nil
}
log(logNow, "click() Found widget =", w.id, w.name, ",", w.text)
w.doWidgetClick()
log(logNow, "click() Found widget =", n.WidgetId, n.Name, ",", n.Text)
n.doWidgetClick()
return nil
}
*/
if _, err := g.SetCurrentView(v.Name()); err != nil {
return err
}
/*
_, cy := v.Cursor()
if l, err = v.Line(cy); err != nil {
l = ""
}
maxX, maxY := g.Size()
if v, err := g.SetView("msg", maxX/2-10, maxY/2, maxX/2+10, maxY/2+2, 0); err == nil || errors.Is(err, gocui.ErrUnknownView) {
v.Clear()
v.SelBgColor = gocui.ColorCyan
v.SelFgColor = gocui.ColorBlack
fmt.Fprintln(v, l)
}
*/
// this seems to delete the button(?)
// g.SetViewOnBottom(v.Name())
log(logNow, "click() END")
log(logVerbose, "click() END")
return nil
}
// display the widgets in the binary tree
func ctrlDown(g *gocui.Gui, v *gocui.View) error {
var found *cuiWidget
var widgets []*cuiWidget
var f func (widget *cuiWidget)
w, h := g.MousePosition()
func findUnderMouse() *node {
var found *node
var widgets []*node
var f func (n *node)
w, h := me.baseGui.MousePosition()
// find buttons that are below where the mouse button click
f = func(widget *cuiWidget) {
// if ((widget.logicalSize.w0 < w) && (w < widget.logicalSize.w1)) {
if ((widget.gocuiSize.w0 <= w) && (w <= widget.gocuiSize.w1) &&
(widget.gocuiSize.h0 <= h) && (h <= widget.gocuiSize.h1)) {
widgets = append(widgets, widget)
found = widget
f = func(n *node) {
widget := n.tk
// ignore widgets that are not visible
if n.Visible() {
if ((widget.gocuiSize.w0 <= w) && (w <= widget.gocuiSize.w1) &&
(widget.gocuiSize.h0 <= h) && (h <= widget.gocuiSize.h1)) {
widgets = append(widgets, n)
found = n
}
}
for _, child := range widget.children {
for _, child := range n.children {
f(child)
}
}
f(me.rootNode)
var t string
for _, widget := range widgets {
// log(logNow, "ctrlDown() FOUND widget", widget.id, widget.name)
t += widget.cuiName + " " + widget.name + "\n"
widget.showWidgetPlacement(logNow, "ctrlDown() FOUND")
// widgets has everything that matches
// TODO: pop up menu with a list of them
for _, n := range widgets {
//log(logNow, "ctrlDown() FOUND widget", widget.id, widget.name)
n.showWidgetPlacement(logNow, "ctrlDown() FOUND")
}
t = strings.TrimSpace(t)
return found
}
// find the widget under the mouse click
func ctrlDown(g *gocui.Gui, v *gocui.View) error {
var found *node
// var widgets []*node
// var f func (n *node)
found = findUnderMouse()
/*
w, h := g.MousePosition()
// find buttons that are below where the mouse button click
f = func(n *node) {
widget := n.tk
// ignore widgets that are not visible
if n.Visible() {
if ((widget.gocuiSize.w0 <= w) && (w <= widget.gocuiSize.w1) &&
(widget.gocuiSize.h0 <= h) && (h <= widget.gocuiSize.h1)) {
widgets = append(widgets, n)
found = n
}
}
for _, child := range n.children {
f(child)
}
}
f(me.rootNode)
*/
if (me.ctrlDown == nil) {
setupCtrlDownWidget()
me.ctrlDown.text = "ctrlDown" // t
me.ctrlDown.cuiName = "ctrlDown"
me.ctrlDown.parent = me.rootNode
me.ctrlDown.Text = found.Name
me.ctrlDown.tk.cuiName = "ctrlDown"
// me.ctrlDown.parent = me.rootNode
}
cd := me.ctrlDown.tk
if (found == nil) {
found = me.rootNode
}
// ? TODO: found.setRealSize()
me.ctrlDown.gocuiSize.w0 = found.startW
me.ctrlDown.gocuiSize.h0 = found.startH
me.ctrlDown.gocuiSize.w1 = me.ctrlDown.gocuiSize.w0 + found.realWidth
me.ctrlDown.gocuiSize.h1 = me.ctrlDown.gocuiSize.h0 + found.realHeight
if (me.ctrlDown.v == nil) {
me.ctrlDown.text = found.text
me.ctrlDown.showWidgetPlacement(logNow, "ctrlDown:")
me.ctrlDown.showView()
} else {
me.ctrlDown.Text = found.Name
newR := found.realGocuiSize()
cd.gocuiSize.w0 = newR.w0
cd.gocuiSize.h0 = newR.h0
cd.gocuiSize.w1 = newR.w1
cd.gocuiSize.h1 = newR.h1
if me.ctrlDown.Visible() {
me.ctrlDown.deleteView()
} else {
me.ctrlDown.updateView()
}
log(logNow, "ctrlDown()", w, h)
me.ctrlDown.showWidgetPlacement(logNow, "ctrlDown:")
return nil
}

View File

@ -8,8 +8,9 @@ import (
// ColorBlack ColorRed ColorGreen ColorYellow ColorBlue ColorMagenta ColorCyan ColorWhite
// gocui.GetColor("#FFAA55") // Dark Purple
func (w *cuiWidget) setDefaultWidgetColor() {
log(logInfo, "setDefaultWidgetColor() on", w.widgetType, w.name)
func (n *node) setDefaultWidgetColor() {
w := n.tk
log(logInfo, "setDefaultWidgetColor() on", n.WidgetType, n.Name)
v, _ := me.baseGui.View(w.cuiName)
if (v == nil) {
log(logError, "setDefaultWidgetColor() failed on view == nil")
@ -24,7 +25,7 @@ func (w *cuiWidget) setDefaultWidgetColor() {
// v.BgColor = gocui.GetColor("#55AAFF") // super light grey
// v.BgColor = gocui.GetColor("#FFC0CB") // 'w3c pink' yellow
switch w.widgetType {
switch n.WidgetType {
case toolkit.Root:
v.FrameColor = gocui.ColorRed
v.BgColor = gocui.GetColor("#B0E0E6") // w3c 'powerder blue'
@ -84,7 +85,8 @@ func (w *cuiWidget) SetColor(c string) {
}
}
func (w *cuiWidget) setDefaultHighlight() {
func (n *node) setDefaultHighlight() {
w := n.tk
if (w.v == nil) {
log(logError, "SetColor() failed on view == nil")
return
@ -100,16 +102,17 @@ func randColor() gocui.Attribute {
return gocui.GetColor(colors[i])
}
func (w *cuiWidget) redoColor(draw bool) {
func (n *node) redoColor(draw bool) {
w := n.tk
if (w == nil) {
return
}
sleep(.05)
w.setDefaultHighlight()
// w.setDefaultWidgetColor()
n.setDefaultHighlight()
n.setDefaultWidgetColor()
for _, child := range w.children {
for _, child := range n.children {
child.redoColor(draw)
}
}

View File

@ -3,118 +3,182 @@ package main
import (
"strconv"
"git.wit.org/wit/gui/toolkit"
// "github.com/awesome-gocui/gocui"
)
func makeWidget(a *toolkit.Action) *cuiWidget {
func makeWidget(n *node) *cuiWidget {
var w *cuiWidget
w = new(cuiWidget)
// Set(w, "default")
w.name = a.Name
w.text = a.Text
w.b = a.B
w.i = a.I
w.s = a.S
w.X = a.X
w.Y = a.Y
t := len(w.text)
w.gocuiSize.w1 = w.gocuiSize.w0 + t + me.PadW
w.gocuiSize.h1 = w.gocuiSize.h0 + me.DefaultHeight + me.PadH
w.realWidth = w.gocuiSize.Width()
w.realHeight = w.gocuiSize.Height()
// set the gocui view.Frame = true by default
w.frame = true
if (w.frame) {
w.realHeight += me.FramePadH
w.gocuiSize.height += me.FramePadH
}
w.widgetType = a.WidgetType
w.id = a.WidgetId
// set the name used by gocui to the id
w.cuiName = strconv.Itoa(w.id)
w.cuiName = strconv.Itoa(n.WidgetId)
if w.widgetType == toolkit.Root {
log(logInfo, "setupWidget() FOUND ROOT w.id =", w.id, "w.parent", w.parent, "ParentId =", a.ParentId)
w.id = 0
me.rootNode = w
if n.WidgetType == toolkit.Root {
log(logInfo, "setupWidget() FOUND ROOT w.id =", n.WidgetId)
n.WidgetId = 0
me.rootNode = n
return w
}
w.parent = findWidget(a.ParentId, me.rootNode)
log(logInfo, "setupWidget() w.id =", w.id, "w.parent", w.parent, "ParentId =", a.ParentId)
if (w.parent == nil) {
log(logError, "setupWidget() ERROR: PARENT = NIL w.id =", w.id, "w.parent", w.parent, "ParentId =", a.ParentId)
// just use the rootNode (hopefully it's not nil)
w.parent = me.rootNode
// return w
}
// add this widget as a child for the parent
w.parent.Append(w)
if (a.WidgetType == toolkit.Box) {
if (a.B) {
w.horizontal = true
if (n.WidgetType == toolkit.Box) {
if (n.B) {
n.horizontal = true
} else {
w.horizontal = false
n.horizontal = false
}
}
if (a.WidgetType == toolkit.Grid) {
if (n.WidgetType == toolkit.Grid) {
w.widths = make(map[int]int) // how tall each row in the grid is
w.heights = make(map[int]int) // how wide each column in the grid is
}
return w
}
func setupCtrlDownWidget() {
var w *cuiWidget
w = new(cuiWidget)
a := new(toolkit.Action)
a.Name = "ctrlDown"
a.WidgetType = toolkit.Dialog
a.WidgetId = -1
a.ParentId = 0
n := addNode(a)
w.name = "ctrlDown"
w.widgetType = toolkit.Flag
w.id = -1
me.ctrlDown = w
// me.rootNode.Append(w)
me.ctrlDown = n
}
func (w *cuiWidget) deleteView() {
func (n *node) deleteView() {
w := n.tk
if (w.v != nil) {
me.baseGui.DeleteView(w.cuiName)
w.v.Visible = false
return
}
// make sure the view isn't really there
me.baseGui.DeleteView(w.cuiName)
w.v = nil
}
func (n *cuiWidget) Append(child *cuiWidget) {
n.children = append(n.children, child)
// child.parent = n
}
// find widget by number
func findWidget(i int, w *cuiWidget) (*cuiWidget) {
if (w == nil) {
log(logVerbose, "findWidget() Trying to find i =", i, "currently checking against w.id = nil")
// searches the binary tree for a WidgetId
func (n *node) findWidgetId(id int) *node {
if (n == nil) {
return nil
}
log(logVerbose, "findWidget() Trying to find i =", i, "currently checking against w.id =", w.id)
if (w.id == i) {
log(logInfo, "findWidget() FOUND w.id ==", i, w.widgetType, w.name)
return w
if n.WidgetId == id {
return n
}
for _, child := range w.children {
newW := findWidget(i, child)
log(logVerbose, "findWidget() Trying to find i =", i, "currently checking against child.id =", child.id)
if (newW != nil) {
return newW
for _, child := range n.children {
newN := child.findWidgetId(id)
if (newN != nil) {
return newN
}
}
return nil
}
// searches the binary tree for a WidgetId
func (n *node) findWidgetName(name string) *node {
if (n == nil) {
return nil
}
if n.tk.cuiName == name {
return n
}
for _, child := range n.children {
newN := child.findWidgetName(name)
if (newN != nil) {
return newN
}
}
return nil
}
func addNode(a *toolkit.Action) *node {
n := new(node)
n.WidgetType = a.WidgetType
n.WidgetId = a.WidgetId
n.ParentId = a.ParentId
// copy the data from the action message
n.Name = a.Name
n.Text = a.Text
n.I = a.I
n.S = a.S
n.B = a.B
n.X = a.X
n.Y = a.Y
n.W = a.W
n.H = a.H
n.AtW = a.AtW
n.AtH = a.AtH
// store the internal toolkit information
n.tk = makeWidget(n)
if (a.WidgetType == toolkit.Root) {
log(logInfo, "addNode() Root")
return n
}
if (me.rootNode.findWidgetId(a.WidgetId) != nil) {
log(logError, "addNode() WidgetId already exists", a.WidgetId)
return me.rootNode.findWidgetId(a.WidgetId)
}
// add this new widget on the binary tree
n.parent = me.rootNode.findWidgetId(a.ParentId)
if n.parent != nil {
n.parent.children = append(n.parent.children, n)
//w := n.tk
//w.parent = n.parent.tk
//w.parent.children = append(w.parent.children, w)
}
return n
}
func (n *node) IsCurrent() bool {
w := n.tk
if (n.WidgetType == toolkit.Tab) {
return w.isCurrent
}
if (n.WidgetType == toolkit.Window) {
return w.isCurrent
}
if (n.WidgetType == toolkit.Root) {
return false
}
return n.parent.IsCurrent()
}
func (n *node) Visible() bool {
if (n == nil) {
return false
}
if (n.tk == nil) {
return false
}
if (n.tk.v == nil) {
return false
}
return n.tk.v.Visible
}
func (n *node) SetVisible(b bool) {
if (n == nil) {
return
}
if (n.tk == nil) {
return
}
if (n.tk.v == nil) {
return
}
n.tk.v.Visible = b
}

View File

@ -2,37 +2,72 @@ package main
import (
"fmt"
"git.wit.org/wit/gui/toolkit"
)
func (w *cuiWidget) dumpTree(draw bool) {
func (n *node) dumpTree(draw bool) {
w := n.tk
if (w == nil) {
return
}
w.showWidgetPlacement(logNow, "Tree:")
n.showWidgetPlacement(logNow, "Tree:")
for _, child := range w.children {
for _, child := range n.children {
child.dumpTree(draw)
}
}
func (w *cuiWidget) showWidgetPlacement(b bool, s string) {
var s1 string
var pId int
if (w == nil) {
func (n *node) showWidgetPlacement(b bool, s string) {
if (n == nil) {
log(logError, "WTF w == nil")
return
}
if (w.parent == nil) {
log(logVerbose, "showWidgetPlacement() parent == nil", w.id, w.cuiName)
w := n.tk
var s1 string
var pId int
if (n.parent == nil) {
log(logVerbose, "showWidgetPlacement() parent == nil", n.WidgetId, w.cuiName)
pId = 0
} else {
pId = w.parent.id
pId = n.parent.WidgetId
}
s1 = fmt.Sprintf("(wId,pId)=(%2d,%2d) ", w.id, pId)
s1 += fmt.Sprintf("s/n (%2d,%2d) (%2d,%2d) ", w.startW, w.startH, w.nextW, w.nextH)
s1 += fmt.Sprintf("size (%2d,%2d) ", w.realWidth, w.realHeight)
s1 += fmt.Sprintf("gocui=(%2d,%2d)(%2d,%2d,%2d,%2d)",
w.gocuiSize.Width(), w.gocuiSize.Height(),
w.gocuiSize.w0, w.gocuiSize.h0, w.gocuiSize.w1, w.gocuiSize.h1)
log(b, s1, s, w.widgetType, ",", w.name) // , "text=", w.text)
s1 = fmt.Sprintf("(wId,pId)=(%2d,%2d) ", n.WidgetId, pId)
s1 += fmt.Sprintf("size=(%2d,%2d)(%2d,%2d,%2d,%2d)",
w.size.Width(), w.size.Height(),
w.size.w0, w.size.h0, w.size.w1, w.size.h1)
if n.Visible() {
s1 += fmt.Sprintf("gocui=(%2d,%2d)(%2d,%2d,%2d,%2d)",
w.gocuiSize.Width(), w.gocuiSize.Height(),
w.gocuiSize.w0, w.gocuiSize.h0, w.gocuiSize.w1, w.gocuiSize.h1)
}
if (n.parent != nil) {
if (n.parent.WidgetType == toolkit.Grid) {
s1 += fmt.Sprintf("At(%2d,%2d) ", n.AtW, n.AtH)
}
}
log(b, s1, s, n.WidgetType, ",", n.Name) // , "text=", w.text)
}
func (n *node) dumpWidget(pad string) {
log(true, "node:", pad, n.WidgetId, "At(", n.AtW, n.AtH, ") ,", n.WidgetType, ",", n.Name)
}
func (n *node) listWidgets() {
if (n == nil) {
return
}
var pad string
for i := 0; i < me.depth; i++ {
pad = pad + " "
}
n.dumpWidget(pad)
for _, child := range n.children {
me.depth += 1
child.listWidgets()
me.depth -= 1
}
return
}

View File

@ -1,37 +0,0 @@
// 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"
"github.com/awesome-gocui/gocui"
)
func globalDown(g *gocui.Gui, v *gocui.View) error {
mx, my := g.MousePosition()
if vx0, vy0, vx1, vy1, err := g.ViewPosition("msg"); err == nil {
if mx >= vx0 && mx <= vx1 && my >= vy0 && my <= vy1 {
return msgDown(g, v)
}
}
globalMouseDown = true
maxX, _ := g.Size()
msg := fmt.Sprintf("Mouse really down at: %d,%d", mx, my) + "foo\n" + "bar\n"
x := mx - len(msg)/2
if x < 0 {
x = 0
} else if x+len(msg)+1 > maxX-1 {
x = maxX - 1 - len(msg) - 1
}
if v, err := g.SetView("globalDown", x, my-1, x+len(msg)+1, my+1, 0); err != nil {
if !errors.Is(err, gocui.ErrUnknownView) {
return err
}
v.WriteString(msg)
}
return nil
}

100
toolkit/gocui/gocui.go Normal file
View File

@ -0,0 +1,100 @@
// 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"
"github.com/awesome-gocui/gocui"
)
// This initializes the gocui package
// it runs SetManagerFunc which passes every input
// event (keyboard, mouse, etc) to the function "gocuiEvent()"
func gocuiMain() {
g, err := gocui.NewGui(gocui.OutputNormal, true)
if err != nil {
panic(err)
}
defer g.Close()
me.baseGui = g
g.Cursor = true
g.Mouse = true
// this sets the function that is run on every event. For example:
// When you click the mouse, move the mouse, or press a key on the keyboard
// This is equivalent to xev or similar to cat /dev/input on linux
g.SetManagerFunc(gocuiEvent)
if err := defaultKeybindings(g); err != nil {
panic(err)
}
if err := g.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) {
panic(err)
}
}
// Thanks to the gocui developers -- your package kicks ass
// This function is called on every event. It is a callback function from the gocui package
// which has an excellent implementation. While gocui handles things like text highlighting
// and the layout of the text areas -- also things like handling SIGWINCH and lots of really
// complicated console handling, it sends events here in a clean way.
// This is equivalent to the linux command xev (apt install x11-utils)
func gocuiEvent(g *gocui.Gui) error {
maxX, maxY := g.Size()
mx, my := g.MousePosition()
log(logVerbose, "handleEvent() START", maxX, maxY, mx, my, msgMouseDown)
if _, err := g.View("msg"); msgMouseDown && err == nil {
moveMsg(g)
}
if widgetView, _ := g.View("msg"); widgetView == nil {
log(logNow, "handleEvent() create output widget now", maxX, maxY, mx, my)
makeOutputWidget(g, "this is a create before a mouse click")
if (me.logStdout != nil) {
// setOutput(me.logStdout)
}
} else {
log(logInfo, "output widget already exists", maxX, maxY, mx, my)
}
mouseMove(g)
log(logVerbose, "handleEvent() END ", maxX, maxY, mx, my, msgMouseDown)
return nil
}
func dragOutputWindow() {
}
// turns off the frame on the global window
func setFrame(b bool) {
// TODO: figure out what this might be useful for
// what is this do? I made it just 2 lines for now. Is this useful for something?
v := SetView("global", 15, 5, 80, 8, 10)
if (v == nil) {
log(logError, "setFrame() global failed")
}
v.Frame = b
}
func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}
func SetView(name string, x0, y0, x1, y1 int, overlaps byte) *gocui.View {
if (me.baseGui == nil) {
log(logError, "SetView() ERROR: me.baseGui == nil")
return nil
}
v, err := me.baseGui.SetView(name, x0, y0, x1, y1, overlaps)
if err != nil {
if !errors.Is(err, gocui.ErrUnknownView) {
log(logError, "SetView() global failed on name =", name)
}
return nil
}
return v
}

View File

@ -12,25 +12,23 @@ import (
"github.com/awesome-gocui/gocui"
)
func addHelp() {
me.baseGui.SetManagerFunc(helplayout)
}
var helpText []string = []string{"KEYBINDINGS",
"",
"d: show/hide debugging",
"h: hide widgets",
"s: show all widgets",
"s/h: show/hide all widgets",
"L: list all widgets",
"q: quit()",
"p: panic()",
"o: show Stdout",
"l: log to /tmp/witgui.log",
"Ctrl-D: Enable Debugging",
"Ctrl-D: Toggle Debugging",
"Ctrl-V: Toggle Verbose Debugging",
"Ctrl-C: Exit",
"",
}
func helplayout(g *gocui.Gui) error {
func helplayout() error {
g := me.baseGui
var err error
maxX, _ := g.Size()

View File

@ -5,6 +5,7 @@
package main
import (
"os"
"github.com/awesome-gocui/gocui"
"git.wit.org/wit/gui/toolkit"
)
@ -21,7 +22,7 @@ func defaultKeybindings(g *gocui.Gui) error {
if err := g.SetKeybinding("", gocui.MouseRelease, gocui.ModNone, mouseUp); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, globalDown); err != nil {
if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, mouseDown); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModMouseCtrl, ctrlDown); err != nil {
@ -47,7 +48,7 @@ func addDebugKeys(g *gocui.Gui) {
func(g *gocui.Gui, v *gocui.View) error {
log(logNow, "gocui.SetKeyBinding() dumpTree() START")
// me.rootNode.dumpTree(true)
fakeStartWidth = me.DevelOffsetW
fakeStartWidth = me.FakeW
fakeStartHeight = me.TabH + me.FramePadH
if (showDebug) {
me.rootNode.showFake()
@ -59,6 +60,12 @@ func addDebugKeys(g *gocui.Gui) {
return nil
})
// display the help menu
g.SetKeybinding("", '?', gocui.ModNone,
func(g *gocui.Gui, v *gocui.View) error {
helplayout()
return nil
})
// hide all widgets
g.SetKeybinding("", 'h', gocui.ModNone,
func(g *gocui.Gui, v *gocui.View) error {
@ -73,6 +80,26 @@ func addDebugKeys(g *gocui.Gui) {
return nil
})
// list all widgets
g.SetKeybinding("", 'L', gocui.ModNone,
func(g *gocui.Gui, v *gocui.View) error {
me.rootNode.listWidgets()
return nil
})
// log to output window
g.SetKeybinding("", 'o', gocui.ModNone,
func(g *gocui.Gui, v *gocui.View) error {
if me.logStdout.Visible() {
me.logStdout.SetVisible(false)
setOutput(os.Stdout)
} else {
me.logStdout.SetVisible(true)
setOutput(me.logStdout.tk)
}
return nil
})
// exit
g.SetKeybinding("", 'q', gocui.ModNone,
func(g *gocui.Gui, v *gocui.View) error {
@ -86,9 +113,28 @@ func addDebugKeys(g *gocui.Gui) {
})
g.SetKeybinding("", gocui.KeyCtrlD, gocui.ModNone,
func(g *gocui.Gui, v *gocui.View) error {
var a toolkit.Action
a.ActionType = toolkit.EnableDebug
me.callback <- a
if (showDebug) {
var a toolkit.Action
a.B = true
a.ActionType = toolkit.EnableDebug
me.callback <- a
logInfo = true
logVerbose = true
} else {
logInfo = false
logVerbose = false
}
return nil
})
g.SetKeybinding("", gocui.KeyCtrlV, gocui.ModNone,
func(g *gocui.Gui, v *gocui.View) error {
if (logVerbose) {
logInfo = false
logVerbose = false
} else {
logInfo = true
logVerbose = true
}
return nil
})

View File

@ -96,7 +96,7 @@ func main() {
ferr, _ := os.OpenFile("/tmp/witgui.err", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664)
os.Stderr = ferr
MouseMain()
gocuiMain()
log(true, "MouseMain() closed")
standardExit()

View File

@ -6,68 +6,13 @@ package main
import (
"errors"
"fmt"
"github.com/awesome-gocui/gocui"
)
func MouseMain() {
g, err := gocui.NewGui(gocui.OutputNormal, true)
if err != nil {
panic(err)
}
defer g.Close()
me.baseGui = g
g.Cursor = true
g.Mouse = true
g.SetManagerFunc(layout)
if err := defaultKeybindings(g); err != nil {
panic(err)
}
if err := g.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) {
panic(err)
}
}
func layout(g *gocui.Gui) error {
maxX, maxY := g.Size()
mx, my := g.MousePosition()
if _, err := g.View("msg"); msgMouseDown && err == nil {
moveMsg(g)
}
// TODO: figure out what this might be useful for
// what is this do? I made it just 2 lines for now. Is this useful for something?
if v, err := g.SetView("global", 15, 5, maxX, 8, 10); err != nil {
if !errors.Is(err, gocui.ErrUnknownView) {
log("global failed", maxX, maxY)
return err
}
v.Frame = false
}
helplayout(g)
if widgetView, _ := g.View("msg"); widgetView == nil {
log(logInfo, "create output widget now", maxX, maxY, mx, my)
makeOutputWidget(g, "this is a create before a mouse click")
if (me.logStdout != nil) {
setOutput(me.logStdout)
}
} else {
log(logInfo, "output widget already exists", maxX, maxY, mx, my)
}
updateHighlightedView(g)
log(logInfo, "layout() END", maxX, maxY, mx, my)
return nil
}
func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}
func updateHighlightedView(g *gocui.Gui) {
// this function uses the mouse position to highlight & unhighlight things
// this is run every time the user moves the mouse over the terminal window
func mouseMove(g *gocui.Gui) {
mx, my := g.MousePosition()
for _, view := range g.Views() {
view.Highlight = false
@ -102,3 +47,28 @@ func mouseUp(g *gocui.Gui, v *gocui.View) error {
}
return nil
}
func mouseDown(g *gocui.Gui, v *gocui.View) error {
mx, my := g.MousePosition()
if vx0, vy0, vx1, vy1, err := g.ViewPosition("msg"); err == nil {
if mx >= vx0 && mx <= vx1 && my >= vy0 && my <= vy1 {
return msgDown(g, v)
}
}
globalMouseDown = true
maxX, _ := g.Size()
msg := fmt.Sprintf("Mouse really down at: %d,%d", mx, my) + "foo\n" + "bar\n"
x := mx - len(msg)/2
if x < 0 {
x = 0
} else if x+len(msg)+1 > maxX-1 {
x = maxX - 1 - len(msg) - 1
}
if v, err := g.SetView("globalDown", x, my-1, x+len(msg)+1, my+1, 0); err != nil {
if !errors.Is(err, gocui.ErrUnknownView) {
return err
}
v.WriteString(msg)
}
return nil
}

View File

@ -1,263 +1,196 @@
package main
import (
"fmt"
// "github.com/awesome-gocui/gocui"
"strings"
"git.wit.org/wit/gui/toolkit"
)
func (w *cuiWidget) placeBox() {
if (w.widgetType != toolkit.Box) {
func (n *node) placeBox(startW int, startH int) {
if (n.WidgetType != toolkit.Box) {
return
}
w.startW = w.parent.nextW
w.startH = w.parent.nextH
w.nextW = w.parent.nextW
w.nextH = w.parent.nextH
w.realWidth = 0
w.realHeight = 0
n.tk.size.w0 = startW
n.tk.size.h0 = startH
n.showWidgetPlacement(logNow, "boxS()")
var maxW int
var maxH int
for _, child := range w.children {
w.showWidgetPlacement(logNow, "boxS()")
child.placeWidgets()
if (w.horizontal) {
log(logVerbose, "BOX IS HORIZONTAL")
newW := startW
newH := startH
for _, child := range n.children {
child.placeWidgets(newW, newH)
// n.showWidgetPlacement(logNow, "boxS()")
// w,h := child.logicalSize()
newR := child.realGocuiSize()
w := newR.w1 - newR.w0
h := newR.h1 - newR.h0
if (n.horizontal) {
log(logNow, "BOX IS HORIZONTAL", n.Name, "newWH()", newW, newH, "child()", w, h, child.Name)
// expand based on the child width
w.nextW += child.realWidth
w.realWidth += child.realWidth
newW += w
} else {
log(logVerbose, "BOX IS VERTICAL")
log(logNow, "BOX IS VERTICAL ", n.Name, "newWH()", newW, newH, "child()", w, h, child.Name)
// expand based on the child height
w.nextH += child.realHeight
w.realHeight += child.realHeight
}
if (maxW < child.realWidth) {
maxW = child.realWidth
}
if (maxH < child.realHeight) {
maxH = child.realHeight
newH += h
}
}
if (w.horizontal) {
w.realHeight = maxH
} else {
w.realWidth = maxW
}
w.showWidgetPlacement(logNow, "boxE()")
// w,h := n.logicalSize()
newR := n.realGocuiSize()
w := newR.w1 - newR.w0
h := newR.h1 - newR.h0
n.tk.size.w1 = n.tk.size.w0 + w
n.tk.size.h1 = n.tk.size.h0 + h
n.showWidgetPlacement(logNow, "boxE()")
}
func (w *cuiWidget) placeWidgets() {
if (w == nil) {
func (n *node) placeWidgets(startW int, startH int) {
if (n == nil) {
return
}
if (me.rootNode == nil) {
return
}
p := w.parent
if (p == nil) {
log(logInfo, "place()", w.id, "parent == nil")
return
}
switch w.widgetType {
switch n.WidgetType {
case toolkit.Window:
for _, child := range w.children {
w.startW = me.RawW
w.startH = me.RawH
w.nextW = me.RawW
w.nextH = me.RawH
w.showWidgetPlacement(logNow, "place()")
child.placeWidgets()
if (w.realWidth < child.realWidth) {
w.realWidth = child.realWidth
}
if (w.realHeight < child.realHeight) {
w.realHeight = child.realHeight
}
w.showWidgetPlacement(logNow, "place()")
for _, child := range n.children {
child.placeWidgets(me.RawW, me.RawH)
return
}
case toolkit.Tab:
for _, child := range w.children {
w.startW = me.RawW
w.startH = me.RawH
w.nextW = me.RawW
w.nextH = me.RawH
w.showWidgetPlacement(logNow, "place()")
child.placeWidgets()
if (w.realWidth < child.realWidth) {
w.realWidth = child.realWidth
}
if (w.realHeight < child.realHeight) {
w.realHeight = child.realHeight
}
w.showWidgetPlacement(logNow, "place()")
for _, child := range n.children {
child.placeWidgets(me.RawW, me.RawH)
return
}
case toolkit.Grid:
w.showWidgetPlacement(logNow, "placeGrid() START")
w.placeGrid()
w.showWidgetPlacement(logNow, "placeGrid() END")
n.placeGrid(startW, startH)
case toolkit.Box:
w.showWidgetPlacement(logNow, "placeBox() START")
w.placeBox()
w.showWidgetPlacement(logNow, "placeBox() END")
n.placeBox(startW, startH)
case toolkit.Group:
// move the group to the parent's next location
w.startW = p.nextW
w.startH = p.nextH
w.nextW = p.nextW
w.nextH = p.nextH
w.moveTo(p.nextW, p.nextH)
n.gocuiSetWH(startW, startH)
n.showWidgetPlacement(logNow, "group()")
// initialize the real width to just the group gocui view
w.realWidth = w.gocuiSize.Width() + me.FramePadW
w.realHeight = w.gocuiSize.Height() + me.FramePadH
// indent the widgets for a group
w.nextW = p.nextW + me.GroupPadW
w.nextH = p.nextH + w.realHeight
w.showWidgetPlacement(logNow, "place()")
// mow move all the children aka: run place() on them
var maxW int
for _, child := range w.children {
child.showWidgetPlacement(logNow, "place()")
child.placeWidgets()
child.showWidgetPlacement(logNow, "place()")
newW := startW + me.GroupPadW
newH := startH + 3 // normal hight of the group label
// now move all the children aka: run place() on them
for _, child := range n.children {
child.placeWidgets(newW, newH)
// _,h := child.logicalSize()
newR := child.realGocuiSize()
// w := newR.w1 - newR.w0
h := newR.h1 - newR.h0
// increment straight down
w.nextH += child.realHeight
w.realHeight += child.realHeight
// track largest width
if (maxW < child.realWidth) {
maxW = child.realWidth
}
newH += h
}
// add real width of largest child
w.realWidth += maxW
w.showWidgetPlacement(logNow, "place()")
default:
w.startW = p.nextW
w.startH = p.nextH
w.nextW = p.nextW
w.nextH = p.nextH
newW := w.gocuiSize.Width()
newH := w.gocuiSize.Height()
w.gocuiSize.w0 = w.startW
w.gocuiSize.h0 = w.startH
w.gocuiSize.w1 = w.gocuiSize.w0 + newW
w.gocuiSize.h1 = w.gocuiSize.h0 + newH
// the realSize should not be smaller than the gocui view (?)
// this might not be a needed check? Maybe there are legit exceptions?
if (w.realWidth < newW) {
w.realWidth = newW
}
if (w.realHeight < newH) {
w.realHeight = newH
}
w.showWidgetPlacement(logNow, "place()")
n.gocuiSetWH(startW, startH)
// n.moveTo(startW, startH)
}
}
/*
func (w *cuiWidget) setWH() {
w.gocuiSize.w1 = w.gocuiSize.w0 + w.gocuiSize.width
w.gocuiSize.h1 = w.gocuiSize.h0 + w.gocuiSize.height
}
*/
func (w *cuiWidget) moveTo(leftW int, topH int) {
if (w.isFake) {
func (n *node) placeGrid(startW int, startH int) {
w := n.tk
w.size.w0 = startW
w.size.h0 = startH
n.showWidgetPlacement(logInfo, "grid0:")
if (n.WidgetType != toolkit.Grid) {
return
}
newW := w.gocuiSize.Width()
newH := w.gocuiSize.Height()
w.gocuiSize.w0 = leftW
w.gocuiSize.h0 = topH
w.gocuiSize.w1 = w.gocuiSize.w0 + newW
w.gocuiSize.h1 = w.gocuiSize.h0 + newH
w.showWidgetPlacement(logInfo, "moveTo()")
}
func (w *cuiWidget) placeGrid() {
w.showWidgetPlacement(logNow, "grid0:")
if (w.widgetType != toolkit.Grid) {
return
}
w.startW = w.parent.nextW
w.startH = w.parent.nextH
w.nextW = w.parent.nextW
w.nextH = w.parent.nextH
var wCount int = 0
var hCount int = 0
for _, child := range w.children {
// increment for the next child
w.nextW = w.startW + wCount * 20
w.nextH = w.startH + hCount * 2
// first compute the max sizes of the rows and columns
for _, child := range n.children {
// childW, childH := child.logicalSize()
newR := child.realGocuiSize()
childW := newR.w1 - newR.w0
childH := newR.h1 - newR.h0
// set the child's realWidth, and grid offset
child.parentH = hCount
child.parentW = wCount
if (w.widths[wCount] < child.realWidth) {
w.widths[wCount] = child.realWidth
if (w.widths[child.AtW] < childW) {
w.widths[child.AtW] = childW
}
if (w.heights[hCount] < child.realHeight) {
w.heights[hCount] = child.realHeight
}
log(logVerbose, "grid1: (w,h count)", wCount, hCount, "(X,Y)", w.X, w.Y, w.name)
child.showWidgetPlacement(logNow, "grid1: " + fmt.Sprintf("next()=(%2d,%2d)", w.nextW, w.nextH))
if ((wCount + 1) < w.X) {
wCount += 1
} else {
wCount = 0
hCount += 1
if (w.heights[child.AtH] < childH) {
w.heights[child.AtH] = childH
}
// child.showWidgetPlacement(logInfo, "grid: ")
log(logVerbose, "placeGrid:", child.Name, "child()", childW, childH, "At()", child.AtW, child.AtH)
}
// reset the size of the whole grid
w.realWidth = 0
w.realHeight = 0
for _, val := range w.widths {
w.realWidth += val
}
for _, val := range w.heights {
w.realHeight += val
}
// find the width and height offset of the grid for AtW,AtH
for _, child := range n.children {
child.showWidgetPlacement(logInfo, "grid1:")
for _, child := range w.children {
child.showWidgetPlacement(logVerbose, "grid2:")
var totalW, totalH int
for i, val := range w.widths {
if (i < child.parentW) {
log(logVerbose, "grid2: (w, widths[])", i, val)
totalW += w.widths[i]
for i, w := range w.widths {
if (i < child.AtW) {
totalW += w
}
}
for i, h := range w.heights {
if (i < child.parentH) {
if (i < child.AtH) {
totalH += h
}
}
// the new corner to move the child to
w.nextW = w.startW + totalW
w.nextH = w.startH + totalH
newW := startW + totalW
newH := startH + totalH
child.placeWidgets()
log(logVerbose, "placeGrid:", child.Name, "new()", newW, newH, "At()", child.AtW, child.AtH)
child.placeWidgets(newW, newH)
child.showWidgetPlacement(logInfo, "grid2:")
log(logInfo)
}
// w.updateLogicalSizes()
w.showWidgetPlacement(logNow, "grid3:")
n.showWidgetPlacement(logInfo, "grid3:")
}
/*
func (w *cuiWidget) setRealSize() {
// computes the real, actual size of all the gocli objects in a widget
func (n *node) realGocuiSize() *rectType {
var f func (n *node, r *rectType)
newR := new(rectType)
// initialize the values to opposite
newR.w0 = 80
newR.h0 = 24
if me.baseGui != nil {
maxW, maxH := me.baseGui.Size()
newR.w0 = maxW
newR.h0 = maxH
}
newR.w1 = 0
newR.h1 = 0
// expand the rectangle to the biggest thing displayed
f = func(n *node, r *rectType) {
newR := n.tk.gocuiSize
if ! n.tk.isFake {
if r.w0 > newR.w0 {
r.w0 = newR.w0
}
if r.h0 > newR.h0 {
r.h0 = newR.h0
}
if r.w1 < newR.w1 {
r.w1 = newR.w1
}
if r.h1 < newR.h1 {
r.h1 = newR.h1
}
}
for _, child := range n.children {
f(child, r)
}
}
f(n, newR)
return newR
}
func (n *node) textSize() (int, int) {
var width, height int
for _, s := range strings.Split(n.Text, "\n") {
if (width < len(s)) {
width = len(s)
}
height += 1
}
return width, height
}
*/

View File

@ -8,12 +8,17 @@ import (
func action(a *toolkit.Action) {
log(logInfo, "action() START", a.WidgetId, a.ActionType, a.WidgetType, a.Name)
w := findWidget(a.WidgetId, me.rootNode)
n := me.rootNode.findWidgetId(a.WidgetId)
var w *cuiWidget
if (n != nil) {
w = n.tk
}
switch a.ActionType {
case toolkit.Add:
if (w == nil) {
w = makeWidget(a)
w.addWidget()
n := addNode(a)
// w = n.tk
n.addWidget()
} else {
// this is done to protect the plugin being 'refreshed' with the
// widget binary tree. TODO: find a way to keep them in sync
@ -22,16 +27,25 @@ func action(a *toolkit.Action) {
}
case toolkit.Show:
if (a.B) {
w.showView()
n.showView()
} else {
w.hideWidgets()
n.hideWidgets()
}
case toolkit.Set:
w.Set(a.A)
if a.WidgetType == toolkit.Flag {
log(logNow, "TODO: set flag here", a.ActionType, a.WidgetType, a.Name)
log(logNow, "TODO: n.WidgetType =", n.WidgetType, "n.Name =", a.Name)
} else {
if (a.A == nil) {
log(logError, "TODO: Set here. a == nil", a.ActionType, "WidgetType =", a.WidgetType, "Name =", a.Name)
} else {
n.Set(a.A)
}
}
case toolkit.SetText:
w.SetText(a.S)
n.SetText(a.S)
case toolkit.AddText:
w.AddText(a.S)
n.AddText(a.S)
case toolkit.Move:
log(logNow, "attempt to move() =", a.ActionType, a.WidgetType, a.Name)
case toolkit.CloseToolkit:
@ -43,42 +57,61 @@ func action(a *toolkit.Action) {
log(logInfo, "action() END")
}
func (w *cuiWidget) AddText(text string) {
if (w == nil) {
func (n *node) AddText(text string) {
if (n == nil) {
log(logNow, "widget is nil")
return
}
w.vals = append(w.vals, text)
for i, s := range w.vals {
log(logNow, "AddText()", w.name, i, s)
n.vals = append(n.vals, text)
for i, s := range n.vals {
log(logNow, "AddText()", n.Name, i, s)
}
w.SetText(text)
n.SetText(text)
}
func (w *cuiWidget) SetText(text string) {
if (w == nil) {
func (n *node) SetText(text string) {
if (n == nil) {
log(logNow, "widget is nil")
return
}
w.text = text
w.s = text
w.textResize()
w.deleteView()
w.showView()
n.S = text
n.Text = text
n.textResize()
n.deleteView()
n.showView()
}
func (w *cuiWidget) Set(val any) {
func (n *node) Set(val any) {
// w := n.tk
log(logInfo, "Set() value =", val)
switch v := val.(type) {
case bool:
w.b = val.(bool)
w.setCheckbox(val.(bool))
n.B = val.(bool)
n.setCheckbox(val.(bool))
case string:
w.SetText(val.(string))
n.SetText(val.(string))
case int:
w.i = val.(int)
n.I = val.(int)
default:
log(logError, "Set() unknown type =", val, v)
}
}
// this passes the user event back from the plugin
func (n *node) doUserEvent() {
if (me.callback == nil) {
log(logError, "doUserEvent() no callback channel was configured")
return
}
var a toolkit.Action
a.WidgetId = n.WidgetId
a.Name = n.Name
a.Text = n.Text
a.B = n.B
a.ActionType = toolkit.User
log(logInfo, "doUserEvent() START: send a button click callback()", a.WidgetId, a.Name)
me.callback <- a
log(logInfo, "doUserEvent() END: sent a button click callback()", a.WidgetId, a.Name)
}

View File

@ -52,14 +52,12 @@ func makeOutputWidget(g *gocui.Gui, stringFromMouseClick string) *gocui.View {
a.WidgetType = toolkit.Stdout
a.WidgetId = -3
a.ParentId = 0
me.logStdout = makeWidget(a)
me.logStdout.gocuiSize.w0 = maxX - 32
me.logStdout.gocuiSize.h0 = maxY/2
me.logStdout.gocuiSize.w1 = me.logStdout.gocuiSize.w0 + outputW
me.logStdout.gocuiSize.h1 = me.logStdout.gocuiSize.h0 + outputH
me.logStdout.realWidth = me.logStdout.gocuiSize.Width()
me.logStdout.realHeight = me.logStdout.gocuiSize.Height()
n := addNode(a)
me.logStdout = n
me.logStdout.tk.gocuiSize.w0 = maxX - 32
me.logStdout.tk.gocuiSize.h0 = maxY/2
me.logStdout.tk.gocuiSize.w1 = me.logStdout.tk.gocuiSize.w0 + outputW
me.logStdout.tk.gocuiSize.h1 = me.logStdout.tk.gocuiSize.h0 + outputH
}
v, err := g.View("msg")
if (v == nil) {
@ -78,19 +76,19 @@ func makeOutputWidget(g *gocui.Gui, stringFromMouseClick string) *gocui.View {
if (err != nil) {
log("create output window failed", err)
// return nil
return nil
}
if (v == nil) {
log("msg == nil. WTF now? err =", err)
return nil
} else {
me.logStdout.v = v
me.logStdout.tk.v = v
}
me.logStdout.v.Clear()
me.logStdout.v.SelBgColor = gocui.ColorCyan
me.logStdout.v.SelFgColor = gocui.ColorBlack
v.Clear()
v.SelBgColor = gocui.ColorCyan
v.SelFgColor = gocui.ColorBlack
fmt.Fprintln(v, "figure out how to capture STDOUT to here\n" + stringFromMouseClick)
g.SetViewOnBottom("msg")
// g.SetViewOnBottom(v.Name())

View File

@ -23,11 +23,12 @@ var me config
type config struct {
baseGui *gocui.Gui // the main gocui handle
rootNode *cuiWidget // the base of the binary tree. it should have id == 0
ctrlDown *cuiWidget // shown if you click the mouse when the ctrl key is pressed
current *cuiWidget // this is the current tab or window to show
logStdout *cuiWidget // where to show STDOUT
logStdoutV *gocui.View // where to show STDOUT
rootNode *node // the base of the binary tree. it should have id == 0
ctrlDown *node // shown if you click the mouse when the ctrl key is pressed
// current *cuiWidget // this is the current tab or window to show
logStdout *node // where to show STDOUT
helpLabel *gocui.View
// this is the channel we send user events like
// mouse clicks or keyboard events back to the program
@ -36,53 +37,47 @@ type config struct {
// this is the channel we get requests to make widgets
pluginChan chan toolkit.Action
helpLabel *gocui.View
DefaultBehavior bool `default:"true"`
// Buttons, Group, Tabs, Windows, etc are by default assumed to be a single line
// as a default, we make buttons 8 chars wide
DefaultWidth int `default:"8"`
DefaultHeight int `default:"1"`
// When the widget has a frame, like a button, it adds 2 lines runes on each side
// so you need 3 char spacing in each direction to not have them overlap
// the amount of padding when there is a frame
FramePadW int `default:"4" dense:"0"`
FramePadW int `default:"1" dense:"0"`
FramePadH int `default:"1" dense:"0"`
PadW int `default:"1" dense:"0"`
PadH int `default:"1" dense:"0"`
// additional amount of space to put between window & tab widgets
WindowPadW int `default:"8" dense:"0"`
TabPadW int `default:"4" dense:"0"`
// how far down to start Window or Tab headings
WindowW int `default:"8" dense:"0"`
WindowH int `default:"-1"`
TabW int `default:"2" dense:"0"`
TabW int `default:"5" dense:"0"`
TabH int `default:"1" dense:"0"`
// additional amount of space to put between window & tab widgets
WindowPadW int `default:"8" dense:"0"`
TabPadW int `default:"4" dense:"0"`
// additional amount of space to indent on a group
GroupPadW int `default:"6" dense:"2"`
// the raw beginning of each window (or tab)
RawW int `default:"7"`
RawH int `default:"3"`
RawW int `default:"1"`
RawH int `default:"5"`
// offset for the hidden widgets
DevelOffsetW int `default:"20"`
FakeW int `default:"20"`
padded bool // add space between things like buttons
bookshelf bool // do you want things arranged in the box like a bookshelf or a stack?
canvas bool // if set to true, the windows are a raw canvas
menubar bool // for windows
stretchy bool // expand things like buttons to the maximum size
padded bool // add space between things like buttons
margin bool // add space around the frames of windows
// writeMutex protects locks the write process
writeMutex sync.Mutex
// used for listWidgets() debugging
depth int
}
// deprecate these
@ -121,23 +116,21 @@ type node struct {
AtW int
AtH int
vals []string // dropdown menu items
// horizontal=true means layout widgets like books on a bookshelf
// horizontal=false means layout widgets like books in a stack
horizontal bool `default:false`
hasTabs bool // does the window have tabs?
// the internal plugin toolkit structure
tk *cuiWidget
}
// the gocui way
// the logical size of the widget
// this is the gocui way
// corner starts at in the upper left corner
type rectType struct {
// where the widget should calculate it's existance from
// startW int
// startH int
// the is a shortcut to access
// width int // this is always w1 - w0
height int // this is always h1 - h0
// this is the gocui way
w0, h0, w1, h1 int // left top right bottom
}
@ -150,82 +143,27 @@ func (r *rectType) Height() int {
}
type cuiWidget struct {
id int // widget ID
// parentId int
widgetType toolkit.WidgetType
name string // a descriptive name of the widget
text string // the current text being displayed
// the gocui package variables
v *gocui.View // this is nil if the widget is not displayed
cuiName string // what gocui uses to reference the widget
vals []string // dropdown menu options
// the logical size of the widget
// For example, 40x12 would be the center of a normal terminal
size rectType
// the actual gocui display view of this widget
// sometimes this isn't visable like with a Box or Grid
gocuiSize rectType
isCurrent bool // is this the currently displayed Window or Tab?
hasTabs bool // does the window have tabs?
isFake bool // widget types like 'box' are 'false'
// where the widget's real corner is
// should we always compute this?
startW int
startH int
// where the next child should be placed
nextW int
nextH int
// the widget size to reserve or things will overlap
realWidth int
realHeight int
gocuiSize rectType // the display size of this widget
// logicalSize rectType // the logical size. Includes all the child widgets
// used to track the size of grids
widths map[int]int // how tall each row in the grid is
heights map[int]int // how wide each column in the grid is
// deprecate // where in the parent grid this widget should go
parentW int
parentH int
// things from toolkit/action
b bool
i int
s string
X int
Y int
width int
height int
// horizontal=true means layout widgets like books on a bookshelf
// horizontal=false means layout widgets like books in a stack
horizontal bool `default:false`
tainted bool
v *gocui.View
frame bool
parent *cuiWidget
children []*cuiWidget
}
func (w *cuiWidget) IsCurrent() bool {
if (w.widgetType == toolkit.Tab) {
return w.isCurrent
}
if (w.widgetType == toolkit.Window) {
return w.isCurrent
}
if (w.widgetType == toolkit.Root) {
return false
}
return w.parent.IsCurrent()
}
func (w *cuiWidget) StartW() {
}
func (w *cuiWidget) StartH() {
}
// from the gocui devs:
@ -238,7 +176,7 @@ func (w *cuiWidget) Write(p []byte) (n int, err error) {
w.tainted = true
me.writeMutex.Lock()
defer me.writeMutex.Unlock()
if (me.logStdout.v == nil) {
if (me.logStdout.tk.v == nil) {
// optionally write the output to /tmp
s := fmt.Sprint(string(p))
s = strings.TrimSuffix(s, "\n")
@ -246,11 +184,11 @@ func (w *cuiWidget) Write(p []byte) (n int, err error) {
v, _ := me.baseGui.View("msg")
if (v != nil) {
// fmt.Fprintln(outf, "found msg")
me.logStdout.v = v
me.logStdout.tk.v = v
}
} else {
// display the output in the gocui window
me.logStdout.v.Clear()
me.logStdout.tk.v.Clear()
s := fmt.Sprint(string(p))
s = strings.TrimSuffix(s, "\n")
@ -260,7 +198,7 @@ func (w *cuiWidget) Write(p []byte) (n int, err error) {
l := len(outputS) - outputH
outputS = outputS[l:]
}
fmt.Fprintln(me.logStdout.v, strings.Join(outputS, "\n"))
fmt.Fprintln(me.logStdout.tk.v, strings.Join(outputS, "\n"))
}
return len(p), nil

View File

@ -3,136 +3,111 @@ package main
// implements widgets 'Window' and 'Tab'
import (
"strings"
"git.wit.org/wit/gui/toolkit"
// "github.com/awesome-gocui/gocui"
)
func (w *cuiWidget) hideWidgets() {
w.isCurrent = false
switch w.widgetType {
case toolkit.Root:
case toolkit.Flag:
case toolkit.Window:
// case toolkit.Tab:
case toolkit.Box:
case toolkit.Grid:
default:
w.deleteView()
}
for _, child := range w.children {
child.hideWidgets()
func (w *cuiWidget) Width() int {
if w.frame {
return w.gocuiSize.w1 - w.gocuiSize.w0
}
return w.gocuiSize.w1 - w.gocuiSize.w0 - 1
}
func (w *cuiWidget) hideFake() {
if (w.isFake) {
w.deleteView()
}
for _, child := range w.children {
child.hideFake()
func (w *cuiWidget) Height() int {
if w.frame {
return w.gocuiSize.h1 - w.gocuiSize.h0
}
return w.gocuiSize.h1 - w.gocuiSize.h0 - 1
}
func (w *cuiWidget) showFake() {
if (w.isFake) {
w.setFake()
w.showWidgetPlacement(logNow, "showFake:")
w.showView()
}
for _, child := range w.children {
child.showFake()
}
}
func (n *node) gocuiSetWH(sizeW, sizeH int) {
w := len(n.Text)
lines := strings.Split(n.Text, "\n")
h := len(lines)
func (w *cuiWidget) showWidgets() {
if (w.isFake) {
// don't display by default
tk := n.tk
if tk.isFake {
tk.gocuiSize.w0 = sizeW
tk.gocuiSize.h0 = sizeH
tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + me.FramePadW
tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + me.FramePadH
return
}
if tk.frame {
tk.size.w0 = sizeW
tk.size.h0 = sizeH
tk.gocuiSize.w0 = tk.size.w0
tk.gocuiSize.h0 = tk.size.h0
tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + me.FramePadW
tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + me.FramePadH
} else {
if w.IsCurrent() {
w.showWidgetPlacement(logInfo, "current:")
w.showView()
} else {
w.showWidgetPlacement(logInfo, "not:")
// w.drawView()
tk.size.w0 = sizeW - 1
tk.size.h0 = sizeH - 1
tk.gocuiSize.w0 = tk.size.w0
tk.gocuiSize.h0 = tk.size.h0
tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + 1
tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + 1
}
}
func redoWindows(nextW int, nextH int) {
for _, n := range me.rootNode.children {
if n.WidgetType != toolkit.Window {
continue
}
}
for _, child := range w.children {
child.showWidgets()
}
}
func (w *cuiWidget) setWindowWH() {
w.gocuiSize.w0 = me.rootNode.nextW
w.gocuiSize.h0 = me.WindowH
t := len(w.text)
w.gocuiSize.w1 = w.gocuiSize.w0 + t + me.PadW
w.gocuiSize.h1 = w.gocuiSize.h0 + me.DefaultHeight + me.PadH
w.realWidth = w.gocuiSize.Width()
w.realHeight = w.gocuiSize.Height()
// move the rootNode width over for the next window
me.rootNode.nextW += w.realWidth + me.WindowPadW
w.nextW = 4
w.nextH = 2
w.showWidgetPlacement(logNow, "setWindowWH:")
}
func (w *cuiWidget) setTabWH() {
// set the start and size of the tab gocui button
w.gocuiSize.w0 = w.parent.nextW
w.gocuiSize.h0 = me.TabH
t := len(w.text)
w.gocuiSize.w1 = w.gocuiSize.w0 + t + me.PadW
w.gocuiSize.h1 = w.gocuiSize.h0 + me.DefaultHeight + me.PadH
w.realWidth = w.gocuiSize.Width()
w.realHeight = w.gocuiSize.Height()
w.realWidth += me.FramePadW
w.realHeight += me.FramePadH
w.parent.nextW += w.realWidth + me.TabPadW
w.showWidgetPlacement(logNow, "setTabWH:")
}
func (w *cuiWidget) redoTabs(draw bool) {
if (w.widgetType == toolkit.Window) {
var tabs bool = false
// figure out if the window is just a bunch of tabs
for _, child := range w.children {
if (child.widgetType == toolkit.Tab) {
w := n.tk
var tabs bool
for _, child := range n.children {
if (child.WidgetType == toolkit.Tab) {
tabs = true
}
}
if (tabs) {
// window is tabs. Don't show it as a standard button
w.frame = false
w.hasTabs = true
n.hasTabs = true
} else {
w.frame = true
w.hasTabs = false
w.frame = false
n.hasTabs = false
}
w.setWindowWH()
w.deleteView()
w.showView()
}
if (w.widgetType == toolkit.Tab) {
w.setTabWH()
w.deleteView()
// show all the tabs for the current window
if w.parent.isCurrent {
w.showView()
}
}
for _, child := range w.children {
child.redoTabs(draw)
w.size.w0 = nextW
w.size.h0 = nextH
n.gocuiSetWH(nextW, nextH)
n.deleteView()
n.showView()
sizeW := w.Width() + me.WindowPadW
sizeH := w.Height()
nextW += sizeW
log(logNow, "redoWindows() start nextW,H =", nextW, nextH, "gocuiSize.W,H =", sizeW, sizeH, n.Name)
if n.hasTabs {
n.redoTabs(me.TabW, me.TabH)
}
}
}
func (p *node) redoTabs(nextW int, nextH int) {
for _, n := range p.children {
if n.WidgetType != toolkit.Tab {
continue
}
w := n.tk
w.frame = true
w.size.w0 = nextW
w.size.h0 = nextH
n.gocuiSetWH(nextW, nextH)
n.deleteView()
// setCurrentTab(n)
n.showView()
sizeW := w.Width() + me.TabPadW
sizeH := w.Height()
log(logNow, "redoTabs() start nextW,H =", nextW, nextH, "gocuiSize.W,H =", sizeW, sizeH, n.Name)
nextW += sizeW
}
}

View File

@ -20,10 +20,11 @@ func splitLines(s string) []string {
return lines
}
func (w *cuiWidget) textResize() {
func (n *node) textResize() {
w := n.tk
var width, height int
for i, s := range splitLines(w.text) {
for i, s := range splitLines(n.Text) {
log(logNow, "textResize() len =", len(s), i, s)
if (width < len(s)) {
width = len(s)
@ -32,38 +33,52 @@ func (w *cuiWidget) textResize() {
}
w.gocuiSize.w1 = w.gocuiSize.w0 + width + me.FramePadW
w.gocuiSize.h1 = w.gocuiSize.h0 + height + me.FramePadH
w.showWidgetPlacement(logNow, "textResize()")
n.showWidgetPlacement(logNow, "textResize()")
}
// display's the text of the widget in gocui
func (w *cuiWidget) showView() {
func (n *node) showView() {
var err error
w := n.tk
if (w.cuiName == "") {
log(logError, "drawView() w.cuiName was not set for widget", w)
w.cuiName = strconv.Itoa(w.id)
log(logError, "showView() w.cuiName was not set for widget", w)
w.cuiName = strconv.Itoa(n.WidgetId)
}
if (w.v != nil) {
log(logInfo, "drawView() w.v already defined for widget", w)
v, _ := me.baseGui.View(w.cuiName)
if (v == nil) {
log(logError, "drawView() ERROR view does not really exist", w)
w.v = nil
} else {
return
}
if (w.v == nil) {
n.updateView()
}
x0, y0, x1, y1, err := me.baseGui.ViewPosition(w.cuiName)
log(logInfo, "showView() w.v already defined for widget", n.Name, err)
if (x0 != w.gocuiSize.w0) || (y0 != w.gocuiSize.h0) {
log(logError, "showView() w.v.w0 != x0", n.Name, w.gocuiSize.w0, x0)
log(logError, "showView() w.v.h0 != y0", n.Name, w.gocuiSize.h0, y0)
n.updateView()
return
}
if (x1 != w.gocuiSize.w1) || (y1 != w.gocuiSize.h1) {
log(logError, "showView() w.v.w1 != x1", n.Name, w.gocuiSize.w1, x1)
log(logError, "showView() w.v.h1 != y1", n.Name, w.gocuiSize.h1, y1)
n.updateView()
return
}
if (w.v.Visible == false) {
log(logInfo, "showView() w.v.Visible set to true ", n.Name)
w.v.Visible = true
}
}
func (n *node) updateView() {
var err error
w := n.tk
if (me.baseGui == nil) {
log(logError, "drawView() ERROR: me.baseGui == nil", w)
return
}
v, _ := me.baseGui.View(w.cuiName)
if (v != nil) {
log(logError, "drawView() already defined for name", w.cuiName)
w.v = v
log(logError, "showView() ERROR: me.baseGui == nil", w)
return
}
me.baseGui.DeleteView(w.cuiName)
w.v = nil
a := w.gocuiSize.w0
b := w.gocuiSize.h0
@ -72,12 +87,12 @@ func (w *cuiWidget) showView() {
w.v, err = me.baseGui.SetView(w.cuiName, a, b, c, d, 0)
if err == nil {
w.showWidgetPlacement(logError, "drawView()")
n.showWidgetPlacement(logError, "drawView()")
log(logError, "drawView() internal plugin error err = nil")
return
}
if !errors.Is(err, gocui.ErrUnknownView) {
w.showWidgetPlacement(logError, "drawView()")
n.showWidgetPlacement(logError, "drawView()")
log(logError, "drawView() internal plugin error error.IS()", err)
return
}
@ -85,13 +100,68 @@ func (w *cuiWidget) showView() {
me.baseGui.SetKeybinding(w.v.Name(), gocui.MouseLeft, gocui.ModNone, click)
w.v.Wrap = true
if (w.widgetType == toolkit.Window) {
w.v.Frame = w.frame
w.v.Clear()
fmt.Fprint(w.v, w.text)
} else {
fmt.Fprintln(w.v, w.text)
}
w.v.Frame = w.frame
w.v.Clear()
fmt.Fprint(w.v, n.Text)
n.showWidgetPlacement(logNow, "Window: " + n.Text)
w.setDefaultWidgetColor()
n.setDefaultHighlight()
n.setDefaultWidgetColor()
}
func (n *node) hideWidgets() {
w := n.tk
w.isCurrent = false
switch n.WidgetType {
case toolkit.Root:
case toolkit.Flag:
case toolkit.Window:
case toolkit.Box:
case toolkit.Grid:
default:
n.deleteView()
}
for _, child := range n.children {
child.hideWidgets()
}
}
func (n *node) hideFake() {
w := n.tk
if (w.isFake) {
n.deleteView()
}
for _, child := range n.children {
child.hideFake()
}
}
func (n *node) showFake() {
w := n.tk
if (w.isFake) {
n.setFake()
n.showWidgetPlacement(logNow, "showFake:")
n.showView()
}
for _, child := range n.children {
child.showFake()
}
}
func (n *node) showWidgets() {
w := n.tk
if (w.isFake) {
// don't display by default
} else {
if n.IsCurrent() {
n.showWidgetPlacement(logInfo, "current:")
n.showView()
} else {
n.showWidgetPlacement(logInfo, "not:")
// w.drawView()
}
}
for _, child := range n.children {
child.showWidgets()
}
}