From 4b6207743b90968d6b822032a4355e43b6ce6da9 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Mon, 3 Apr 2023 10:26:47 -0500 Subject: gocui: working towards correct layout make a gocui widget binary tree more debugging cleanups sample button app displays in gocui geometry logic closer to correct improvements in gocui layout continued attempts to clean up tabs dump binary tree moving towards proper chan callback() deprecate Widget.Name Signed-off-by: Jeff Carr --- toolkit/gocui/, | 191 +++++++++++++++++++++++++++++++++++++ toolkit/gocui/Makefile | 4 + toolkit/gocui/button.go | 53 ----------- toolkit/gocui/click.go | 165 ++++++++++++++++++++++++++++++++ toolkit/gocui/color.go | 113 ++++++++++++++++++++++ toolkit/gocui/common.go | 100 ++++++++++++++++++++ toolkit/gocui/debug.go | 143 ++++++++++------------------ toolkit/gocui/gocui.go | 112 ---------------------- toolkit/gocui/group.go | 36 ------- toolkit/gocui/help.go | 49 ++++++++++ toolkit/gocui/keybindings.go | 146 ++++++++-------------------- toolkit/gocui/log.go | 8 ++ toolkit/gocui/logical.go | 72 ++++++++++++++ toolkit/gocui/main.go | 40 ++++++++ toolkit/gocui/mouse.go | 171 +++++++++++++++++++++++++++++++++ toolkit/gocui/place.go | 221 +++++++++++++++++++++++++++++++++++++++++++ toolkit/gocui/plugin.go | 153 ++++++++++++------------------ toolkit/gocui/structs.go | 142 +++++++++++++++++++++++++++ toolkit/gocui/tab.go | 88 +++++++++++++++++ toolkit/gocui/view.go | 86 +++++++++++++++++ toolkit/gocui/views.go | 80 ---------------- toolkit/gocui/window.go | 16 ---- 22 files changed, 1601 insertions(+), 588 deletions(-) create mode 100644 toolkit/gocui/, delete mode 100644 toolkit/gocui/button.go create mode 100644 toolkit/gocui/click.go create mode 100644 toolkit/gocui/color.go create mode 100644 toolkit/gocui/common.go delete mode 100644 toolkit/gocui/gocui.go delete mode 100644 toolkit/gocui/group.go create mode 100644 toolkit/gocui/help.go create mode 100644 toolkit/gocui/logical.go create mode 100644 toolkit/gocui/main.go create mode 100644 toolkit/gocui/mouse.go create mode 100644 toolkit/gocui/place.go create mode 100644 toolkit/gocui/structs.go create mode 100644 toolkit/gocui/tab.go create mode 100644 toolkit/gocui/view.go delete mode 100644 toolkit/gocui/views.go delete mode 100644 toolkit/gocui/window.go (limited to 'toolkit/gocui') diff --git a/toolkit/gocui/, b/toolkit/gocui/, new file mode 100644 index 0000000..762f8eb --- /dev/null +++ b/toolkit/gocui/, @@ -0,0 +1,191 @@ +// Copyright 2014 The gocui Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "errors" + "fmt" + "log" + + "github.com/awesome-gocui/gocui" +) + +func main() { + g, err := gocui.NewGui(gocui.OutputNormal, true) + if err != nil { + log.Panicln(err) + } + defer g.Close() + + g.Cursor = false + g.Mouse = true + + g.SetManagerFunc(layout) + + if err := keybindings(g); err != nil { + log.Panicln(err) + } + + if err := g.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) { + log.Panicln(err) + } +} + +var initialMouseX, initialMouseY, xOffset, yOffset int +var globalMouseDown, msgMouseDown, movingMsg bool + +func layout(g *gocui.Gui) error { + maxX, maxY := g.Size() + if _, err := g.View("msg"); msgMouseDown && err == nil { + moveMsg(g) + } + if v, err := g.SetView("global", -1, -1, maxX, maxY, 0); err != nil { + if !errors.Is(err, gocui.ErrUnknownView) { + return err + } + v.Frame = false + } + if v, err := g.SetView("but1", 2, 2, 22, 7, 0); err != nil { + if !errors.Is(err, gocui.ErrUnknownView) { + return err + } + v.SelBgColor = gocui.ColorGreen + v.SelFgColor = gocui.ColorBlack + fmt.Fprintln(v, "Button 1 - line 1") + fmt.Fprintln(v, "Button 1 - line 2") + fmt.Fprintln(v, "Button 1 - line 3") + fmt.Fprintln(v, "Button 1 - line 4") + if _, err := g.SetCurrentView("but1"); err != nil { + return err + } + } + if v, err := g.SetView("but2", 24, 2, 44, 4, 0); err != nil { + if !errors.Is(err, gocui.ErrUnknownView) { + return err + } + v.SelBgColor = gocui.ColorGreen + v.SelFgColor = gocui.ColorBlack + fmt.Fprintln(v, "Button 2 - line 1") + } + updateHighlightedView(g) + return nil +} + +func keybindings(g *gocui.Gui) error { + if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { + return err + } + for _, n := range []string{"but1", "but2"} { + if err := g.SetKeybinding(n, gocui.MouseLeft, gocui.ModNone, showMsg); err != nil { + return err + } + } + if err := g.SetKeybinding("", gocui.MouseRelease, gocui.ModNone, mouseUp); err != nil { + return err + } + if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, globalDown); err != nil { + return err + } + if err := g.SetKeybinding("msg", gocui.MouseLeft, gocui.ModNone, msgDown); err != nil { + return err + } + return nil +} + +func quit(g *gocui.Gui, v *gocui.View) error { + return gocui.ErrQuit +} + +func showMsg(g *gocui.Gui, v *gocui.View) error { + var l string + var err error + + 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) + } + return nil +} + +func updateHighlightedView(g *gocui.Gui) { + mx, my := g.MousePosition() + for _, view := range g.Views() { + view.Highlight = false + } + if v, err := g.ViewByPosition(mx, my); err == nil { + v.Highlight = true + } +} + +func moveMsg(g *gocui.Gui) { + mx, my := g.MousePosition() + if !movingMsg && (mx != initialMouseX || my != initialMouseY) { + movingMsg = true + } + g.SetView("msg", mx-xOffset, my-yOffset, mx-xOffset+20, my-yOffset+2, 0) +} + +func msgDown(g *gocui.Gui, v *gocui.View) error { + initialMouseX, initialMouseY = g.MousePosition() + if vx, vy, _, _, err := g.ViewPosition("msg"); err == nil { + xOffset = initialMouseX - vx + yOffset = initialMouseY - vy + msgMouseDown = true + } + return nil +} + +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 down at: %d,%d", mx, my) + 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 +} + +func mouseUp(g *gocui.Gui, v *gocui.View) error { + if msgMouseDown { + msgMouseDown = false + if movingMsg { + movingMsg = false + return nil + } else { + g.DeleteView("msg") + } + } else if globalMouseDown { + globalMouseDown = false + g.DeleteView("globalDown") + } + return nil +} diff --git a/toolkit/gocui/Makefile b/toolkit/gocui/Makefile index 597b2b2..35e4e96 100644 --- a/toolkit/gocui/Makefile +++ b/toolkit/gocui/Makefile @@ -9,3 +9,7 @@ plugin: objdump: objdump -t ../gocui.so |less + +log: + reset + tail -f /tmp/witgui.* /tmp/guilogfile diff --git a/toolkit/gocui/button.go b/toolkit/gocui/button.go deleted file mode 100644 index 355dc30..0000000 --- a/toolkit/gocui/button.go +++ /dev/null @@ -1,53 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "strings" - - "github.com/awesome-gocui/gocui" - "git.wit.org/wit/gui/toolkit" -) - -func newButton(parentW *toolkit.Widget, w *toolkit.Widget) { - log(true, "AddButton()", w.Name) - addButton(w.Name) - stringWidget[w.Name] = w - listMap() -} - -func addButton(name string) *gocui.View { - t := len(name) - if (baseGui == nil) { - panic("WTF") - } - v, err := baseGui.SetView(name, currentX, currentY, currentX+t+3, currentY+2, 0) - if err == nil { - log("wit/gui internal plugin error", err) - return nil - } - if !errors.Is(err, gocui.ErrUnknownView) { - log("wit/gui internal plugin error", err) - return nil - } - - v.Wrap = true - fmt.Fprintln(v, " " + name) - fmt.Fprintln(v, strings.Repeat("foo\n", 2)) - - currentView, err := baseGui.SetCurrentView(name) - if err != nil { - log("wit/gui internal plugin error", err) - return nil - } - log("wit/gui addbutton() current view name =", currentView.Name()) - - views = append(views, name) - curView = len(views) - 1 - idxView += 1 - currentY += 3 - if (groupSize < len(name)) { - groupSize = len(name) - } - return currentView -} diff --git a/toolkit/gocui/click.go b/toolkit/gocui/click.go new file mode 100644 index 0000000..9dc1a42 --- /dev/null +++ b/toolkit/gocui/click.go @@ -0,0 +1,165 @@ +package main + +import ( + "fmt" + "errors" + "strconv" + "strings" + + "github.com/awesome-gocui/gocui" + "git.wit.org/wit/gui/toolkit" +) + +func (w *cuiWidget) doWidgetClick() { + switch w.widgetType { + case toolkit.Root: + me.rootNode.redoTabs(true) + // me.rootNode.redoFake(true) + case toolkit.Flag: + me.rootNode.redoColor(true) + case toolkit.Window: + me.rootNode.redoTabs(true) + w.redoBox(true) + w.toggleTree() + me.rootNode.redoColor(true) + case toolkit.Tab: + me.rootNode.redoTabs(true) + w.redoBox(true) + w.toggleTree() + me.rootNode.redoColor(true) + case toolkit.Box: + w.showWidgetPlacement(logNow, "drawTree()") + if (w.horizontal) { + log("BOX IS HORIZONTAL", w.nextW, w.nextH, w.name) + } else { + log("BOX IS VERTICAL", w.nextW, w.nextH, w.name) + } + // w.redoBox(true) + default: + // w.textResize() + // something + } +} + +var toggle bool = true +func (w *cuiWidget) toggleTree() { + if (toggle) { + w.drawTree(toggle) + toggle = false + } else { + w.hideWidgets() + toggle = true + } +} + + +// display the widgets in the binary tree +func (w *cuiWidget) drawTree(draw bool) { + if (w == nil) { + return + } + w.showWidgetPlacement(logNow, "drawTree()") + if (draw) { + w.textResize() + w.drawView() + } else { + me.baseGui.DeleteView(w.cuiName) + w.v = nil + } + + for _, child := range w.children { + child.drawTree(draw) + } +} + +func click(g *gocui.Gui, v *gocui.View) error { + var l string + var err error + + log(logNow, "click() START", v.Name()) + i, err := strconv.Atoi(v.Name()) + if (err != nil) { + log(logNow, "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(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() + 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") + return nil +} + +// display the widgets in the binary tree + +func ctrlDown(g *gocui.Gui, v *gocui.View) error { + var widgets []*cuiWidget + var f func (widget *cuiWidget) + w, h := g.MousePosition() + + f = func(widget *cuiWidget) { + if ((widget.logicalSize.w0 < w) && (w < widget.logicalSize.w1)) { + widgets = append(widgets, widget) + } + + for _, child := range widget.children { + f(child) + } + } + f(me.rootNode) + var t string + for i, widget := range widgets { + log(logNow, "ctrlDown() FOUND widget", i, widget.name) + t += widget.cuiName + " " + widget.name + "\n" + // widget.showWidgetPlacement(logNow, "drawTree()") + } + t = strings.TrimSpace(t) + if (me.ctrlDown == nil) { + setupCtrlDownWidget() + } + me.ctrlDown.text = t + me.ctrlDown.realSize.w0 = w + me.ctrlDown.realSize.h0 = h + me.ctrlDown.textResize() + me.ctrlDown.drawView() + + /* + v, err := g.SetView("ctrlDown", maxX/2-10, maxY/2, maxX/2+10, maxY/2+2, 0) + if (err != nil) { + log(logError, "ctrlDown() g.SetView() error:", err) + return + } + v.Clear() + v.SelBgColor = gocui.ColorCyan + v.SelFgColor = gocui.ColorBlack + fmt.Fprintln(v, l) + */ + + log(logNow, "ctrlDown()", w, h) + return nil +} diff --git a/toolkit/gocui/color.go b/toolkit/gocui/color.go new file mode 100644 index 0000000..efe44dd --- /dev/null +++ b/toolkit/gocui/color.go @@ -0,0 +1,113 @@ +package main + +import ( + "math/rand" + "github.com/awesome-gocui/gocui" + "git.wit.org/wit/gui/toolkit" +) + +// 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) + v, _ := me.baseGui.View(w.cuiName) + if (v == nil) { + log(logError, "SetDefaultWidgetColor() failed on view == nil") + return + } + // v.BgColor = gocui.GetColor("#FFAA55") // Dark Purple + // v.BgColor = gocui.GetColor("#88AA55") // heavy purple + // v.BgColor = gocui.GetColor("#111111") // crazy red + // v.BgColor = gocui.GetColor("#FF9911") // heavy red + // v.SelBgColor = gocui.GetColor("#FFEE11") // blood red + + // v.BgColor = gocui.GetColor("#55AAFF") // super light grey + // v.BgColor = gocui.GetColor("#FFC0CB") // 'w3c pink' yellow + switch w.widgetType { + case toolkit.Root: + v.FrameColor = gocui.ColorRed + v.BgColor = gocui.GetColor("#B0E0E6") // w3c 'powerder blue' + case toolkit.Flag: + v.FrameColor = gocui.ColorRed + v.BgColor = gocui.GetColor("#B0E0E6") // w3c 'powerder blue' + case toolkit.Window: + v.FgColor = gocui.ColorCyan + v.SelBgColor = gocui.ColorBlue + v.FrameColor = gocui.ColorBlue + case toolkit.Tab: + v.SelBgColor = gocui.ColorBlue + v.FrameColor = gocui.ColorBlue + case toolkit.Button: + v.BgColor = gocui.ColorWhite + v.FrameColor = gocui.ColorGreen + v.SelBgColor = gocui.ColorBlack + v.SelFgColor = gocui.ColorGreen + case toolkit.Label: + v.BgColor = gocui.GetColor("#55AAFF") // super light grey + v.SelBgColor = gocui.GetColor("#55AAFF") // super light grey + case toolkit.Box: + v.FrameColor = gocui.ColorRed + // v.BgColor = gocui.GetColor("#FFC0CB") // 'w3c pink' yellow + v.BgColor = gocui.GetColor("#DDDDDD") // light purple + case toolkit.Grid: + // v.FgColor = gocui.ColorCyan + // v.SelBgColor = gocui.ColorBlue + // v.FrameColor = gocui.ColorBlue + case toolkit.Group: + v.BgColor = gocui.GetColor("#55AAFF") // super light grey + default: + } +} + +// SetColor("#FFAA55") // purple +func (w *cuiWidget) SetColor(c string) { + if (w.v == nil) { + log(logError, "SetColor() failed on view == nil") + return + } + w.v.SelBgColor = gocui.ColorCyan + w.v.SelFgColor = gocui.ColorBlack + switch c { + case "Green": + w.v.BgColor = gocui.ColorGreen + case "Purple": + w.v.BgColor = gocui.GetColor("#FFAA55") + case "Yellow": + w.v.BgColor = gocui.ColorYellow + case "Blue": + w.v.BgColor = gocui.ColorBlue + case "Red": + w.v.BgColor = gocui.ColorRed + default: + w.v.BgColor = gocui.GetColor(c) + } +} + +func (w *cuiWidget) SetDefaultHighlight() { + if (w.v == nil) { + log(logError, "SetColor() failed on view == nil") + return + } +// w.v.SelBgColor = gocui.ColorGreen +// w.v.SelFgColor = gocui.ColorBlack +} + +func randColor() gocui.Attribute { + colors := []string{"Green", "#FFAA55", "Yellow", "Blue", "Red", "Black", "White"} + i := rand.Intn(len(colors)) + log("randColor() i =", i) + return gocui.GetColor(colors[i]) +} + +func (w *cuiWidget) redoColor(draw bool) { + if (w == nil) { + return + } + + sleep(.05) + w.SetDefaultWidgetColor() + + for _, child := range w.children { + child.redoColor(draw) + } +} diff --git a/toolkit/gocui/common.go b/toolkit/gocui/common.go new file mode 100644 index 0000000..2fe82cb --- /dev/null +++ b/toolkit/gocui/common.go @@ -0,0 +1,100 @@ +package main + +import ( + "strconv" + "git.wit.org/wit/gui/toolkit" +// "github.com/awesome-gocui/gocui" +) + +func setupWidget(a *toolkit.Action) *cuiWidget { + var w *cuiWidget + w = new(cuiWidget) + + 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 + w.width = a.Width + w.height = a.Height + + w.widgetType = a.WidgetType + w.id = a.WidgetId + // set the name used by gocui to the id + w.cuiName = strconv.Itoa(w.id) + + w.parent = findWidget(a.ParentId, me.rootNode) + log(logInfo, "setupWidget() w.id =", w.id, "w.parent", w.parent, "ParentId =", a.ParentId) + if (w.id == 0) { + me.rootNode = w + // this is the rootNode + return w + } + 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 + } + if (w.parent == nil) { + log(logError, "setupWidget() ERROR: PARENT = NIL w.id =", w.id, "w.parent", w.parent, "ParentId =", a.ParentId) + me.rootNode = w + 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 + } else { + w.horizontal = false + } + } + + // w.showWidgetPlacement(logNow) + return w +} + +func setupCtrlDownWidget() { + var w *cuiWidget + w = new(cuiWidget) + + w.name = "ctrlDown" + + w.widgetType = toolkit.Flag + w.id = -1 + me.ctrlDown = w + // me.rootNode.Append(w) +} + +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") + 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 + } + + 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 + } + } + return nil +} + diff --git a/toolkit/gocui/debug.go b/toolkit/gocui/debug.go index b38fd83..3ecdfdd 100644 --- a/toolkit/gocui/debug.go +++ b/toolkit/gocui/debug.go @@ -1,109 +1,70 @@ package main -import "git.wit.org/wit/gui/toolkit" +import ( + "fmt" -var defaultBehavior bool = true + "git.wit.org/wit/gui/toolkit" +// "github.com/awesome-gocui/gocui" +) -var bookshelf bool // do you want things arranged in the box like a bookshelf or a stack? -var canvas bool // if set to true, the windows are a raw canvas -var menubar bool // for windows -var stretchy bool // expand things like buttons to the maximum size -var padded bool // add space between things like buttons -var margin bool // add space around the frames of windows - -var debugToolkit bool -var debugChange bool -var debugPlugin bool -var debugFlags bool -var debugError bool = true - -// This is important. This sets the defaults for the gui. Without this, there isn't correct padding, etc -func setDefaultBehavior(s bool) { - defaultBehavior = s - if (defaultBehavior) { - log(debugToolkit, "Setting this toolkit to use the default behavior.") - log(debugToolkit, "This is the 'guessing' part as defined by the wit/gui 'Principles'. Refer to the docs.") - stretchy = false - padded = true - menubar = true - margin = true - canvas = false - bookshelf = true // 99% of the time, things make a vertical stack of objects - } else { - log(debugToolkit, "This toolkit is set to ignore the default behavior.") +func actionDump(b bool, a *toolkit.Action) { + if (a == nil) { + log(b, "action = nil") + return } -} -func ShowDebug () { - log(true, "debugToolkit =", debugToolkit) - log(true, "debugChange =", debugChange) - log(true, "debugPlugin =", debugPlugin) - log(true, "debugFlags =", debugFlags) - log(true, "debugError =", debugError) + log(b, "a.Name =", a.Name) + log(b, "a.Text =", a.Text) + log(b, "a.WidgetId =", a.WidgetId) + log(b, "a.ParentId =", a.ParentId) + log(b, "a.B =", a.B) + log(b, "a.S =", a.S) } -/* -func (t *gocuiT) Dump(b bool) { - if ! b { +func (w *cuiWidget) dumpTree(draw bool) { + log(logNow, "dumpTree() START", w) + if (w == nil) { return } - log(b, "Name = ", t.Name, t.Width, t.Height) - if (t.uiBox != nil) { - log(b, "uiBox =", t.uiBox) - } - if (t.uiButton != nil) { - log(b, "uiButton =", t.uiButton) - } - if (t.uiCombobox != nil) { - log(b, "uiCombobox =", t.uiCombobox) - } - if (t.uiWindow != nil) { - log(b, "uiWindow =", t.uiWindow) - } - if (t.uiTab != nil) { - log(b, "uiTab =", t.uiTab) - } - if (t.uiGroup != nil) { - log(b, "uiGroup =", t.uiGroup) - } - if (t.uiEntry != nil) { - log(b, "uiEntry =", t.uiEntry) - } - if (t.uiMultilineEntry != nil) { - log(b, "uiMultilineEntry =", t.uiMultilineEntry) - } - if (t.uiSlider != nil) { - log(b, "uiSlider =", t.uiSlider) - } - if (t.uiCheckbox != nil) { - log(b, "uiCheckbox =", t.uiCheckbox) + w.showWidgetPlacement(logNow, "Tree:") + + for _, child := range w.children { + child.dumpTree(draw) } - widgetDump(b, t.tw) } -*/ -func widgetDump(b bool, w *toolkit.Widget) { +func (w *cuiWidget) showWidgetPlacement(b bool, s string) { if (w == nil) { - log(b, "widget = nil") + log(logError, "WTF w == nil") return } + if (w.id == 0) { + log(logVerbose, "showWidgetPlacement() parent == nil ok. This is the rootNode", w.id, w.cuiName) + return + } + if (w.parent == nil) { + log(logError, "showWidgetPlacement() WTF parent == nil", w.id, w.cuiName) + log(logError, "showWidgetPlacement() WTF parent == nil", w.id, w.cuiName) + log(logError, "showWidgetPlacement() WTF parent == nil", w.id, w.cuiName) + return + } + log(b, "dump()", s, + fmt.Sprintf("(wId,pId)=(%3d,%3d)", w.id, w.parent.id), + fmt.Sprintf("real()=(%3d,%3d,%3d,%3d)", w.realSize.w0, w.realSize.h0, w.realSize.w1, w.realSize.h1), + "next()=(", w.nextW, ",", w.nextH, ")", + "logical()=(", w.logicalSize.w0, ",", w.logicalSize.h0, ",", w.logicalSize.w1, ",", w.logicalSize.h1, ")", + w.widgetType, ",", w.name, "text=", w.text) - /* - log(b, "widget.Name =", w.Name) - // log(b, "widget.Action =", w.Action) - log(b, "widget.Type =", w.Type) - log(b, "widget.Custom =", w.Custom) - log(b, "widget.B =", w.B) - log(b, "widget.I =", w.I) - log(b, "widget.Width =", w.Width) - log(b, "widget.Height =", w.Height) - log(b, "widget.X =", w.X) - log(b, "widget.Y =", w.Y) - */ -} - -/* -func GetDebugToolkit () bool { - return debugToolkit + if (w.realWidth != (w.realSize.w1 - w.realSize.w0)) { + log(b, "dump()", s, + "badsize()=(", w.realWidth, ",", w.realHeight, ")", + "badreal()=(", w.realSize.w0, ",", w.realSize.h0, ",", w.realSize.w1, ",", w.realSize.h1, ")", + w.widgetType, ",", w.name) + } + if (w.realHeight != (w.realSize.h1 - w.realSize.h0)) { + log(b, "dump()", s, + "badsize()=(", w.realWidth, ",", w.realHeight, ")", + "badreal()=(", w.realSize.w0, ",", w.realSize.h0, ",", w.realSize.w1, ",", w.realSize.h1, ")", + w.widgetType, ",", w.name) + } } -*/ diff --git a/toolkit/gocui/gocui.go b/toolkit/gocui/gocui.go deleted file mode 100644 index 0a302bc..0000000 --- a/toolkit/gocui/gocui.go +++ /dev/null @@ -1,112 +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" - "os" - - "git.wit.org/wit/gui/toolkit" - - "github.com/awesome-gocui/gocui" -) - -const delta = 1 - -var ( - views = []string{} - curView = -1 - idxView = 0 - currentX = 5 - currentY = 2 - groupSize = 0 - baseGui *gocui.Gui - helpLabel *gocui.View - err error - ch chan(func ()) - outf *os.File -) - -func Init() { - baseGui, err = gocui.NewGui(gocui.OutputNormal, true) - if err != nil { - exit(err) - } - - baseGui.Highlight = true - baseGui.SelFgColor = gocui.ColorRed - baseGui.SelFrameColor = gocui.ColorRed - - baseGui.Cursor = true - baseGui.Mouse = true - - baseGui.SetManagerFunc(layout) - - if err := initKeybindings(baseGui); err != nil { - exit(err) - } - - viewWidget = make(map[*gocui.View]*toolkit.Widget) - stringWidget = make(map[string]*toolkit.Widget) - - ch = make(chan func()) - - outf, err = os.OpenFile("/tmp/witgui.log", os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666) - if err != nil { - exit("error opening file: %v", err) - } - // hmm. where to put this? - // defer outf.Close() - - setOutput(outf) - log("This is a test log entry") -} - -func Queue(f func()) { - log("QUEUEEEEE") - f() -} - -func Main(f func()) { - // close the STDOUT log file - defer outf.Close() - if (baseGui == nil) { - panic("WTF Main()") - } - defer baseGui.Close() - // log.Println("ADDDDDDDD BUTTTTTTTTTON") - // addButton("test 3") - f() - if err := baseGui.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) { - exit(err) - } - baseGui.Close() - os.Exit(0) -} - -func layout(g *gocui.Gui) error { - var err error - maxX, _ := g.Size() - - helpLabel, err = g.SetView("help", maxX-32, 0, maxX-1, 12, 0) - if err != nil { - if !errors.Is(err, gocui.ErrUnknownView) { - return err - } - fmt.Fprintln(helpLabel, "KEYBINDINGS") - fmt.Fprintln(helpLabel, "Enter: Click Button") - fmt.Fprintln(helpLabel, "Tab/Space: Switch Buttons") - fmt.Fprintln(helpLabel, "") - fmt.Fprintln(helpLabel, "h: Help") - fmt.Fprintln(helpLabel, "Backspace: Delete Button") - fmt.Fprintln(helpLabel, "Arrow keys: Move Button") - fmt.Fprintln(helpLabel, "t: Move Button to the top") - fmt.Fprintln(helpLabel, "b: Move Button to the button") - fmt.Fprintln(helpLabel, "STDOUT: /tmp/witgui.log") - fmt.Fprintln(helpLabel, "Ctrl-C or Q: Exit") - } - return nil -} diff --git a/toolkit/gocui/group.go b/toolkit/gocui/group.go deleted file mode 100644 index 2d8600c..0000000 --- a/toolkit/gocui/group.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import ( - "git.wit.org/wit/gui/toolkit" -) - -func newGroup(parentW *toolkit.Widget, w *toolkit.Widget) { - if (parentW == nil) { - log(debugError, "plugin error. parent widget == nil") - return - } - if (w == nil) { - log(debugPlugin, "plugin error. widget == nil") - return - } - if (w.Name == "") { - w.Name = parentW.Name - } - if (w.Name == "") { - w.Name = "nil newGroup" - } - log("AddGroup", w.Name) - addGroup(w.Name) - stringWidget[w.Name] = w -} - -func addGroup(name string) { - log("addGroup() START name =", name) - log("addGroup() START groupSize =", groupSize, "currentY =", currentY, "currentX =", currentX) - - currentY = 2 - currentX += groupSize + 5 - groupSize = 0 - - log("addGroup() START, RESET Y = 3, RESET X = ", currentX) -} diff --git a/toolkit/gocui/help.go b/toolkit/gocui/help.go new file mode 100644 index 0000000..8796ad6 --- /dev/null +++ b/toolkit/gocui/help.go @@ -0,0 +1,49 @@ +// 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 addHelp() { + me.baseGui.SetManagerFunc(helplayout) +} + +func helplayout(g *gocui.Gui) error { + var err error + maxX, _ := g.Size() + + help, err := g.SetView("help", maxX-32, 0, maxX-1, 17, 0) + if err != nil { + if !errors.Is(err, gocui.ErrUnknownView) { + return err + } + help.SelBgColor = gocui.ColorGreen + help.SelFgColor = gocui.ColorBlack + fmt.Fprintln(help, "KEYBINDINGS") + fmt.Fprintln(help, "Enter: Click Button") + fmt.Fprintln(help, "Tab/Space: Switch Buttons") + fmt.Fprintln(help, "") + fmt.Fprintln(help, "h: Help") + fmt.Fprintln(help, "Backspace: Delete Button") + fmt.Fprintln(help, "Arrow keys: Move Button") + fmt.Fprintln(help, "t: Move Button to the top") + fmt.Fprintln(help, "b: Move Button to the button") + fmt.Fprintln(help, "h: hide buttons") + fmt.Fprintln(help, "s: show buttons") + fmt.Fprintln(help, "p: panic()") + fmt.Fprintln(help, "STDOUT: /tmp/witgui.log") + fmt.Fprintln(help, "Ctrl-C or Q: Exit") + if _, err := g.SetCurrentView("help"); err != nil { + return err + } + } + me.helpLabel = help + return nil +} diff --git a/toolkit/gocui/keybindings.go b/toolkit/gocui/keybindings.go index 43dbf47..18d3097 100644 --- a/toolkit/gocui/keybindings.go +++ b/toolkit/gocui/keybindings.go @@ -6,130 +6,66 @@ package main import ( "github.com/awesome-gocui/gocui" - "git.wit.org/wit/gui/toolkit" ) -func initKeybindings(g *gocui.Gui) error { - log("got to initKeybindings") - if err := g.SetKeybinding("", 'q', gocui.ModNone, - func(g *gocui.Gui, v *gocui.View) error { - return gocui.ErrQuit - }); err != nil { - return err - } - if err := g.SetKeybinding("", 'Q', gocui.ModNone, - func(g *gocui.Gui, v *gocui.View) error { - return gocui.ErrQuit - }); err != nil { - return err - } - if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, - func(g *gocui.Gui, v *gocui.View) error { - return gocui.ErrQuit - }); err != nil { - return err - } - if err := g.SetKeybinding("", gocui.KeySpace, gocui.ModNone, - func(g *gocui.Gui, v *gocui.View) error { - return newView(g) - }); err != nil { +func defaultKeybindings(g *gocui.Gui) error { + if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { return err } - if err := g.SetKeybinding("", gocui.KeyBackspace, gocui.ModNone, - func(g *gocui.Gui, v *gocui.View) error { - return delView(g) - }); err != nil { - return err - } - if err := g.SetKeybinding("", gocui.KeyBackspace2, gocui.ModNone, - func(g *gocui.Gui, v *gocui.View) error { - return delView(g) - }); err != nil { - return err + for _, n := range []string{"but1", "but2", "help", "but3"} { + if err := g.SetKeybinding(n, gocui.MouseLeft, gocui.ModNone, showMsg); err != nil { + return err + } } - if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, - func(g *gocui.Gui, v *gocui.View) error { - log("tab", v.Name()) - return nextView(g, true) - }); err != nil { + if err := g.SetKeybinding("", gocui.MouseRelease, gocui.ModNone, mouseUp); err != nil { return err } - if err := g.SetKeybinding("", gocui.KeyArrowLeft, gocui.ModNone, - func(g *gocui.Gui, v *gocui.View) error { - return moveView(g, v, -delta, 0) - }); err != nil { + if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, globalDown); err != nil { return err } - if err := g.SetKeybinding("", gocui.KeyArrowRight, gocui.ModNone, - func(g *gocui.Gui, v *gocui.View) error { - return moveView(g, v, delta, 0) - }); err != nil { + if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModMouseCtrl, ctrlDown); err != nil { return err } - if err := g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, - func(g *gocui.Gui, v *gocui.View) error { - log("down", v.Name()) - return moveView(g, v, 0, delta) - }); err != nil { + /* + if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, globalDown); err != nil { return err } - if err := g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone, - func(g *gocui.Gui, v *gocui.View) error { - log("up", v.Name()) - return moveView(g, v, 0, -delta) - }); err != nil { + */ + if err := g.SetKeybinding("msg", gocui.MouseLeft, gocui.ModNone, msgDown); err != nil { return err } - if err := g.SetKeybinding("", gocui.KeyEnter, gocui.ModNone, + addDebugKeys(g) + return nil +} + +// dump out the widgets +func addDebugKeys(g *gocui.Gui) { + // dump all widget info to the log + g.SetKeybinding("", 'd', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { - log("enter", v.Name()) - var w *toolkit.Widget - w = stringWidget[v.Name()] - if (w == nil) { - log("COULD NOT FIND WIDGET", v.Name()) - } else { - log("FOUND WIDGET!", w) - if (w.Custom != nil) { - w.Custom() - return nil - } - // if (w.Event != nil) { - // w.Event(w) - // return nil - // } - } + log(logNow, "gocui.SetKeyBinding() dumpTree() START") + me.rootNode.dumpTree(true) return nil - }); err != nil { - return err - } - if err := g.SetKeybinding("", 't', gocui.ModNone, + }) + + // hide all widgets + g.SetKeybinding("", 'h', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { - _, err := g.SetViewOnTop(views[curView]) - return err - }); err != nil { - return err - } - if err := g.SetKeybinding("", 'b', gocui.ModNone, + me.rootNode.hideWidgets() + return nil + }) + + // show all widgets + g.SetKeybinding("", 's', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { - _, err := g.SetViewOnBottom(views[curView]) - return err - }); err != nil { - return err - } - if err := g.SetKeybinding("", 'h', gocui.ModNone, + me.rootNode.showWidgets() + return nil + }) + + // panic + g.SetKeybinding("", 'p', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { - log("help", v.Name()) - tmp, _ := g.SetViewOnTop("help") - log("help 2", tmp.Name()) -// g.SetView("help", 2, 2, 30, 15, 0); - g.SetCurrentView("help") -// moveView(g, tmp, 0, -delta) - if err := g.DeleteView("help"); err != nil { - exit("gocui SetKeybinding()", err) - } + panic("forced panic in gocui") return nil - }); err != nil { - return err - } - return nil + }) } diff --git a/toolkit/gocui/log.go b/toolkit/gocui/log.go index 527d057..222332d 100644 --- a/toolkit/gocui/log.go +++ b/toolkit/gocui/log.go @@ -5,7 +5,15 @@ import ( witlog "git.wit.org/wit/gui/log" ) +// various debugging flags +var logNow bool = true // useful for active development +var logError bool = true +var logWarn bool = false +var logInfo bool = false +var logVerbose bool = false + func log(a ...any) { + witlog.Where = "wit/gocui" witlog.Log(a...) } diff --git a/toolkit/gocui/logical.go b/toolkit/gocui/logical.go new file mode 100644 index 0000000..2c6081b --- /dev/null +++ b/toolkit/gocui/logical.go @@ -0,0 +1,72 @@ +package main + +import ( + // "git.wit.org/wit/gui/toolkit" +) + +var adjusted bool = false + +// expands the logical size of the parents +func (w *cuiWidget) setParentLogical(p *cuiWidget) { + if (w.visable) { + // expand the parent logicalsize to include the widget realSize + if (p.logicalSize.w0 > w.realSize.w0) { + p.logicalSize.w0 = w.realSize.w0 + adjusted = true + } + if (p.logicalSize.h0 > w.realSize.h0) { + p.logicalSize.h0 = w.realSize.h0 + adjusted = true + } + if (p.logicalSize.w1 < w.realSize.w1) { + p.logicalSize.w1 = w.realSize.w1 + adjusted = true + } + if (p.logicalSize.h1 < w.realSize.h1) { + p.logicalSize.h1 = w.realSize.h1 + adjusted = true + } + } else { + // expand the parent logicalsize to include the widget logicalsize + if (p.logicalSize.w0 > w.logicalSize.w0) { + p.logicalSize.w0 = w.logicalSize.w0 + adjusted = true + } + if (p.logicalSize.h0 > w.logicalSize.h0) { + p.logicalSize.h0 = w.logicalSize.h0 + adjusted = true + } + if (p.logicalSize.w1 < w.logicalSize.w1) { + p.logicalSize.w1 = w.logicalSize.w1 + adjusted = true + } + if (p.logicalSize.h1 < w.logicalSize.h1) { + p.logicalSize.h1 = w.logicalSize.h1 + adjusted = true + } + } + if (w.visable) { + // adjust the widget realSize to the top left corner of the logicalsize + if (w.logicalSize.w0 > w.realSize.w0) { + w.realSize.w0 = w.logicalSize.w0 + w.realSize.w1 = w.realSize.w0 + w.realWidth + adjusted = true + } + if (w.logicalSize.h0 > w.realSize.h0) { + w.realSize.h0 = w.logicalSize.h0 + w.realSize.h1 = w.realSize.h0 + w.realHeight + adjusted = true + } + } + w.showWidgetPlacement(logNow, "setParentLogical() widget") + p.showWidgetPlacement(logNow, "setParentLogical() parent") + if (w.id == 0) || (p.id == 0) { + // stop resizing when you hit the root widget + return + } + // pass the logical resizing up + pP := w.parent + if (pP != nil) { + p.setParentLogical(pP) + } +} diff --git a/toolkit/gocui/main.go b/toolkit/gocui/main.go new file mode 100644 index 0000000..11df910 --- /dev/null +++ b/toolkit/gocui/main.go @@ -0,0 +1,40 @@ +// 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 ( + "os" +) + +func Init() { + log(logInfo, "Init() of awesome-gocui") + me.defaultWidth = 10 + me.defaultHeight = 2 + me.defaultBehavior = true + + me.horizontalPadding = 20 + me.groupPadding = 2 + me.buttonPadding = 2 +} + +func Exit() { + // TODO: exit correctly + me.baseGui.Close() +} + +func Main(f func()) { + log("start Init()") + + outf, err := os.OpenFile("/tmp/witgui.log", os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666) + if err != nil { + exit("error opening file: %v", err) + } + defer outf.Close() + + setOutput(outf) + log("This is a test log entry") + + MouseMain() +} diff --git a/toolkit/gocui/mouse.go b/toolkit/gocui/mouse.go new file mode 100644 index 0000000..196e080 --- /dev/null +++ b/toolkit/gocui/mouse.go @@ -0,0 +1,171 @@ +// 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 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() + if _, err := g.View("msg"); msgMouseDown && err == nil { + moveMsg(g) + } + if v, err := g.SetView("global", -1, -1, maxX, maxY, 0); err != nil { + if !errors.Is(err, gocui.ErrUnknownView) { + return err + } + v.Frame = false + } + /* + if v, err := g.SetView("but1", 2, 2, 22, 7, 0); err != nil { + if !errors.Is(err, gocui.ErrUnknownView) { + return err + } + v.SelBgColor = gocui.ColorGreen + v.SelFgColor = gocui.ColorBlack + fmt.Fprintln(v, "Button 1 - line 1") + fmt.Fprintln(v, "Button 1 - line 2") + fmt.Fprintln(v, "Button 1 - line 3") + fmt.Fprintln(v, "Button 1 - line 4") + if _, err := g.SetCurrentView("but1"); err != nil { + return err + } + } + if v, err := g.SetView("but2", 24, 2, 44, 4, 0); err != nil { + if !errors.Is(err, gocui.ErrUnknownView) { + return err + } + v.SelBgColor = gocui.ColorGreen + v.SelFgColor = gocui.ColorBlack + fmt.Fprintln(v, "Button 2 - line 1") + } + */ + helplayout(g) + updateHighlightedView(g) + return nil +} + +func quit(g *gocui.Gui, v *gocui.View) error { + return gocui.ErrQuit +} + +func showMsg(g *gocui.Gui, v *gocui.View) error { + var l string + var err error + + 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) + } + return nil +} + +func updateHighlightedView(g *gocui.Gui) { + mx, my := g.MousePosition() + for _, view := range g.Views() { + view.Highlight = false + } + if v, err := g.ViewByPosition(mx, my); err == nil { + v.Highlight = true + } +} + +func moveMsg(g *gocui.Gui) { + mx, my := g.MousePosition() + if !movingMsg && (mx != initialMouseX || my != initialMouseY) { + movingMsg = true + } + g.SetView("msg", mx-xOffset, my-yOffset, mx-xOffset+20, my-yOffset+2, 0) +} + +func msgDown(g *gocui.Gui, v *gocui.View) error { + initialMouseX, initialMouseY = g.MousePosition() + if vx, vy, _, _, err := g.ViewPosition("msg"); err == nil { + xOffset = initialMouseX - vx + yOffset = initialMouseY - vy + msgMouseDown = true + } + return nil +} + +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 down at: %d,%d", mx, my) + 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 +} + +func mouseUp(g *gocui.Gui, v *gocui.View) error { + if msgMouseDown { + msgMouseDown = false + if movingMsg { + movingMsg = false + return nil + } else { + g.DeleteView("msg") + } + } else if globalMouseDown { + globalMouseDown = false + g.DeleteView("globalDown") + } + return nil +} diff --git a/toolkit/gocui/place.go b/toolkit/gocui/place.go new file mode 100644 index 0000000..905ebed --- /dev/null +++ b/toolkit/gocui/place.go @@ -0,0 +1,221 @@ +package main + +import ( +// "github.com/awesome-gocui/gocui" + "git.wit.org/wit/gui/toolkit" +) + +var fakeStartWidth int = 80 +var fakeStartHeight int = 0 +func (w *cuiWidget) setFake() { + if (w.visable) { + return + } + t := len(w.name) + // setup fake labels for non-visable things off screen + w.realWidth = t + 2 + w.realHeight = me.defaultHeight + w.realSize.w0 = fakeStartWidth + w.realSize.h0 = fakeStartHeight + w.realSize.w1 = w.realSize.w0 + w.realWidth + w.realSize.h1 = w.realSize.h0 + w.realHeight + fakeStartHeight += 3 + if (fakeStartHeight > 24) { + fakeStartHeight = 0 + fakeStartWidth += 20 + } + w.showWidgetPlacement(logNow, "setFake()") +} + +func findPlace(w *cuiWidget) { + w.visable = true + switch w.widgetType { + case toolkit.Root: + w.visable = false + w.setFake() + case toolkit.Flag: + w.visable = false + w.setFake() + case toolkit.Grid: + w.visable = false + w.setFake() + case toolkit.Box: + w.visable = false + w.setFake() + default: + w.redoBox(true) + } +} + +func (w *cuiWidget) redoBox(draw bool) { + if (w == nil) { + return + } + if (me.rootNode == nil) { + return + } + p := w.parent + if (p == nil) { + log(logInfo, "redoBox()", w.id, "parent == nil") + return + } + + t := len(w.text) + w.visable = true + switch w.widgetType { + case toolkit.Window: + for _, child := range w.children { + child.redoBox(draw) + } + case toolkit.Tab: + for _, child := range w.children { + child.redoBox(draw) + } + case toolkit.Grid: + // hmm + w.logicalSize.w0 = p.nextW + w.logicalSize.h0 = p.nextH + w.logicalSize.w1 = p.nextW + w.logicalSize.h1 = p.nextH + + w.nextW = p.nextW + w.nextH = p.nextH + + var wCount, hCount int + var b bool = true + for _, child := range w.children { + child.redoBox(draw) + if (b) { + wCount += 1 + b = false + } else { + wCount = 0 + w.nextH += 1 + b = true + } + w.nextW = p.nextW + wCount * 20 + w.nextH = p.nextH + hCount * 4 + log(logInfo, "redoBox(GRID) (w,h count)", wCount, hCount, "(X,Y)", w.x, w.y, w.name) + } + w.showWidgetPlacement(logNow, "grid:") + case toolkit.Box: + w.logicalSize.w0 = p.nextW + w.logicalSize.h0 = p.nextH + w.logicalSize.w1 = p.nextW + w.logicalSize.h1 = p.nextH + + w.nextW = p.nextW + w.nextH = p.nextH + for _, child := range w.children { + child.redoBox(draw) + if (w.horizontal) { + log("BOX IS HORIZONTAL", p.nextW, p.nextW, p.name) + log("BOX IS HORIZONTAL", w.nextW, w.nextH, w.name) + log("BOX IS HORIZONTAL") + // expand based on the child width + w.nextW = child.nextW + me.horizontalPadding + // reset height to parent + w.nextH = p.nextH + } else { + log("BOX IS VERTICAL", p.nextW, p.nextW, p.name) + log("BOX IS VERTICAL", w.nextW, w.nextH, w.name) + log("BOX IS VERTICAL") + // go straight down + w.nextW = p.nextW + // expand based on the child height + w.nextH = child.nextH + } + } + w.showWidgetPlacement(logNow, "box:") + case toolkit.Group: + w.realWidth = t + me.buttonPadding + w.realHeight = me.defaultHeight + + w.realSize.w0 = p.nextW + w.realSize.h0 = p.nextH + w.realSize.w1 = w.realSize.w0 + w.realWidth + w.realSize.h1 = w.realHeight + + w.logicalSize.w0 = w.realSize.w0 + w.logicalSize.h0 = w.realSize.h0 + w.logicalSize.w1 = w.realSize.w1 + w.logicalSize.h1 = w.realSize.h1 + + w.nextW = p.nextW + me.groupPadding + w.nextH = p.nextH + me.buttonPadding + for _, child := range w.children { + child.redoBox(draw) + // reset nextW to straight down + w.nextW = p.nextW + 4 + w.nextH = child.nextH + } + // expand the height of the parent now that the group is done + // p.nextW = w.nextW + // p.nextH = w.nextH + w.showWidgetPlacement(logNow, "group:") + default: + w.realWidth = t + 3 + w.realHeight = me.defaultHeight + w.realSize.w0 = p.nextW + w.realSize.h0 = p.nextH + w.realSize.w1 = p.nextW + w.realWidth + w.realSize.h1 = p.nextH + w.realHeight + + w.logicalSize.w0 = p.nextW + w.logicalSize.h0 = p.nextH + w.logicalSize.w1 = p.nextW + w.realWidth + w.logicalSize.h1 = p.nextH + w.realHeight + + w.nextW = w.realSize.w1 + w.nextH = w.realSize.h1 + } +} + +func (w *cuiWidget) boxedPlace() { + t := len(w.name) + if (w.id == 0) { + w.realWidth = 0 + w.realHeight = 0 + return + } + p := w.parent + if (p == nil) { + log(logError, "boxedPlace() parentId widget == nil") + return + } + + w.realWidth = t + 3 + w.realHeight = me.defaultHeight + w.realSize.w0 = p.nextW + w.realSize.h0 = p.nextH + w.realSize.w1 = p.nextW + w.realWidth + w.realSize.h1 = p.nextH + w.realHeight + + w.logicalSize.w0 = p.nextW + w.logicalSize.h0 = p.nextH + w.logicalSize.w1 = p.nextW + w.realWidth + w.logicalSize.h1 = p.nextH + w.realHeight + + w.nextW = w.realSize.w1 + w.nextH = w.realSize.h1 + + w.showWidgetPlacement(logNow, "bP widget") +} + +func (w *cuiWidget) updateLogicalSizes() { + for _, child := range w.children { + child.updateLogicalSizes() + if (w.logicalSize.w0 > child.logicalSize.w0) { + w.logicalSize.w0 = child.logicalSize.w0 + } + if (w.logicalSize.w1 < child.logicalSize.w1) { + w.logicalSize.w1 = child.logicalSize.w1 + } + if (w.logicalSize.h0 > child.logicalSize.h0) { + w.logicalSize.h0 = child.logicalSize.h0 + } + if (w.logicalSize.h1 < child.logicalSize.h1) { + w.logicalSize.h1 = child.logicalSize.h1 + } + } +} diff --git a/toolkit/gocui/plugin.go b/toolkit/gocui/plugin.go index f0f45d3..7c4eee5 100644 --- a/toolkit/gocui/plugin.go +++ b/toolkit/gocui/plugin.go @@ -4,114 +4,77 @@ import ( // if you include more than just this import // then your plugin might be doing something un-ideal (just a guess from 2023/02/27) "git.wit.org/wit/gui/toolkit" - - "github.com/awesome-gocui/gocui" ) -// This is a map between the widgets in wit/gui and the internal structures of gocui -var viewWidget map[*gocui.View]*toolkit.Widget -var stringWidget map[string]*toolkit.Widget - func Quit() { - baseGui.Close() + me.baseGui.Close() } -// This lists out the know mappings -func listMap() { - for v, w := range viewWidget { - log("view =", v.Name, "widget name =", w.Name) - } - for s, w := range stringWidget { - log("string =", s, "widget =", w) +func Action(a *toolkit.Action) { + log(logInfo, "Action() START", a.WidgetId, a.ActionType, a.WidgetType, a.Name) + w := findWidget(a.WidgetId, me.rootNode) + switch a.ActionType { + case toolkit.Add: + w = setupWidget(a) + findPlace(w) + w.drawView() + case toolkit.Show: + if (a.B) { + w.drawView() + } else { + w.hideWidgets() + } + case toolkit.Set: + w.Set(a.A) + case toolkit.SetText: + w.SetText(a.S) + case toolkit.AddText: + w.AddText(a.S) + case toolkit.Move: + log(logNow, "attempt to move() =", a.ActionType, a.WidgetType, a.Name) + default: + log(logError, "Action() Unknown =", a.ActionType, a.WidgetType, a.Name) } + log(logInfo, "Action() END") } - -// -// This should be called ? -// Pass() ? -// This handles all interaction between the wit/gui package (what golang knows about) -// and this plugin that talks to the OS and does scary and crazy things to make -// a GUI on whatever OS or whatever GUI toolkit you might have (GTK, QT, WASM, libcurses) -// -// Once you are here, you should be in a protected goroutine created by the golang wit/gui package -// -// TODO: make sure you can't escape this goroutine -// -func Send(p *toolkit.Widget, c *toolkit.Widget) { - if (p == nil) { - log(debugPlugin, "Send() parent = nil") - } else { - log(debugPlugin, "Send() parent =", p.Name, ",", p.Type) +func (w *cuiWidget) AddText(text string) { + if (w == 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) } - log(debugPlugin, "Send() child =", c.Name, ",", c.Type) + w.SetText(text) +} - /* - if (c.Action == "SetMargin") { - log(debugError, "need to implement SetMargin here") - setMargin(c, c.B) +func (w *cuiWidget) SetText(text string) { + if (w == nil) { + log(logNow, "widget is nil") return } - */ + w.text = text + w.s = text + w.textResize() + me.baseGui.DeleteView(w.cuiName) + w.drawView() +} - switch c.Type { - /* - case toolkit.Window: - // doWindow(c) - case toolkit.Tab: - // doTab(p, c) - */ - case toolkit.Group: - newGroup(p, c) - case toolkit.Button: - newButton(p, c) - /* - case toolkit.Checkbox: - // doCheckbox(p, c) - case toolkit.Label: - // doLabel(p, c) - case toolkit.Textbox: - // doTextbox(p, c) - case toolkit.Slider: - // doSlider(p, c) - case toolkit.Spinner: - // doSpinner(p, c) - case toolkit.Dropdown: - // doDropdown(p, c) - case toolkit.Combobox: - // doCombobox(p, c) - case toolkit.Grid: - // doGrid(p, c) - */ - /* - case toolkit.Flag: - // log(debugFlags, "plugin Send() flag parent =", p.Name, p.Type) - // log(debugFlags, "plugin Send() flag child =", c.Name, c.Type) - // log(debugFlags, "plugin Send() flag child.Action =", c.Action) - // log(debugFlags, "plugin Send() flag child.S =", c.S) - // log(debugFlags, "plugin Send() flag child.B =", c.B) - // log(debugFlags, "plugin Send() what to flag?") - // should set the checkbox to this value - switch c.S { - case "Toolkit": - debugToolkit = c.B - case "Change": - debugChange = c.B - case "Plugin": - debugPlugin = c.B - case "Flags": - debugFlags = c.B - case "Error": - debugError = c.B - case "Show": - ShowDebug() - default: - log(debugError, "Can't set unknown flag", c.S) - } - */ +func (w *cuiWidget) Set(val any) { + log(logInfo, "Set() value =", val) + var a toolkit.Action + a.ActionType = toolkit.Set + + switch v := val.(type) { + case bool: + w.b = val.(bool) + case string: + w.SetText(val.(string)) + case int: + w.i = val.(int) default: - log(debugError, "plugin Send() unknown parent =", p.Name, p.Type) - log(debugError, "plugin Send() unknown child =", c.Name, c.Type) - log(debugError, "plugin Send() Don't know how to do", c.Type, "yet") + log(logError, "Set() unknown type =", v, "a =", a) } } diff --git a/toolkit/gocui/structs.go b/toolkit/gocui/structs.go new file mode 100644 index 0000000..a3f0207 --- /dev/null +++ b/toolkit/gocui/structs.go @@ -0,0 +1,142 @@ +// LICENSE: same as the go language itself +// Copyright 2023 WIT.COM + +// all structures and variables are local (aka lowercase) +// since the plugin should be isolated to access only +// by functions() to insure everything here is run +// inside a dedicated goroutine + +package main + +import ( + "fmt" + "sync" + "github.com/awesome-gocui/gocui" + "git.wit.org/wit/gui/toolkit" +) + +// const delta = 1 + +// It's probably a terrible idea to call this 'me' +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 + + callback func(int) + helpLabel *gocui.View + + defaultBehavior bool + defaultWidth int + defaultHeight int + nextW int // where the next window or tab flag should go + + 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 + + horizontalPadding int + groupPadding int + buttonPadding int +} + +/* +// This is a map between the widgets in wit/gui and the internal structures of gocui +var viewWidget map[*gocui.View]*toolkit.Widget +var stringWidget map[string]*toolkit.Widget +*/ + +var ( +// g *gocui.Gui +// Custom func(string) + + initialMouseX, initialMouseY, xOffset, yOffset int + globalMouseDown, msgMouseDown, movingMsg bool + +// err error +) + +// the gocui way +// the logical size of the widget +// corner starts at in the upper left corner +type rectType struct { + // this is the gocui way + w0, h0, w1, h1 int // left top right bottom +} + +/* +type realSizeT struct { + width, 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 + cuiName string // what gocui uses to reference the widget + + vals []string // dropdown menu options + + visable bool // widget types like 'box' are 'false' + realWidth int // the real width + realHeight int // the real height + realSize rectType // the display size of this widget + logicalSize rectType // the logical size. Includes all the child widgets + + nextW int + nextH int + + // things from toolkit/action + b bool + i int + s string + x int + y int + width int + height int + + //deprecate +// nextX int +// nextY 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 + + // writeMutex protects locks the write process + writeMutex sync.Mutex + + parent *cuiWidget + children []*cuiWidget +} + +// from the gocui devs: +// Write appends a byte slice into the view's internal buffer. Because +// View implements the io.Writer interface, it can be passed as parameter +// of functions like fmt.Fprintf, fmt.Fprintln, io.Copy, etc. Clear must +// be called to clear the view's buffer. + +func (w *cuiWidget) Write(p []byte) (n int, err error) { + w.tainted = true + w.writeMutex.Lock() + defer w.writeMutex.Unlock() + // v.makeWriteable(v.wx, v.wy) + // v.writeRunes(bytes.Runes(p)) + fmt.Fprintln(w.v, p) + log(logNow, "widget.Write()") + + return len(p), nil +} diff --git a/toolkit/gocui/tab.go b/toolkit/gocui/tab.go new file mode 100644 index 0000000..42e5127 --- /dev/null +++ b/toolkit/gocui/tab.go @@ -0,0 +1,88 @@ +package main + +// implements widgets 'Window' and 'Tab' + +import ( + "git.wit.org/wit/gui/toolkit" +// "github.com/awesome-gocui/gocui" +) + +func (w *cuiWidget) hideWidgets() { + switch w.widgetType { + case toolkit.Root: + case toolkit.Flag: + case toolkit.Window: + case toolkit.Tab: + case toolkit.Box: + case toolkit.Grid: + default: + if (w.v != nil) { + me.baseGui.DeleteView(w.cuiName) + w.v = nil + } + } + for _, child := range w.children { + child.hideWidgets() + } +} + +func (w *cuiWidget) showWidgets() { + w.drawView() + for _, child := range w.children { + child.showWidgets() + } +} + +func (w *cuiWidget) redoTabs(draw bool) { + log(logNow, "redoTabs() START", w.name) + if (w == nil) { + return + } + if (w.widgetType == toolkit.Root) { + w.logicalSize.w0 = 0 + w.logicalSize.h0 = 0 + w.logicalSize.w1 = 0 + w.logicalSize.h1 = 0 + + w.nextW = 2 + w.nextH = 2 + } + + log(logNow, "redoTabs() about to check for window and tab ", w.name) + w.text = w.name + t := len(w.text) + if ((w.widgetType == toolkit.Window) || (w.widgetType == toolkit.Tab)) { + log(logNow, "redoTabs() in Window and Tab", w.name) + w.realWidth = t + 2 + w.realHeight = me.defaultHeight + + w.realSize.w0 = me.rootNode.logicalSize.w1 + w.realSize.h0 = 0 + w.realSize.w1 = w.realSize.w0 + w.realWidth + w.realSize.h1 = w.realHeight + + w.logicalSize.w0 = 0 + w.logicalSize.h0 = 0 + w.logicalSize.w1 = 0 + w.logicalSize.h1 = w.realHeight + + // spaces right 1 space to next tab widget + // spaces down 1 line to the next widget + w.nextW = 2 + w.nextH = w.realHeight + 1 + + me.rootNode.logicalSize.w1 = w.realSize.w1 + 1 + me.rootNode.logicalSize.h1 = 0 + + me.baseGui.DeleteView(w.cuiName) + w.v = nil + w.drawView() + w.showWidgetPlacement(logNow, "redoTabs()") + } + + log(logNow, "redoTabs() about to for loop children", w.name) + for _, child := range w.children { + log(logNow, "redoTabs() got to child", child.name) + child.redoTabs(draw) + } +} diff --git a/toolkit/gocui/view.go b/toolkit/gocui/view.go new file mode 100644 index 0000000..d1d4b51 --- /dev/null +++ b/toolkit/gocui/view.go @@ -0,0 +1,86 @@ +package main + +import ( + "fmt" + "errors" + "strconv" + "bufio" + "strings" + + "github.com/awesome-gocui/gocui" +// "git.wit.org/wit/gui/toolkit" +) + +func splitLines(s string) []string { + var lines []string + sc := bufio.NewScanner(strings.NewReader(s)) + for sc.Scan() { + lines = append(lines, sc.Text()) + } + return lines +} + +func (w *cuiWidget) textResize() { + var width, height int + + for i, s := range splitLines(w.text) { + log(logNow, "textResize() len =", len(s), i, s) + if (width < len(s)) { + width = len(s) + } + height = i + } + w.realWidth = width + 3 + w.realHeight = me.defaultHeight + height + w.realSize.w1 = w.realSize.w0 + w.realWidth + w.realSize.h1 = w.realSize.h0 + w.realHeight + w.showWidgetPlacement(logNow, "textResize()") +} + +func (w *cuiWidget) drawView() { + var err error + if (w.cuiName == "") { + log(logError, "drawView() w.cuiName was not set for widget", w) + w.cuiName = strconv.Itoa(w.id) + } + + 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 + } + } + + v, _ := me.baseGui.View(w.cuiName) + if (v != nil) { + log(logError, "drawView() already defined for name", w.cuiName) + w.v = v + return + } + + a := w.realSize.w0 + b := w.realSize.h0 + c := w.realSize.w1 + d := w.realSize.h1 + + w.v, err = me.baseGui.SetView(w.cuiName, a, b, c, d, 0) + if err == nil { + log(logError, "drawView() internal plugin error err = nil") + return + } + if !errors.Is(err, gocui.ErrUnknownView) { + log(logError, "drawView() internal plugin error error.IS()", err) + return + } + + me.baseGui.SetKeybinding(w.v.Name(), gocui.MouseLeft, gocui.ModNone, click) + + w.v.Wrap = true + fmt.Fprintln(w.v, " " + w.text) + + // w.SetDefaultWidgetColor() +} diff --git a/toolkit/gocui/views.go b/toolkit/gocui/views.go deleted file mode 100644 index 8b17acf..0000000 --- a/toolkit/gocui/views.go +++ /dev/null @@ -1,80 +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" - "strings" - - "github.com/awesome-gocui/gocui" - // "git.wit.org/wit/gui/toolkit" -) - -func newView(g *gocui.Gui) error { - maxX, maxY := g.Size() - name := fmt.Sprintf("v%v", idxView) - v, err := g.SetView(name, maxX/2-5, maxY/2-5, maxX/2+5, maxY/2+5, 0) - if err == nil { - return err - } - if !errors.Is(err, gocui.ErrUnknownView) { - return err - } - - v.Wrap = true - fmt.Fprintln(v, strings.Repeat(name+" ", 30)) - - if _, err := g.SetCurrentView(name); err != nil { - return err - } - - views = append(views, name) - curView = len(views) - 1 - idxView += 1 - return nil -} - -func delView(g *gocui.Gui) error { - if len(views) <= 1 { - return nil - } - - if err := g.DeleteView(views[curView]); err != nil { - return err - } - views = append(views[:curView], views[curView+1:]...) - - return nextView(g, false) -} - -func nextView(g *gocui.Gui, disableCurrent bool) error { - next := curView + 1 - if next > len(views)-1 { - next = 0 - } - - if _, err := g.SetCurrentView(views[next]); err != nil { - return err - } - - curView = next - return nil -} - -func moveView(g *gocui.Gui, v *gocui.View, dx, dy int) error { - name := v.Name() - x0, y0, x1, y1, err := g.ViewPosition(name) - if err != nil { - return err - } - log(x0, y0, x1, y1) - if _, err := g.SetView(name, x0+dx, y0+dy, x1+dx, y1+dy, 0); err != nil { - return err - } - x0, y0, x1, y1, err = g.ViewPosition(name) - log(x0, y0, x1, y1) - return nil -} diff --git a/toolkit/gocui/window.go b/toolkit/gocui/window.go deleted file mode 100644 index 67948d6..0000000 --- a/toolkit/gocui/window.go +++ /dev/null @@ -1,16 +0,0 @@ -package main - -import ( - "git.wit.org/wit/gui/toolkit" -) - -func NewWindow(w *toolkit.Widget) { - if (w == nil) { - log("wit/gui plugin error. widget == nil") - return - } - if (w.Name == "") { - w.Name = "nil newWindow" - } - log("gui.gocui.AddWindow", w.Name) -} -- cgit v1.2.3