From 4fbbd2cee13546dbe570509e2c2e0755225a1489 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Sun, 28 Jan 2024 02:20:31 -0600 Subject: [PATCH] large refactor to use the tree package Things build and now need to be fixed treeRoot has no children lists all widgets works shows help module loads Signed-off-by: Jeff Carr --- .gitignore | 1 + Makefile | 10 +- add.go | 30 +++--- args.go | 4 +- checkbox.go | 20 ++-- click.go | 274 ++++++++++++++++++++++++++++--------------------- color.go | 32 +++--- common.go | 218 --------------------------------------- debug.go | 50 +++++---- gocui.go | 1 + keybindings.go | 44 ++++---- main.go | 41 ++------ mouse.go | 43 +++++--- place.go | 100 ++++++++++-------- plugin.go | 83 ++++++++------- showStdout.go | 21 ++-- structs.go | 59 ++++++++--- tab.go | 57 +++++----- tree.go | 24 +++++ view.go | 104 +++++++++---------- widget.go | 101 +++++++++++------- 21 files changed, 624 insertions(+), 693 deletions(-) create mode 100644 .gitignore delete mode 100644 common.go create mode 100644 tree.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1377554 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/Makefile b/Makefile index fffcfa7..adbb0b0 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,6 @@ all: plugin plugin: GO111MODULE=off go build -v -buildmode=plugin -o ../gocui.so -goget: - go get -v -t -u - objdump: objdump -t ../gocui.so |less @@ -14,11 +11,8 @@ log: reset tail -f /tmp/witgui.* /tmp/guilogfile -cleanbuild: - go build -v -x -buildmode=plugin -o ../nocui.so - -check-git-clean: - @git diff-index --quiet HEAD -- || (echo "Git repository is dirty, please commit your changes first"; exit 1) +goimports: + goimports -w *.go redomod: rm -f go.* diff --git a/add.go b/add.go index 76a1ea7..c732ab4 100644 --- a/add.go +++ b/add.go @@ -2,6 +2,7 @@ package main import ( log "go.wit.com/log" + "go.wit.com/toolkits/tree" "go.wit.com/widget" ) @@ -9,11 +10,12 @@ var fakeStartWidth int = me.FakeW var fakeStartHeight int = me.TabH + me.FramePadH // setup fake labels for non-visible things off screen -func (n *node) setFake() { - w := n.tk +func setFake(n *tree.Node) { + var w *guiWidget + w = n.TK.(*guiWidget) w.isFake = true - n.gocuiSetWH(fakeStartWidth, fakeStartHeight) + w.gocuiSetWH(fakeStartWidth, fakeStartHeight) fakeStartHeight += w.gocuiSize.Height() // TODO: use the actual max hight of the terminal window @@ -22,23 +24,25 @@ func (n *node) setFake() { fakeStartWidth += me.FakeW } if true { - n.showView() + w.showView() } } // set the widget start width & height -func (n *node) addWidget() { - nw := n.tk - log.Log(INFO, "setStartWH() w.id =", n.WidgetId, "n.name", n.progname) +// func (n *node) addWidget(n *tree.Node) { +func addWidget(n *tree.Node) { + var nw *guiWidget + nw = n.TK.(*guiWidget) + log.Log(INFO, "setStartWH() w.id =", n.WidgetId, "n.name", n.String()) switch n.WidgetType { case widget.Root: - log.Log(INFO, "setStartWH() rootNode w.id =", n.WidgetId, "w.name", n.progname) + log.Log(INFO, "setStartWH() rootNode w.id =", n.WidgetId, "w.name", n.String()) nw.color = &colorRoot - n.setFake() + setFake(n) return case widget.Flag: nw.color = &colorFlag - n.setFake() + setFake(n) return case widget.Window: nw.frame = false @@ -54,12 +58,12 @@ func (n *node) addWidget() { case widget.Box: nw.color = &colorBox nw.isFake = true - n.setFake() + setFake(n) return case widget.Grid: nw.color = &colorGrid nw.isFake = true - n.setFake() + setFake(n) return case widget.Group: nw.color = &colorGroup @@ -76,5 +80,5 @@ func (n *node) addWidget() { } */ } - n.showWidgetPlacement(true, "addWidget()") + showWidgetPlacement(n, true, "addWidget()") } diff --git a/args.go b/args.go index f4f7aaa..a170ef5 100644 --- a/args.go +++ b/args.go @@ -23,10 +23,10 @@ func init() { short := "gocui" NOW = log.NewFlag("NOW", true, full, short, "temp debugging stuff") - INFO = log.NewFlag("INFO", false, full, short, "normal debugging stuff") + INFO = log.NewFlag("INFO", true, full, short, "normal debugging stuff") WARN = log.NewFlag("WARN", true, full, short, "bad things") SPEW = log.NewFlag("SPEW", false, full, short, "spew stuff") - ERROR = log.NewFlag("ERROR", false, full, short, "toolkit errors") + ERROR = log.NewFlag("ERROR", true, full, short, "toolkit errors") } diff --git a/checkbox.go b/checkbox.go index 1ca0a9a..7fecb27 100644 --- a/checkbox.go +++ b/checkbox.go @@ -1,23 +1,21 @@ package main import ( - // "github.com/awesome-gocui/gocui" "go.wit.com/widget" ) -func (n *node) setCheckbox(b any) { - w := n.tk - if n.WidgetType != widget.Checkbox { +func (w *guiWidget) setCheckbox(b any) { + if w.node.WidgetType != widget.Checkbox { return } if widget.GetBool(b) { - n.value = b - n.tk.label = "X " + n.label + w.checked = widget.GetBool(b) + w.label = "X " + w.label } else { - n.value = b - n.tk.label = " " + n.label + w.checked = widget.GetBool(b) + w.label = " " + w.label } - t := len(n.tk.label) + 1 + t := len(w.label) + 1 w.gocuiSize.w1 = w.gocuiSize.w0 + t // w.realWidth = w.gocuiSize.Width() + me.PadW @@ -28,6 +26,6 @@ func (n *node) setCheckbox(b any) { // w.realHeight += me.FramePadH // } - n.deleteView() - n.showView() + w.deleteView() + w.showView() } diff --git a/click.go b/click.go index 7de1e81..53c4abf 100644 --- a/click.go +++ b/click.go @@ -2,22 +2,23 @@ package main import ( "fmt" + "github.com/awesome-gocui/gocui" "go.wit.com/log" + "go.wit.com/toolkits/tree" "go.wit.com/widget" ) // set isCurrent = false everywhere -func unsetCurrent(n *node) { - w := n.tk +func unsetCurrent(w *guiWidget) { w.isCurrent = false - if n.WidgetType == widget.Tab { + if w.node.WidgetType == widget.Tab { // n.tk.color = &colorTab // n.setColor() } - for _, child := range n.children { + for _, child := range w.children { unsetCurrent(child) } } @@ -25,14 +26,17 @@ func unsetCurrent(n *node) { // 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 (n *node) updateCurrent() { - log.Log(NOW, "updateCurrent()", n.progname) +func updateCurrent(n *tree.Node) { + var w *guiWidget + w = n.TK.(*guiWidget) + + log.Log(NOW, "updateCurrent()", w.String()) if n.WidgetType == widget.Tab { - if n.IsCurrent() { + if w.IsCurrent() { // n.tk.color = &colorActiveT - n.setColor(&colorActiveT) - n.hideView() - n.showView() + setColor(n, &colorActiveT) + w.hideView() + w.showView() setCurrentTab(n) } else { // n.tk.color = &colorTab @@ -41,7 +45,7 @@ func (n *node) updateCurrent() { return } if n.WidgetType == widget.Window { - if n.IsCurrent() { + if w.IsCurrent() { // setCurrentWindow(n) } return @@ -49,24 +53,27 @@ func (n *node) updateCurrent() { if n.WidgetType == widget.Root { return } - n.parent.updateCurrent() + updateCurrent(n.Parent) } // shows the widgets in a window -func setCurrentWindow(n *node) { - if n.IsCurrent() { +func setCurrentWindow(n *tree.Node) { + var w *guiWidget + w = n.TK.(*guiWidget) + if w.IsCurrent() { return } - w := n.tk if n.WidgetType != widget.Window { return } - unsetCurrent(me.rootNode) + var rootTK *guiWidget + rootTK = me.treeRoot.TK.(*guiWidget) + unsetCurrent(rootTK) - if n.hasTabs { + if w.hasTabs { // set isCurrent = true on the first tab - for _, child := range n.children { - child.tk.isCurrent = true + for _, child := range w.children { + child.isCurrent = true break } } else { @@ -75,46 +82,53 @@ func setCurrentWindow(n *node) { } // shows the widgets in a tab -func setCurrentTab(n *node) { - w := n.tk +func setCurrentTab(n *tree.Node) { + var w, p, rootTK *guiWidget + w = n.TK.(*guiWidget) if n.WidgetType != widget.Tab { return } - unsetCurrent(me.rootNode) + rootTK = me.treeRoot.TK.(*guiWidget) + unsetCurrent(rootTK) w.isCurrent = true - p := n.parent.tk + p = n.Parent.TK.(*guiWidget) p.isCurrent = true - log.Log(NOW, "setCurrent()", n.progname) + log.Log(NOW, "setCurrent()", n.String()) } -func (n *node) doWidgetClick() { +func doWidgetClick(n *tree.Node) { switch n.WidgetType { case widget.Root: // THIS IS THE BEGINING OF THE LAYOUT - log.Log(NOW, "doWidgetClick()", n.progname) + log.Log(NOW, "doWidgetClick()", n.String()) redoWindows(0, 0) case widget.Flag: - log.Log(NOW, "doWidgetClick() FLAG widget name =", n.progname) + log.Log(NOW, "doWidgetClick() FLAG widget name =", n.String()) log.Log(NOW, "doWidgetClick() if this is the dropdown menu, handle it here?") case widget.Window: if me.currentWindow == n { return } if me.currentWindow != nil { - unsetCurrent(me.currentWindow) - me.currentWindow.setColor(&colorWindow) - me.currentWindow.hideWidgets() + var w *guiWidget + w = me.currentWindow.TK.(*guiWidget) + unsetCurrent(w) + setColor(me.currentWindow, &colorWindow) + w.hideWidgets() } - n.hideWidgets() me.currentWindow = n // setCurrentWindow(n) // probably delete this - n.setColor(&colorActiveW) - n.redoTabs(me.TabW, me.TabH) - for _, child := range n.children { + setColor(n, &colorActiveW) + + var w *guiWidget + w = n.TK.(*guiWidget) + w.hideWidgets() + w.redoTabs(me.TabW, me.TabH) + for _, child := range w.children { if child.currentTab == true { - log.Log(NOW, "FOUND CURRENT TAB", child.progname) - setCurrentTab(child) - child.placeWidgets(me.RawW, me.RawH) + log.Log(NOW, "FOUND CURRENT TAB", child.String()) + setCurrentTab(child.node) + placeWidgets(child.node, me.RawW, me.RawH) child.showWidgets() return } @@ -124,62 +138,76 @@ func (n *node) doWidgetClick() { } */ case widget.Tab: - if n.IsCurrent() { + var w *guiWidget + w = n.TK.(*guiWidget) + if w.IsCurrent() { return // do nothing if you reclick on the already selected tab } // find the window and disable the active tab - p := n.parent + p := n.Parent if p != nil { - p.hideWidgets() - p.redoTabs(me.TabW, me.TabH) - unsetCurrent(p) - for _, child := range p.children { - if child.WidgetType == widget.Tab { - child.setColor(&colorTab) - n.currentTab = false + var w *guiWidget + w = p.TK.(*guiWidget) + w.hideWidgets() + w.redoTabs(me.TabW, me.TabH) + unsetCurrent(w) + for _, child := range w.children { + if child.node.WidgetType == widget.Tab { + setColor(child.node, &colorTab) + child.currentTab = false } } } - n.currentTab = true - n.setColor(&colorActiveT) + w.currentTab = true + setColor(n, &colorActiveT) setCurrentTab(n) - n.placeWidgets(me.RawW, me.RawH) - n.showWidgets() + placeWidgets(n, me.RawW, me.RawH) + w.showWidgets() case widget.Group: // n.placeWidgets(p.tk.startH, newH) - n.toggleTree() + toggleTree(n) case widget.Checkbox: - if widget.GetBool(n.value) { - n.setCheckbox(false) + var w *guiWidget + w = n.TK.(*guiWidget) + if widget.GetBool(w.value) { + w.setCheckbox(false) } else { - n.setCheckbox(true) + w.setCheckbox(true) } - n.doUserEvent() + // n.doUserEvent() + me.myTree.SendUserEvent(me.treeRoot) case widget.Grid: - newR := n.realGocuiSize() + newR := realGocuiSize(n) // w,h := n.logicalSize() // w := newR.w1 - newR.w0 // h := newR.h1 - newR.h0 - n.placeGrid(newR.w0, newR.h0) - n.showWidgets() + placeGrid(n, newR.w0, newR.h0) + var w *guiWidget + w = n.TK.(*guiWidget) + w.showWidgets() case widget.Box: + var w *guiWidget + w = n.TK.(*guiWidget) // w.showWidgetPlacement(logNow, "drawTree()") - if n.direction == widget.Horizontal { - log.Log(NOW, "BOX IS HORIZONTAL", n.progname) + if w.direction == widget.Horizontal { + log.Log(NOW, "BOX IS HORIZONTAL", n.String()) } else { - log.Log(NOW, "BOX IS VERTICAL", n.progname) + log.Log(NOW, "BOX IS VERTICAL", n.String()) } - // n.placeWidgets() - n.toggleTree() + placeWidgets(n, me.RawW, me.RawH) + toggleTree(n) case widget.Button: - n.doUserEvent() + // doUserEvent(n) + me.myTree.SendUserEvent(n) case widget.Dropdown: log.Log(NOW, "do the dropdown here") if me.ddview == nil { me.ddview = addDropdown() - tk := me.ddview.tk + // n.TK = initWidget(n) + var tk *guiWidget + tk = me.ddview.TK.(*guiWidget) tk.gocuiSize.w0 = 20 tk.gocuiSize.w1 = 40 tk.gocuiSize.h0 = 10 @@ -196,28 +224,30 @@ func (n *node) doWidgetClick() { tk.v.Frame = true tk.v.Clear() fmt.Fprint(tk.v, "example.com\nwit.com") - me.ddview.SetVisible(true) + SetVisible(me.ddview, true) return } - log.Log(NOW, "doWidgetClick() visible =", me.ddview.Visible()) - if me.ddview.Visible() { - me.ddview.SetVisible(false) + log.Log(NOW, "doWidgetClick() visible =", Visible(me.ddview)) + var tk *guiWidget + tk = me.ddview.TK.(*guiWidget) + if Visible(me.ddview) { + SetVisible(me.ddview, false) me.baseGui.DeleteView("ddview") - me.ddview.tk.v = nil + tk.v = nil } else { var dnsList string - for i, s := range n.vals { - log.Log(NOW, "AddText()", n.progname, i, s) + for i, s := range tk.vals { + log.Log(NOW, "AddText()", n.String(), i, s) dnsList += s + "\n" } me.ddNode = n log.Log(NOW, "new dns list should be set to:", dnsList) - me.ddview.label = dnsList - me.ddview.SetText(dnsList) - me.ddview.SetVisible(true) + tk.label = dnsList + tk.SetText(dnsList) + SetVisible(me.ddview, true) } - for i, s := range n.vals { - log.Log(NOW, "AddText()", n.progname, i, s) + for i, s := range tk.vals { + log.Log(NOW, "AddText()", tk.String(), i, s) } default: } @@ -225,32 +255,35 @@ func (n *node) doWidgetClick() { var toggle bool = true -func (n *node) toggleTree() { +func toggleTree(n *tree.Node) { + var w *guiWidget + w = n.TK.(*guiWidget) if toggle { - n.drawTree(toggle) + drawTree(n, toggle) toggle = false } else { - n.hideWidgets() + w.hideWidgets() toggle = true } } // display the widgets in the binary tree -func (n *node) drawTree(draw bool) { - w := n.tk +func drawTree(n *tree.Node, draw bool) { + var w *guiWidget + w = n.TK.(*guiWidget) if w == nil { return } - n.showWidgetPlacement(true, "drawTree()") + showWidgetPlacement(n, true, "drawTree()") if draw { // w.textResize() - n.showView() + w.showView() } else { - n.deleteView() + w.deleteView() } - for _, child := range n.children { - child.drawTree(draw) + for _, child := range w.children { + drawTree(child.node, draw) } } @@ -261,14 +294,16 @@ func click(g *gocui.Gui, v *gocui.View) error { log.Log(INFO, "click() START", v.Name()) // n := me.rootNode.findWidgetName(v.Name()) n := findUnderMouse() + var w *guiWidget + w = n.TK.(*guiWidget) if n != nil { - log.Log(NOW, "click() Found widget =", n.WidgetId, n.progname, ",", n.label) - if n.progname == "DropBox" { + log.Log(NOW, "click() Found widget =", n.WidgetId, n.String(), ",", w.label) + if n.String() == "DropBox" { log.Log(NOW, "click() this is the dropdown menu. set a flag here what did I click? where is the mouse?") log.Log(NOW, "click() set a global dropdown clicked flag=true here") me.ddClicked = true } - n.doWidgetClick() + doWidgetClick(n) } else { log.Log(NOW, "click() could not find node name =", v.Name()) } @@ -282,17 +317,18 @@ func click(g *gocui.Gui, v *gocui.View) error { return nil } -func findUnderMouse() *node { - var found *node - var widgets []*node - var f func(n *node) +func findUnderMouse() *tree.Node { + var found *tree.Node + var widgets []*tree.Node + var f func(n *tree.Node) w, h := me.baseGui.MousePosition() // find buttons that are below where the mouse button click - f = func(n *node) { - widget := n.tk + f = func(n *tree.Node) { + var widget *guiWidget + widget = n.TK.(*guiWidget) // ignore widgets that are not visible - if n.Visible() { + if Visible(n) { if (widget.gocuiSize.w0 <= w) && (w <= widget.gocuiSize.w1) && (widget.gocuiSize.h0 <= h) && (h <= widget.gocuiSize.h1) { widgets = append(widgets, n) @@ -301,7 +337,7 @@ func findUnderMouse() *node { } if n == me.ddview { log.Log(NOW, "findUnderMouse() found ddview") - if n.Visible() { + if Visible(n) { log.Log(NOW, "findUnderMouse() and ddview is visable. hide it here. TODO: find highlighted row") found = n // find the actual value here and set the dropdown widget @@ -311,47 +347,51 @@ func findUnderMouse() *node { } } - for _, child := range n.children { - f(child) + for _, child := range widget.children { + f(child.node) } } - f(me.rootNode) + f(me.treeRoot) // 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(true, "findUnderMouse() FOUND") + showWidgetPlacement(n, true, "findUnderMouse() FOUND") } return found } // find the widget under the mouse click func ctrlDown(g *gocui.Gui, v *gocui.View) error { - var found *node + var found *tree.Node // var widgets []*node // var f func (n *node) found = findUnderMouse() if me.ctrlDown == nil { setupCtrlDownWidget() - me.ctrlDown.label = found.progname - me.ctrlDown.tk.cuiName = "ctrlDown" + + var tk *guiWidget + tk = me.ctrlDown.TK.(*guiWidget) + tk.label = found.String() + tk.cuiName = "ctrlDown" // me.ctrlDown.parent = me.rootNode } - cd := me.ctrlDown.tk + var tk *guiWidget + tk = me.ctrlDown.TK.(*guiWidget) if found == nil { - found = me.rootNode + found = me.treeRoot } - me.ctrlDown.label = found.progname - 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.hideView() + tk.label = found.String() + newR := realGocuiSize(found) + tk.gocuiSize.w0 = newR.w0 + tk.gocuiSize.h0 = newR.h0 + tk.gocuiSize.w1 = newR.w1 + tk.gocuiSize.h1 = newR.h1 + if tk.Visible() { + // me.ctrlDown.hideView() } else { - me.ctrlDown.showView() + // me.ctrlDown.showView() } - me.ctrlDown.showWidgetPlacement(true, "ctrlDown:") + // me.ctrlDown.showWidgetPlacement(true, "ctrlDown:") return nil } diff --git a/color.go b/color.go index 45600c3..4c9ffe6 100644 --- a/color.go +++ b/color.go @@ -1,10 +1,12 @@ package main import ( - "github.com/awesome-gocui/gocui" "math/rand" + "github.com/awesome-gocui/gocui" + "go.wit.com/log" + "go.wit.com/toolkits/tree" ) //w.v.SelBgColor = gocui.ColorCyan @@ -65,8 +67,10 @@ var colorNone colorT = colorT{none, none, none, none, none, "debug none"} // TODO: maybe enough of us could actually do that if we made it a goal. // TODO: start with riscv boards and fix it universally there // TODO: so just a small little 'todo' item here -func (n *node) setColor(newColor *colorT) { - tk := n.tk +func setColor(n *tree.Node, newColor *colorT) { + var tk *guiWidget + tk = n.TK.(*guiWidget) + if tk.color == newColor { // nothing to do since the colors have nto changed return @@ -80,15 +84,18 @@ func (n *node) setColor(newColor *colorT) { tk.color = &colorNone } log.Log(NOW, "Set the node to color =", tk.color.name) - n.recreateView() + tk.recreateView() } -func (n *node) setDefaultWidgetColor() { - n.showView() +func setDefaultWidgetColor(n *tree.Node) { + var w *guiWidget + w = n.TK.(*guiWidget) + w.showView() } -func (n *node) setDefaultHighlight() { - w := n.tk +func setDefaultHighlight(n *tree.Node) { + var w *guiWidget + w = n.TK.(*guiWidget) if w.v == nil { log.Log(ERROR, "SetColor() failed on view == nil") return @@ -104,17 +111,16 @@ func randColor() gocui.Attribute { return gocui.GetColor(colors[i]) } -func (n *node) redoColor(draw bool) { - w := n.tk +func (w *guiWidget) redoColor(draw bool) { if w == nil { return } log.Sleep(.05) - n.setDefaultHighlight() - n.setDefaultWidgetColor() + setDefaultHighlight(w.node) + setDefaultWidgetColor(w.node) - for _, child := range n.children { + for _, child := range w.children { child.redoColor(draw) } } diff --git a/common.go b/common.go deleted file mode 100644 index bb21b57..0000000 --- a/common.go +++ /dev/null @@ -1,218 +0,0 @@ -package main - -/* - These code should be common to all gui plugins - - There are some helper functions that are probably going to be - the same everywhere. Mostly due to handling the binary tree structure - and the channel communication - - For now, it's just a symlink to the 'master' version in - ./toolkit/nocui/common.go -*/ - -import ( - "go.wit.com/log" - "go.wit.com/widget" -) - -// this is the channel we send user events like -// mouse clicks or keyboard events back to the program -var callback chan widget.Action - -// this is the channel we get requests to make widgets -var pluginChan chan widget.Action - -type node struct { - parent *node - children []*node - - WidgetId int // widget ID - WidgetType widget.WidgetType - ParentId int // parent ID - - state widget.State - - // a reference name for programming and debuggign. Must be unique - progname string - - // the text used for button labesl, window titles, checkbox names, etc - label string - - // horizontal means layout widgets like books on a bookshelf - // vertical means layout widgets like books in a stack - // direction widget.Orientation - direction widget.Orientation - - // This is how the values are passed back and forth - // values from things like checkboxes & dropdown's - value any - - strings []string - - // This is used for things like a slider(0,100) - X int - Y int - - // This is for the grid size & widget position - W int - H int - AtW int - AtH int - - vals []string // dropdown menu items - - // horizontal bool `default:false` - - hasTabs bool // does the window have tabs? - currentTab bool // the visible tab - - // the internal plugin toolkit structure - // in the gtk plugin, it has gtk things like margin & border settings - // in the text console one, it has text console things like colors for menus & buttons - tk *guiWidget -} - -// searches the binary tree for a WidgetId -func (n *node) findWidgetId(id int) *node { - if n == nil { - return nil - } - - if n.WidgetId == id { - return n - } - - for _, child := range n.children { - newN := child.findWidgetId(id) - if newN != nil { - return newN - } - } - return nil -} - -func (n *node) doUserEvent() { - if callback == nil { - log.Log(ERROR, "doUserEvent() callback == nil", n.WidgetId) - return - } - var a widget.Action - a.WidgetId = n.WidgetId - a.Value = n.value - a.ActionType = widget.User - log.Log(INFO, "doUserEvent() START: send a user event to the callback channel") - callback <- a - log.Log(INFO, "doUserEvent() END: sent a user event to the callback channel") - return -} - -// Other goroutines must use this to access the GUI -// -// You can not acess / process the GUI thread directly from -// other goroutines. This is due to the nature of how -// Linux, MacOS and Windows work (they all work differently. suprise. surprise.) -// -// this sets the channel to send user events back from the plugin -func Callback(guiCallback chan widget.Action) { - callback = guiCallback -} - -func PluginChannel() chan widget.Action { - return pluginChan -} - -/* -func convertString(val any) string { - switch v := val.(type) { - case bool: - n.B = val.(bool) - case string: - n.label = val.(string) - n.S = val.(string) - case int: - n.I = val.(int) - default: - log.Error(errors.New("Set() unknown type"), "v =", v) - } -} -*/ - -/* -// this is in common.go, do not move it -func getString(A any) string { - if A == nil { - log.Warn("getString() got nil") - return "" - } - var k reflect.Kind - k = reflect.TypeOf(A).Kind() - - switch k { - case reflect.Int: - var i int - i = A.(int) - return string(i) - case reflect.String: - return A.(string) - case reflect.Bool: - if A.(bool) == true { - return "true" - } else { - return "false" - } - default: - log.Warn("getString uknown kind", k, "value =", A) - return "" - } - return "" -} -*/ - -// this is in common.go, do not move it -func addNode(a *widget.Action) *node { - n := new(node) - n.WidgetType = a.WidgetType - n.WidgetId = a.WidgetId - n.ParentId = a.ParentId - - n.state = a.State - - // copy the data from the action message - n.progname = a.ProgName - n.value = a.Value - n.direction = a.Direction - n.strings = a.Strings - - // TODO: these need to be rethought - 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 = initWidget(n) - // n.tk = new(guiWidget) - - if a.WidgetType == widget.Root { - log.Log(INFO, "addNode() Root") - return n - } - - if me.rootNode.findWidgetId(a.WidgetId) != nil { - log.Log(ERROR, "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 -} diff --git a/debug.go b/debug.go index c7888c0..0ff8e1d 100644 --- a/debug.go +++ b/debug.go @@ -2,59 +2,68 @@ package main import ( "fmt" + "go.wit.com/log" + "go.wit.com/toolkits/tree" "go.wit.com/widget" ) -func (n *node) dumpTree(draw bool) { - w := n.tk +func dumpTree(n *tree.Node, draw bool) { + w := n.TK.(*guiWidget) + log.Log(ERROR, "dumpTree n", n.WidgetId, n.WidgetType, n.String()) if w == nil { + log.Log(ERROR, "dumpTree n.TK == nil", n.WidgetId, n.WidgetType, n.String()) return } - n.showWidgetPlacement(true, "dumpTree()") + showWidgetPlacement(n, true, "dumpTree()") - for _, child := range n.children { - child.dumpTree(draw) + for _, child := range w.children { + dumpTree(child.node, draw) } } -func (n *node) showWidgetPlacement(b bool, s string) { +func showWidgetPlacement(n *tree.Node, b bool, s string) { if n == nil { log.Log(ERROR, "WTF w == nil") return } - w := n.tk + w := n.TK.(*guiWidget) + w.showWidgetPlacement(b, s) +} +func (w *guiWidget) showWidgetPlacement(b bool, s string) { var s1 string var pId int - if n.parent == nil { - log.Log(INFO, "showWidgetPlacement() parent == nil", n.WidgetId, w.cuiName) + if w.node.Parent == nil { + log.Log(INFO, "showWidgetPlacement() parent == nil", w.node.WidgetId, w.cuiName) pId = 0 } else { - pId = n.parent.WidgetId + pId = w.node.Parent.WidgetId } - s1 = fmt.Sprintf("(wId,pId)=(%2d,%2d) ", n.WidgetId, pId) - if n.Visible() { + s1 = fmt.Sprintf("(wId,pId)=(%2d,%2d) ", w.node.WidgetId, pId) + if w.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) } else { s1 += fmt.Sprintf(" ") } - if n.parent != nil { - if n.parent.WidgetType == widget.Grid { - s1 += fmt.Sprintf("At(%2d,%2d) ", n.AtW, n.AtH) + if w.node.Parent != nil { + if w.node.Parent.WidgetType == widget.Grid { + s1 += fmt.Sprintf("At(%2d,%2d) ", w.AtW, w.AtH) } } - tmp := "." + n.progname + "." - log.Log(INFO, s1, s, n.WidgetType, ",", tmp) // , "text=", w.text) + tmp := "." + w.String() + "." + log.Log(INFO, s1, s, w.node.WidgetType, ",", tmp) // , "text=", w.text) } -func (n *node) dumpWidget(pad string) { +/* +func dumpWidget(n *tree.Node, pad string) { log.Log(NOW, "node:", pad, n.WidgetId, "At(", n.AtW, n.AtH, ") ,", n.WidgetType, ", n.progname =", n.progname, ", n.label =", n.label) } - -func (n *node) listWidgets() { +*/ +/* +func listWidgets(n *tree.Node) { if n == nil { return } @@ -72,3 +81,4 @@ func (n *node) listWidgets() { } return } +*/ diff --git a/gocui.go b/gocui.go index 6d924f8..482cfc4 100644 --- a/gocui.go +++ b/gocui.go @@ -6,6 +6,7 @@ package main import ( "errors" + "github.com/awesome-gocui/gocui" "go.wit.com/log" diff --git a/keybindings.go b/keybindings.go index 2d4b4ad..669ecb3 100644 --- a/keybindings.go +++ b/keybindings.go @@ -8,7 +8,6 @@ import ( "github.com/awesome-gocui/gocui" "go.wit.com/log" - "go.wit.com/widget" ) func defaultKeybindings(g *gocui.Gui) error { @@ -51,11 +50,13 @@ func addDebugKeys(g *gocui.Gui) { func(g *gocui.Gui, v *gocui.View) error { fakeStartWidth = me.FakeW fakeStartHeight = me.TabH + me.FramePadH + var w *guiWidget + w = me.treeRoot.TK.(*guiWidget) if showDebug { - me.rootNode.showFake() + w.showFake() showDebug = false } else { - me.rootNode.hideFake() + w.hideFake() showDebug = true } return nil @@ -77,11 +78,13 @@ func addDebugKeys(g *gocui.Gui) { // redraw all the widgets g.SetKeybinding("", 'r', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { + var w *guiWidget + w = me.treeRoot.TK.(*guiWidget) if redoWidgets { redoWindows(0, 0) redoWidgets = false } else { - me.rootNode.hideWidgets() + w.hideWidgets() redoWidgets = true } return nil @@ -90,28 +93,32 @@ func addDebugKeys(g *gocui.Gui) { // hide all widgets g.SetKeybinding("", 'h', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { - me.rootNode.hideWidgets() + var w *guiWidget + w = me.treeRoot.TK.(*guiWidget) + w.hideWidgets() return nil }) // show all widgets g.SetKeybinding("", 's', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { - me.rootNode.showWidgets() + var w *guiWidget + w = me.treeRoot.TK.(*guiWidget) + w.showWidgets() return nil }) // list all widgets g.SetKeybinding("", 'L', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { - me.rootNode.listWidgets() + me.treeRoot.ListWidgets() return nil }) // list all widgets with positions g.SetKeybinding("", 'M', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { - me.rootNode.dumpTree(true) + dumpTree(me.treeRoot, true) return nil }) @@ -119,13 +126,15 @@ func addDebugKeys(g *gocui.Gui) { g.SetKeybinding("", 'o', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { log.Log(ERROR, "TODO: re-implement this") - if me.logStdout.Visible() { - me.logStdout.SetVisible(false) - // setOutput(os.Stdout) - } else { - me.logStdout.SetVisible(true) - // setOutput(me.logStdout.tk) - } + /* + if me.logStdout.Visible() { + me.logStdout.SetVisible(false) + // setOutput(os.Stdout) + } else { + me.logStdout.SetVisible(true) + // setOutput(me.logStdout.tk) + } + */ return nil }) @@ -143,10 +152,7 @@ func addDebugKeys(g *gocui.Gui) { g.SetKeybinding("", gocui.KeyCtrlD, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { if showDebug { - var a widget.Action - a.Value = true - a.ActionType = widget.EnableDebug - callback <- a + me.myTree.SendEnableDebugger() } return nil }) diff --git a/main.go b/main.go index 0ae6608..ab6d902 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,7 @@ import ( "os" "go.wit.com/log" - "go.wit.com/widget" + "go.wit.com/toolkits/tree" ) // sets defaults and establishes communication @@ -19,38 +19,19 @@ func init() { // init the config struct default values Set(&me, "default") - pluginChan = make(chan widget.Action) + me.myTree = tree.New() + me.myTree.PluginName = "gocui" + me.myTree.ActionFromChannel = action + + // pluginChan = make(chan widget.Action) log.Log(NOW, "Init() start pluginChan") - go catchActionChannel() + // go catchActionChannel() log.Sleep(.1) // probably not needed, but in here for now under development go main() log.Sleep(.1) // probably not needed, but in here for now under development } -/* -recieves requests from the program to do things like: -* add new widgets -* change the text of a label -* etc.. -*/ -func catchActionChannel() { - log.Log(INFO, "catchActionChannel() START") - for { - log.Log(INFO, "catchActionChannel() infinite for() loop restarted select on channel") - select { - case a := <-pluginChan: - if me.baseGui == nil { - // something went wrong initializing the gocui - log.Log(ERROR, "ERROR: console did not initialize") - continue - } - log.Log(INFO, "catchActionChannel()", a.WidgetId, a.ActionType, a.WidgetType, a.ProgName) - action(&a) - } - } -} - func Exit() { // TODO: what should actually happen here? log.Log(NOW, "Exit() here. doing standardExit()") @@ -65,18 +46,12 @@ func standardExit() { // log(true, "standardExit() setOutput(os.Stdout)") // setOutput(os.Stdout) log.Log(NOW, "standardExit() send back Quit()") - go sendBackQuit() // don't stall here in case the + // go sendBackQuit() // don't stall here in case the // induces a delay in case the callback channel is broken log.Sleep(1) log.Log(NOW, "standardExit() exit()") os.Exit(0) } -func sendBackQuit() { - // send 'Quit' back to the program (?) - var a widget.Action - a.ActionType = widget.UserQuit - callback <- a -} var outf *os.File diff --git a/mouse.go b/mouse.go index 02efc0d..791f51e 100644 --- a/mouse.go +++ b/mouse.go @@ -7,6 +7,7 @@ package main import ( "errors" "fmt" + "github.com/awesome-gocui/gocui" "go.wit.com/log" @@ -36,15 +37,17 @@ func msgDown(g *gocui.Gui, v *gocui.View) error { } func hideDDview() error { + var tk *guiWidget + tk = me.ddview.TK.(*guiWidget) w, h := me.baseGui.MousePosition() log.Log(NOW, "hide dropdown menu() view msgMouseDown (w,h) =", w, h) if me.ddview == nil { return gocui.ErrUnknownView } - if me.ddview.tk.v == nil { + if tk.v == nil { return gocui.ErrUnknownView } - me.ddview.SetVisible(false) + SetVisible(me.ddview, false) return nil } @@ -54,40 +57,50 @@ func showDDview() error { if me.ddview == nil { return gocui.ErrUnknownView } - if me.ddview.tk.v == nil { + var tk *guiWidget + tk = me.ddview.TK.(*guiWidget) + if tk.v == nil { return gocui.ErrUnknownView } - me.ddview.SetVisible(true) + SetVisible(me.ddview, true) return nil } func mouseUp(g *gocui.Gui, v *gocui.View) error { + var tk *guiWidget + tk = me.ddview.TK.(*guiWidget) + w, h := g.MousePosition() log.Log(NOW, "mouseUp() view msgMouseDown (check here for dropdown menu click) (w,h) =", w, h) if me.ddClicked { me.ddClicked = false log.Log(NOW, "mouseUp() ddview is the thing that was clicked", w, h) - log.Log(NOW, "mouseUp() find out what the string is here", w, h, me.ddview.tk.gocuiSize.h1) + log.Log(NOW, "mouseUp() find out what the string is here", w, h, tk.gocuiSize.h1) var newZone string = "" if me.ddNode != nil { - value := h - me.ddview.tk.gocuiSize.h0 - 1 - log.Log(NOW, "mouseUp() me.ddview.tk.gocuiSize.h1 =", me.ddview.tk.gocuiSize.h1) - log.Log(NOW, "mouseUp() me.ddNode.vals =", me.ddNode.vals) - valsLen := len(me.ddNode.vals) + var ddtk *guiWidget + ddtk = me.ddview.TK.(*guiWidget) + value := h - tk.gocuiSize.h0 - 1 + log.Log(NOW, "mouseUp() me.ddview.tk.gocuiSize.h1 =", tk.gocuiSize.h1) + log.Log(NOW, "mouseUp() me.ddNode.vals =", ddtk.vals) + valsLen := len(ddtk.vals) log.Log(NOW, "mouseUp() value =", value, "valsLen =", valsLen) - log.Log(NOW, "mouseUp() me.ddNode.vals =", me.ddNode.vals) + log.Log(NOW, "mouseUp() me.ddNode.vals =", ddtk.vals) if (value >= 0) && (value < valsLen) { - newZone = me.ddNode.vals[value] + newZone = ddtk.vals[value] log.Log(NOW, "mouseUp() value =", value, "newZone =", newZone) } } hideDDview() if newZone != "" { if me.ddNode != nil { - me.ddNode.SetText(newZone) - me.ddNode.value = newZone - me.ddNode.doUserEvent() + var ddtk *guiWidget + ddtk = me.ddview.TK.(*guiWidget) + ddtk.SetText(newZone) + ddtk.value = newZone + // me.ddNode.doUserEvent() + me.myTree.SendUserEvent(me.ddNode) } } return nil @@ -125,7 +138,7 @@ func mouseDown(g *gocui.Gui, v *gocui.View) error { test := findUnderMouse() msg := fmt.Sprintf("Mouse really down at: %d,%d", mx, my) + "foobar" if test == me.ddview { - if me.ddview.Visible() { + if Visible(me.ddview) { log.Log(NOW, "hide DDview() Mouse really down at:", mx, my) hideDDview() } else { diff --git a/place.go b/place.go index 217325d..9543e07 100644 --- a/place.go +++ b/place.go @@ -4,29 +4,33 @@ import ( "strings" "go.wit.com/log" + "go.wit.com/toolkits/tree" "go.wit.com/widget" ) -func (n *node) placeBox(startW int, startH int) { +func placeBox(n *tree.Node, startW int, startH int) { + var tk *guiWidget + tk = n.TK.(*guiWidget) + if n.WidgetType != widget.Box { return } - n.showWidgetPlacement(true, "boxS()") + showWidgetPlacement(n, true, "boxS()") newW := startW newH := startH - for _, child := range n.children { - child.placeWidgets(newW, newH) + for _, child := range tk.children { + placeWidgets(child.node, newW, newH) // n.showWidgetPlacement(logNow, "boxS()") - newR := child.realGocuiSize() + newR := realGocuiSize(child.node) w := newR.w1 - newR.w0 h := newR.h1 - newR.h0 - if n.direction == widget.Horizontal { - log.Log(NOW, "BOX IS HORIZONTAL", n.progname, "newWH()", newW, newH, "child()", w, h, child.progname) + if child.direction == widget.Horizontal { + log.Log(NOW, "BOX IS HORIZONTAL", n.String(), "newWH()", newW, newH, "child()", w, h, child.String()) // expand based on the child width newW += w } else { - log.Log(NOW, "BOX IS VERTICAL ", n.progname, "newWH()", newW, newH, "child()", w, h, child.progname) + log.Log(NOW, "BOX IS VERTICAL ", n.String(), "newWH()", newW, newH, "child()", w, h, child.String()) // expand based on the child height newH += h } @@ -35,43 +39,46 @@ func (n *node) placeBox(startW int, startH int) { // just compute this every time? // newR := n.realGocuiSize() - n.showWidgetPlacement(true, "boxE()") + showWidgetPlacement(n, true, "boxE()") } -func (n *node) placeWidgets(startW int, startH int) { +func placeWidgets(n *tree.Node, startW int, startH int) { if n == nil { return } - if me.rootNode == nil { + if me.treeRoot == nil { return } + var tk *guiWidget + tk = n.TK.(*guiWidget) + switch n.WidgetType { case widget.Window: - for _, child := range n.children { - child.placeWidgets(me.RawW, me.RawH) + for _, child := range tk.children { + placeWidgets(child.node, me.RawW, me.RawH) return } case widget.Tab: - for _, child := range n.children { - child.placeWidgets(me.RawW, me.RawH) + for _, child := range tk.children { + placeWidgets(child.node, me.RawW, me.RawH) return } case widget.Grid: - n.placeGrid(startW, startH) + placeGrid(n, startW, startH) case widget.Box: - n.placeBox(startW, startH) + placeBox(n, startW, startH) case widget.Group: // move the group to the parent's next location - n.gocuiSetWH(startW, startH) - n.showWidgetPlacement(true, "group()") + tk.gocuiSetWH(startW, startH) + showWidgetPlacement(n, true, "group()") 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) - newR := child.realGocuiSize() + for _, child := range tk.children { + placeWidgets(child.node, newW, newH) + newR := realGocuiSize(child.node) // w := newR.w1 - newR.w0 h := newR.h1 - newR.h0 @@ -79,21 +86,22 @@ func (n *node) placeWidgets(startW int, startH int) { newH += h } default: - n.gocuiSetWH(startW, startH) + tk.gocuiSetWH(startW, startH) // n.moveTo(startW, startH) } } -func (n *node) placeGrid(startW int, startH int) { - w := n.tk - n.showWidgetPlacement(true, "grid0:") +func placeGrid(n *tree.Node, startW int, startH int) { + var w *guiWidget + w = n.TK.(*guiWidget) + showWidgetPlacement(n, true, "grid0:") if n.WidgetType != widget.Grid { return } // first compute the max sizes of the rows and columns - for _, child := range n.children { - newR := child.realGocuiSize() + for _, child := range w.children { + newR := realGocuiSize(child.node) childW := newR.w1 - newR.w0 childH := newR.h1 - newR.h0 @@ -105,12 +113,12 @@ func (n *node) placeGrid(startW int, startH int) { w.heights[child.AtH] = childH } // child.showWidgetPlacement(logInfo, "grid: ") - log.Log(INFO, "placeGrid:", child.progname, "child()", childW, childH, "At()", child.AtW, child.AtH) + log.Log(INFO, "placeGrid:", child.String(), "child()", childW, childH, "At()", child.AtW, child.AtH) } // find the width and height offset of the grid for AtW,AtH - for _, child := range n.children { - child.showWidgetPlacement(true, "grid1:") + for _, child := range w.children { + showWidgetPlacement(w.node, true, "grid1:") var totalW, totalH int for i, w := range w.widths { @@ -128,16 +136,16 @@ func (n *node) placeGrid(startW int, startH int) { newW := startW + totalW newH := startH + totalH - log.Log(INFO, "placeGrid:", child.progname, "new()", newW, newH, "At()", child.AtW, child.AtH) - child.placeWidgets(newW, newH) - child.showWidgetPlacement(true, "grid2:") + log.Log(INFO, "placeGrid:", child.String(), "new()", newW, newH, "At()", child.AtW, child.AtH) + placeWidgets(child.node, newW, newH) + showWidgetPlacement(child.node, true, "grid2:") } - n.showWidgetPlacement(true, "grid3:") + showWidgetPlacement(n, true, "grid3:") } // 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) +func realGocuiSize(n *tree.Node) *rectType { + var f func(n *tree.Node, r *rectType) newR := new(rectType) // initialize the values to opposite newR.w0 = 80 @@ -151,9 +159,11 @@ func (n *node) realGocuiSize() *rectType { 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 { + f = func(n *tree.Node, r *rectType) { + var tk *guiWidget + tk = n.TK.(*guiWidget) + newR := tk.gocuiSize + if !tk.isFake { if r.w0 > newR.w0 { r.w0 = newR.w0 } @@ -167,18 +177,20 @@ func (n *node) realGocuiSize() *rectType { r.h1 = newR.h1 } } - for _, child := range n.children { - f(child, r) + for _, child := range tk.children { + f(child.node, r) } } f(n, newR) return newR } -func (n *node) textSize() (int, int) { +func textSize(n *tree.Node) (int, int) { + var tk *guiWidget + tk = n.TK.(*guiWidget) var width, height int - for _, s := range strings.Split(widget.GetString(n.value), "\n") { + for _, s := range strings.Split(widget.GetString(tk.value), "\n") { if width < len(s) { width = len(s) } diff --git a/plugin.go b/plugin.go index e18d4d1..7bf6757 100644 --- a/plugin.go +++ b/plugin.go @@ -7,19 +7,27 @@ import ( "go.wit.com/widget" ) -func action(a *widget.Action) { +func action(a widget.Action) { log.Log(INFO, "action() START", a.WidgetId, a.ActionType, a.WidgetType, a.ProgName) - n := me.rootNode.findWidgetId(a.WidgetId) + // n := me.rootNode.findWidgetId(a.WidgetId) + n := me.treeRoot.FindWidgetId(a.WidgetId) var w *guiWidget if n != nil { - w = n.tk + w = n.TK.(*guiWidget) } switch a.ActionType { case widget.Add: if w == nil { - n := addNode(a) - // w = n.tk - n.addWidget() + n := me.myTree.AddNode(&a) + if n == nil { + log.Warn("WTF") + panic("WTF") + } + n.TK = initWidget(n) + if n.WidgetType == widget.Root { + me.treeRoot = n + } + addWidget(n) } else { // this is done to protect the plugin being 'refreshed' with the // widget binary tree. TODO: find a way to keep them in sync @@ -28,42 +36,42 @@ func action(a *widget.Action) { } case widget.Show: if widget.GetBool(a.Value) { - n.showView() + w.showView() } else { - n.hideWidgets() + w.hideWidgets() } case widget.Set: if a.WidgetType == widget.Flag { log.Log(NOW, "TODO: set flag here", a.ActionType, a.WidgetType, a.ProgName) - log.Log(NOW, "TODO: n.WidgetType =", n.WidgetType, "n.progname =", a.ProgName) + log.Log(NOW, "TODO: n.WidgetType =", n.WidgetType, "n.String() =", a.ProgName) } else { if a.Value == nil { log.Log(ERROR, "TODO: Set here. a == nil id =", a.WidgetId, "type =", a.WidgetType, "Name =", a.ProgName) - log.Log(ERROR, "TODO: Set here. id =", a.WidgetId, "n.progname =", n.progname) + log.Log(ERROR, "TODO: Set here. id =", a.WidgetId, "n.String() =", n.String()) } else { - n.Set(a.Value) + w.Set(a.Value) } } case widget.SetText: - n.SetText(widget.GetString(a.Value)) + w.SetText(widget.GetString(a.Value)) case widget.AddText: - n.AddText(widget.GetString(a.Value)) + w.AddText(widget.GetString(a.Value)) case widget.Move: log.Log(NOW, "attempt to move() =", a.ActionType, a.WidgetType, a.ProgName) case widget.ToolkitClose: log.Log(NOW, "attempting to close the plugin and release stdout and stderr") standardExit() case widget.Enable: - if n.Visible() { + if w.Visible() { // widget was already shown } else { - log.Log(INFO, "Setting Visable to true", a.ProgName) - n.SetVisible(true) + log.Log(INFO, "Setting Visible to true", a.ProgName) + SetVisible(n, true) } case widget.Disable: - if n.Visible() { - log.Log(INFO, "Setting Visable to false", a.ProgName) - n.SetVisible(false) + if w.Visible() { + log.Log(INFO, "Setting Visible to false", a.ProgName) + SetVisible(n, false) } else { // widget was already hidden } @@ -73,45 +81,44 @@ func action(a *widget.Action) { log.Log(INFO, "action() END") } -func (n *node) AddText(text string) { - if n == nil { +func (w *guiWidget) AddText(text string) { + if w == nil { log.Log(NOW, "widget is nil") return } - n.vals = append(n.vals, text) - for i, s := range n.vals { - log.Log(NOW, "AddText()", n.progname, i, s) + w.vals = append(w.vals, text) + for i, s := range w.vals { + log.Log(NOW, "AddText()", w.String(), i, s) } - n.SetText(text) + w.SetText(text) } -func (n *node) SetText(text string) { +func (w *guiWidget) SetText(text string) { var changed bool = false - if n == nil { + if w == nil { log.Log(NOW, "widget is nil") return } - if widget.GetString(n.value) != text { - n.value = text + if widget.GetString(w.value) != text { + w.value = text changed = true } if !changed { return } - if n.Visible() { - n.textResize() - n.deleteView() - n.showView() + if w.Visible() { + w.textResize() + w.deleteView() + w.showView() } } -func (n *node) Set(val any) { - // w := n.tk +func (w *guiWidget) Set(val any) { log.Log(INFO, "Set() value =", val) - n.value = val - if n.WidgetType != widget.Checkbox { - n.setCheckbox(val) + w.value = val.(string) + if w.node.WidgetType != widget.Checkbox { + w.setCheckbox(val) } } diff --git a/showStdout.go b/showStdout.go index d247b42..06feb78 100644 --- a/showStdout.go +++ b/showStdout.go @@ -43,7 +43,7 @@ func showMsg(g *gocui.Gui, v *gocui.View) error { func makeOutputWidget(g *gocui.Gui, stringFromMouseClick string) *gocui.View { maxX, maxY := g.Size() - if me.rootNode == nil { + if me.treeRoot == nil { // keep skipping this until the binary tree is initialized return nil } @@ -54,12 +54,17 @@ func makeOutputWidget(g *gocui.Gui, stringFromMouseClick string) *gocui.View { a.WidgetType = widget.Stdout a.WidgetId = -3 a.ParentId = 0 - n := addNode(a) + // n := addNode(a) + n := me.myTree.AddNode(a) + n.TK = initWidget(n) 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 + + var tk *guiWidget + tk = me.logStdout.TK.(*guiWidget) + tk.gocuiSize.w0 = maxX - 32 + tk.gocuiSize.h0 = maxY / 2 + tk.gocuiSize.w1 = tk.gocuiSize.w0 + outputW + tk.gocuiSize.h1 = tk.gocuiSize.h0 + outputH } v, err := g.View("msg") if v == nil { @@ -85,7 +90,9 @@ func makeOutputWidget(g *gocui.Gui, stringFromMouseClick string) *gocui.View { log.Log(NOW, "makeoutputwindow() msg == nil. WTF now? err =", err) return nil } else { - me.logStdout.tk.v = v + var tk *guiWidget + tk = me.logStdout.TK.(*guiWidget) + tk.v = v } v.Clear() diff --git a/structs.go b/structs.go index e899f78..0599a0c 100644 --- a/structs.go +++ b/structs.go @@ -10,13 +10,16 @@ package main import ( "fmt" - "github.com/awesome-gocui/gocui" "reflect" "strconv" "strings" "sync" + "github.com/awesome-gocui/gocui" + "go.wit.com/log" + "go.wit.com/toolkits/tree" + "go.wit.com/widget" ) // It's probably a terrible idea to call this 'me' @@ -27,19 +30,22 @@ var showHelp bool = true var redoWidgets bool = true // This is the window that is currently active -var currentWindow *node +var currentWindow *tree.Node type config struct { baseGui *gocui.Gui // the main gocui handle - rootNode *node // the base of the binary tree. it should have id == 0 + // 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 - currentWindow *node // this is the current tab or window to show - logStdout *node // where to show STDOUT + treeRoot *tree.Node // the base of the binary tree. it should have id == 0 + myTree *tree.TreeInfo + + ctrlDown *tree.Node // shown if you click the mouse when the ctrl key is pressed + currentWindow *tree.Node // this is the current tab or window to show + logStdout *tree.Node // where to show STDOUT helpLabel *gocui.View - ddview *node // the gocui view to select dropdrown lists - ddClicked bool // the dropdown menu view was clicked - ddNode *node // the dropdown menu is for this widget + ddview *tree.Node // the gocui view to select dropdrown lists + ddClicked bool // the dropdown menu view was clicked + ddNode *tree.Node // the dropdown menu is for this widget /* // this is the channel we send user events like @@ -118,9 +124,32 @@ type guiWidget struct { v *gocui.View // this is nil if the widget is not displayed cuiName string // what gocui uses to reference the widget + WidgetType widget.WidgetType + + // tw *toolkit.Widget + parent *guiWidget + children []*guiWidget + + node *tree.Node + + hasTabs bool // does the window have tabs? + currentTab bool // the visible tab + + value string + checked bool + // the actual text to display in the console label string + vals []string // dropdown menu items + + AtW int + AtH int + + direction widget.Orientation + + progname string + // the logical size of the widget // For example, 40x12 would be the center of a normal terminal // size rectType @@ -140,7 +169,7 @@ type guiWidget struct { frame bool // for a window, this is currently selected tab - selectedTab *node + selectedTab *tree.Node // what color to use color *colorT @@ -156,7 +185,9 @@ func (w *guiWidget) Write(p []byte) (n int, err error) { w.tainted = true me.writeMutex.Lock() defer me.writeMutex.Unlock() - if me.logStdout.tk.v == nil { + var tk *guiWidget + tk = me.logStdout.TK.(*guiWidget) + if tk.v == nil { // optionally write the output to /tmp s := fmt.Sprint(string(p)) s = strings.TrimSuffix(s, "\n") @@ -164,11 +195,11 @@ func (w *guiWidget) Write(p []byte) (n int, err error) { v, _ := me.baseGui.View("msg") if v != nil { // fmt.Fprintln(outf, "found msg") - me.logStdout.tk.v = v + tk.v = v } } else { // display the output in the gocui window - me.logStdout.tk.v.Clear() + tk.v.Clear() s := fmt.Sprint(string(p)) s = strings.TrimSuffix(s, "\n") @@ -178,7 +209,7 @@ func (w *guiWidget) Write(p []byte) (n int, err error) { l := len(outputS) - outputH outputS = outputS[l:] } - fmt.Fprintln(me.logStdout.tk.v, strings.Join(outputS, "\n")) + fmt.Fprintln(tk.v, strings.Join(outputS, "\n")) } return len(p), nil diff --git a/tab.go b/tab.go index 23b254d..d2b57ce 100644 --- a/tab.go +++ b/tab.go @@ -23,12 +23,12 @@ func (w *guiWidget) Height() int { return w.gocuiSize.h1 - w.gocuiSize.h0 - 1 } -func (n *node) gocuiSetWH(sizeW, sizeH int) { - w := len(widget.GetString(n.value)) - lines := strings.Split(widget.GetString(n.value), "\n") +func (tk *guiWidget) gocuiSetWH(sizeW, sizeH int) { + w := len(widget.GetString(tk.value)) + lines := strings.Split(widget.GetString(tk.value), "\n") h := len(lines) - tk := n.tk + // tk := n.tk if tk.isFake { tk.gocuiSize.w0 = sizeW tk.gocuiSize.h0 = sizeH @@ -51,61 +51,60 @@ func (n *node) gocuiSetWH(sizeW, sizeH int) { } func redoWindows(nextW int, nextH int) { - for _, n := range me.rootNode.children { - if n.WidgetType != widget.Window { + wRoot := me.treeRoot.TK.(*guiWidget) + for _, win := range wRoot.children { + if win.node.WidgetType != widget.Window { continue } - w := n.tk var tabs bool - for _, child := range n.children { - if child.WidgetType == widget.Tab { + for _, child := range win.children { + if child.node.WidgetType == widget.Tab { tabs = true } } if tabs { // window is tabs. Don't show it as a standard button - w.frame = false - n.hasTabs = true + win.frame = false + win.hasTabs = true } else { - w.frame = false - n.hasTabs = false + win.frame = false + win.hasTabs = false } - n.gocuiSetWH(nextW, nextH) - n.deleteView() - n.showView() + win.gocuiSetWH(nextW, nextH) + win.deleteView() + win.showView() - sizeW := w.Width() + me.WindowPadW - sizeH := w.Height() + sizeW := win.Width() + me.WindowPadW + sizeH := win.Height() nextW += sizeW - log.Log(NOW, "redoWindows() start nextW,H =", nextW, nextH, "gocuiSize.W,H =", sizeW, sizeH, n.progname) + log.Log(NOW, "redoWindows() start nextW,H =", nextW, nextH, "gocuiSize.W,H =", sizeW, sizeH, win.String()) - if n.hasTabs { - n.redoTabs(me.TabW, me.TabH) + if win.hasTabs { + win.redoTabs(me.TabW, me.TabH) } } } -func (p *node) redoTabs(nextW int, nextH int) { - for _, n := range p.children { - if n.WidgetType != widget.Tab { +func (p *guiWidget) redoTabs(nextW int, nextH int) { + for _, w := range p.children { + if w.node.WidgetType != widget.Tab { continue } - w := n.tk w.frame = true - n.gocuiSetWH(nextW, nextH) - n.deleteView() + w.gocuiSetWH(nextW, nextH) + w.deleteView() // setCurrentTab(n) // if (len(w.cuiName) < 4) { // w.cuiName = "abcd" // } - n.showView() + w.showView() sizeW := w.Width() + me.TabPadW sizeH := w.Height() - log.Log(NOW, "redoTabs() start nextW,H =", nextW, nextH, "gocuiSize.W,H =", sizeW, sizeH, n.progname) + log.Log(NOW, "redoTabs() start nextW,H =", nextW, nextH, "gocuiSize.W,H =", sizeW, sizeH, w.String()) nextW += sizeW } } diff --git a/tree.go b/tree.go new file mode 100644 index 0000000..57f283a --- /dev/null +++ b/tree.go @@ -0,0 +1,24 @@ +package main + +/* + This is reference code for toolkit developers +*/ + +import ( + "go.wit.com/widget" +) + +// Other goroutines must use this to access the GUI +// +// You can not acess / process the GUI thread directly from +// other goroutines. This is due to the nature of how +// Linux, MacOS and Windows work (they all work differently. suprise. surprise.) +// +// this sets the channel to send user events back from the plugin +func Callback(guiCallback chan widget.Action) { + me.myTree.Callback(guiCallback) +} + +func PluginChannel() chan widget.Action { + return me.myTree.PluginChannel() +} diff --git a/view.go b/view.go index 7e6dfd4..7a26105 100644 --- a/view.go +++ b/view.go @@ -21,12 +21,12 @@ func splitLines(s string) []string { return lines } -func (n *node) textResize() bool { - w := n.tk +func (w *guiWidget) textResize() bool { + // w := n.tk var width, height int = 0, 0 var changed bool = false - for i, s := range splitLines(n.tk.label) { + for i, s := range splitLines(w.label) { log.Log(INFO, "textResize() len =", len(s), i, s) if width < len(s) { width = len(s) @@ -42,78 +42,76 @@ func (n *node) textResize() bool { changed = true } if changed { - n.showWidgetPlacement(true, "textResize() changed") + showWidgetPlacement(w.node, true, "textResize() changed") } return changed } -func (n *node) hideView() { - n.SetVisible(false) +func (w *guiWidget) hideView() { + SetVisible(w.node, false) } // display's the text of the widget in gocui // will create a new gocui view if there isn't one or if it has been moved -func (n *node) showView() { +func (w *guiWidget) showView() { var err error - w := n.tk if w.cuiName == "" { log.Log(ERROR, "showView() w.cuiName was not set for widget", w) - w.cuiName = string(n.WidgetId) + w.cuiName = string(w.node.WidgetId) } // if the gocui element doesn't exist, create it if w.v == nil { - n.recreateView() + w.recreateView() } x0, y0, x1, y1, err := me.baseGui.ViewPosition(w.cuiName) - log.Log(INFO, "showView() w.v already defined for widget", n.progname, err) + log.Log(INFO, "showView() w.v already defined for widget", w.String(), err) // n.smartGocuiSize() - changed := n.textResize() + changed := w.textResize() if changed { log.Log(NOW, "showView() textResize() changed. Should recreateView here wId =", w.cuiName) } else { log.Log(NOW, "showView() Clear() and Fprint() here wId =", w.cuiName) w.v.Clear() - fmt.Fprint(w.v, n.tk.label) - n.SetVisible(false) - n.SetVisible(true) + fmt.Fprint(w.v, w.label) + w.SetVisible(false) + w.SetVisible(true) return } // if the gocui element has changed where it is supposed to be on the screen // recreate it if x0 != w.gocuiSize.w0 { - n.recreateView() + w.recreateView() return } if y0 != w.gocuiSize.h0 { log.Log(ERROR, "showView() start hight mismatch id=", w.cuiName, "gocui h vs computed h =", w.gocuiSize.h0, y0) - n.recreateView() + w.recreateView() return } if x1 != w.gocuiSize.w1 { log.Log(ERROR, "showView() too wide", w.cuiName, "w,w", w.gocuiSize.w1, x1) - n.recreateView() + w.recreateView() return } if y1 != w.gocuiSize.h1 { log.Log(ERROR, "showView() too high", w.cuiName, "h,h", w.gocuiSize.h1, y1) - n.recreateView() + w.recreateView() return } - n.SetVisible(true) + w.SetVisible(true) } // create or recreate the gocui widget visible // deletes the old view if it exists and recreates it -func (n *node) recreateView() { +func (w *guiWidget) recreateView() { var err error - w := n.tk - log.Log(ERROR, "recreateView() START", n.WidgetType, n.progname) + log.Log(ERROR, "recreateView() START", w.WidgetType, w.String()) if me.baseGui == nil { log.Log(ERROR, "recreateView() ERROR: me.baseGui == nil", w) return @@ -123,11 +121,11 @@ func (n *node) recreateView() { me.baseGui.DeleteView(w.cuiName) w.v = nil - if n.progname == "CLOUDFLARE_EMAIL" { - n.showWidgetPlacement(true, "n.progname="+n.progname+" n.tk.label="+n.tk.label+" "+w.cuiName) - n.dumpWidget("jwc") - n.textResize() - n.showWidgetPlacement(true, "n.progname="+n.progname+" n.tk.label="+n.tk.label+" "+w.cuiName) + if w.String() == "CLOUDFLARE_EMAIL" { + w.showWidgetPlacement(true, "n.String()="+w.String()+" n.tk.label="+w.label+" "+w.cuiName) + // w.dumpWidget("jwc") + w.textResize() + w.showWidgetPlacement(true, "n.String()="+w.String()+" n.tk.label="+w.label+" "+w.cuiName) } a := w.gocuiSize.w0 @@ -137,12 +135,12 @@ func (n *node) recreateView() { w.v, err = me.baseGui.SetView(w.cuiName, a, b, c, d, 0) if err == nil { - n.showWidgetPlacement(true, "recreateView()") + showWidgetPlacement(w.node, true, "recreateView()") log.Log(ERROR, "recreateView() internal plugin error err = nil") return } if !errors.Is(err, gocui.ErrUnknownView) { - n.showWidgetPlacement(true, "recreateView()") + showWidgetPlacement(w.node, true, "recreateView()") log.Log(ERROR, "recreateView() internal plugin error error.IS()", err) return } @@ -161,8 +159,8 @@ func (n *node) recreateView() { w.v.Wrap = true w.v.Frame = w.frame w.v.Clear() - fmt.Fprint(w.v, n.tk.label) - // n.showWidgetPlacement(true, "n.progname=" + n.progname + " n.tk.label=" + n.tk.label + " " + w.cuiName) + fmt.Fprint(w.v, w.label) + // n.showWidgetPlacement(true, "n.String()=" + n.String() + " n.tk.label=" + n.tk.label + " " + w.cuiName) // n.dumpWidget("jwc 2") // if you don't do this here, it will be black & white only @@ -173,61 +171,57 @@ func (n *node) recreateView() { w.v.SelFgColor = w.color.selFg w.v.SelBgColor = w.color.selBg } - if n.progname == "CLOUDFLARE_EMAIL" { - n.showWidgetPlacement(true, "n.progname="+n.progname+" n.tk.label="+n.tk.label+" "+w.cuiName) - n.dumpTree(true) + if w.String() == "CLOUDFLARE_EMAIL" { + w.showWidgetPlacement(true, "w.String()="+w.String()+" w.label="+w.label+" "+w.cuiName) + dumpTree(w.node, true) } log.Log(ERROR, "recreateView() END") } -func (n *node) hideWidgets() { - w := n.tk +func (w *guiWidget) hideWidgets() { w.isCurrent = false - switch n.WidgetType { + switch w.node.WidgetType { case widget.Root: case widget.Flag: case widget.Window: case widget.Box: case widget.Grid: default: - n.hideView() + w.hideView() } - for _, child := range n.children { + for _, child := range w.children { child.hideWidgets() } } -func (n *node) hideFake() { - w := n.tk +func (w *guiWidget) hideFake() { if w.isFake { - n.hideView() + w.hideView() } - for _, child := range n.children { + for _, child := range w.children { child.hideFake() } } -func (n *node) showFake() { - w := n.tk +func (w *guiWidget) showFake() { if w.isFake { - n.setFake() - n.showWidgetPlacement(true, "showFake:") - n.showView() + // w.setFake() + w.showWidgetPlacement(true, "showFake:") + w.showView() } - for _, child := range n.children { + for _, child := range w.children { child.showFake() } } -func (n *node) showWidgets() { - w := n.tk +func (w *guiWidget) showWidgets() { if w.isFake { // don't display by default } else { - n.showWidgetPlacement(true, "current:") - n.showView() + w.showWidgetPlacement(true, "current:") + w.showView() } - for _, child := range n.children { + for _, child := range w.children { child.showWidgets() } } diff --git a/widget.go b/widget.go index dc1b405..f854a3a 100644 --- a/widget.go +++ b/widget.go @@ -2,10 +2,11 @@ package main import ( "go.wit.com/log" + "go.wit.com/toolkits/tree" "go.wit.com/widget" ) -func initWidget(n *node) *guiWidget { +func initWidget(n *tree.Node) *guiWidget { var w *guiWidget w = new(guiWidget) // Set(w, "default") @@ -15,11 +16,12 @@ func initWidget(n *node) *guiWidget { // set the name used by gocui to the id w.cuiName = string(n.WidgetId) + w.node = n + w.WidgetType = n.WidgetType + if n.WidgetType == widget.Root { log.Log(INFO, "setupWidget() FOUND ROOT w.id =", n.WidgetId) - n.WidgetId = 0 - me.rootNode = n - return w + // me.treeRoot = n } if n.WidgetType == widget.Grid { @@ -36,13 +38,13 @@ func setupCtrlDownWidget() { a.WidgetType = widget.Dialog a.WidgetId = -1 a.ParentId = 0 - n := addNode(a) + // n := addNode(a) + n := me.myTree.AddNode(a) me.ctrlDown = n } -func (n *node) deleteView() { - w := n.tk +func (w *guiWidget) deleteView() { if w.v != nil { w.v.Visible = false return @@ -52,8 +54,9 @@ func (n *node) deleteView() { w.v = nil } +/* // searches the binary tree for a WidgetId -func (n *node) findWidgetName(name string) *node { +func findWidgetName(n *tree.Node, name string) *node { if n == nil { return nil } @@ -70,70 +73,94 @@ func (n *node) findWidgetName(name string) *node { } return nil } +*/ -func (n *node) IsCurrent() bool { - w := n.tk - if n.WidgetType == widget.Tab { +func (w *guiWidget) IsCurrent() bool { + if w.node.WidgetType == widget.Tab { return w.isCurrent } - if n.WidgetType == widget.Window { + if w.node.WidgetType == widget.Window { return w.isCurrent } - if n.WidgetType == widget.Root { + if w.node.WidgetType == widget.Root { return false } - return n.parent.IsCurrent() + return w.parent.IsCurrent() } -func (n *node) Visible() bool { +func (tk *guiWidget) String() string { + return tk.progname +} + +func (tk *guiWidget) Visible() bool { + if tk == nil { + return false + } + if tk.v == nil { + return false + } + return tk.v.Visible +} + +func Visible(n *tree.Node) bool { if n == nil { return false } - if n.tk == nil { + if n.TK == nil { return false } - if n.tk.v == nil { - return false - } - return n.tk.v.Visible + var w *guiWidget + w = n.TK.(*guiWidget) + return w.Visible() } -func (n *node) SetVisible(b bool) { +func (w *guiWidget) SetVisible(b bool) { + if w.v == nil { + return + } + w.v.Visible = b +} + +func SetVisible(n *tree.Node, b bool) { if n == nil { return } - if n.tk == nil { + if n.TK == nil { return } - if n.tk.v == nil { + var w *guiWidget + w = n.TK.(*guiWidget) + if w.v == nil { return } - n.tk.v.Visible = b + w.v.Visible = b } -func addDropdown() *node { - n := new(node) +func addDropdown() *tree.Node { + n := new(tree.Node) n.WidgetType = widget.Flag n.WidgetId = -2 n.ParentId = 0 - // copy the data from the action message - n.progname = "DropBox" - n.tk.label = "DropBox text" - // store the internal toolkit information - n.tk = new(guiWidget) - n.tk.frame = true + tk := new(guiWidget) + tk.frame = true + tk.label = "DropBox text" + + // copy the data from the action message + tk.progname = "DropBox" // set the name used by gocui to the id - n.tk.cuiName = "-1 dropbox" + tk.cuiName = "-1 dropbox" - n.tk.color = &colorFlag + tk.color = &colorFlag // add this new widget on the binary tree - n.parent = me.rootNode - if n.parent != nil { - n.parent.children = append(n.parent.children, n) + tk.parent = me.treeRoot.TK.(*guiWidget) + if tk.parent != nil { + tk.parent.children = append(tk.parent.children, tk) } + + n.TK = tk return n }