init move into seperate repo. all history lost :(
Signed-off-by: Jeff Carr <jcarr@wit.com>
This commit is contained in:
commit
3ac6b2486a
|
@ -0,0 +1,26 @@
|
|||
all: plugin
|
||||
ldd ../gocui.so
|
||||
|
||||
plugin:
|
||||
GO111MODULE=off go build -v -buildmode=plugin -o ../gocui.so
|
||||
|
||||
goget:
|
||||
go get -v -t -u
|
||||
|
||||
objdump:
|
||||
objdump -t ../gocui.so |less
|
||||
|
||||
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)
|
||||
|
||||
redomod:
|
||||
rm -f go.*
|
||||
GO111MODULE= go mod init
|
||||
GO111MODULE= go mod tidy
|
|
@ -0,0 +1,80 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"go.wit.com/lib/widget"
|
||||
log "go.wit.com/log"
|
||||
)
|
||||
|
||||
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
|
||||
w.isFake = true
|
||||
|
||||
n.gocuiSetWH(fakeStartWidth, fakeStartHeight)
|
||||
|
||||
fakeStartHeight += w.gocuiSize.Height()
|
||||
// TODO: use the actual max hight of the terminal window
|
||||
if fakeStartHeight > 24 {
|
||||
fakeStartHeight = me.TabH
|
||||
fakeStartWidth += me.FakeW
|
||||
}
|
||||
if true {
|
||||
n.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)
|
||||
switch n.WidgetType {
|
||||
case widget.Root:
|
||||
log.Log(INFO, "setStartWH() rootNode w.id =", n.WidgetId, "w.name", n.progname)
|
||||
nw.color = &colorRoot
|
||||
n.setFake()
|
||||
return
|
||||
case widget.Flag:
|
||||
nw.color = &colorFlag
|
||||
n.setFake()
|
||||
return
|
||||
case widget.Window:
|
||||
nw.frame = false
|
||||
nw.color = &colorWindow
|
||||
// redoWindows(0,0)
|
||||
return
|
||||
case widget.Tab:
|
||||
nw.color = &colorTab
|
||||
// redoWindows(0,0)
|
||||
return
|
||||
case widget.Button:
|
||||
nw.color = &colorButton
|
||||
case widget.Box:
|
||||
nw.color = &colorBox
|
||||
nw.isFake = true
|
||||
n.setFake()
|
||||
return
|
||||
case widget.Grid:
|
||||
nw.color = &colorGrid
|
||||
nw.isFake = true
|
||||
n.setFake()
|
||||
return
|
||||
case widget.Group:
|
||||
nw.color = &colorGroup
|
||||
nw.frame = false
|
||||
return
|
||||
case widget.Label:
|
||||
nw.color = &colorLabel
|
||||
nw.frame = false
|
||||
return
|
||||
default:
|
||||
/*
|
||||
if n.IsCurrent() {
|
||||
n.updateCurrent()
|
||||
}
|
||||
*/
|
||||
}
|
||||
n.showWidgetPlacement(true, "addWidget()")
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package main
|
||||
|
||||
/*
|
||||
this enables command line options from other packages like 'gui' and 'log'
|
||||
*/
|
||||
|
||||
import (
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
var outputS []string
|
||||
|
||||
var NOW *log.LogFlag
|
||||
var INFO *log.LogFlag
|
||||
|
||||
var SPEW *log.LogFlag
|
||||
var WARN *log.LogFlag
|
||||
|
||||
var ERROR *log.LogFlag
|
||||
|
||||
func init() {
|
||||
full := "toolkit/nocui"
|
||||
short := "nocui"
|
||||
|
||||
NOW = log.NewFlag("NOW", true, full, short, "temp debugging stuff")
|
||||
INFO = log.NewFlag("INFO", false, 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")
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
// "github.com/awesome-gocui/gocui"
|
||||
"go.wit.com/lib/widget"
|
||||
)
|
||||
|
||||
func (n *node) setCheckbox(b any) {
|
||||
w := n.tk
|
||||
if n.WidgetType != widget.Checkbox {
|
||||
return
|
||||
}
|
||||
if widget.GetBool(b) {
|
||||
n.value = b
|
||||
n.tk.label = "X " + n.label
|
||||
} else {
|
||||
n.value = b
|
||||
n.tk.label = " " + n.label
|
||||
}
|
||||
t := len(n.tk.label) + 1
|
||||
w.gocuiSize.w1 = w.gocuiSize.w0 + t
|
||||
|
||||
// w.realWidth = w.gocuiSize.Width() + me.PadW
|
||||
// w.realHeight = w.gocuiSize.Height() + me.PadH
|
||||
|
||||
// if w.frame {
|
||||
// w.realWidth += me.FramePadW
|
||||
// w.realHeight += me.FramePadH
|
||||
// }
|
||||
|
||||
n.deleteView()
|
||||
n.showView()
|
||||
}
|
|
@ -0,0 +1,357 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"go.wit.com/lib/widget"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
// set isCurrent = false everywhere
|
||||
func unsetCurrent(n *node) {
|
||||
w := n.tk
|
||||
w.isCurrent = false
|
||||
|
||||
if n.WidgetType == widget.Tab {
|
||||
// n.tk.color = &colorTab
|
||||
// n.setColor()
|
||||
}
|
||||
|
||||
for _, child := range n.children {
|
||||
unsetCurrent(child)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
if n.WidgetType == widget.Tab {
|
||||
if n.IsCurrent() {
|
||||
// n.tk.color = &colorActiveT
|
||||
n.setColor(&colorActiveT)
|
||||
n.hideView()
|
||||
n.showView()
|
||||
setCurrentTab(n)
|
||||
} else {
|
||||
// n.tk.color = &colorTab
|
||||
// n.setColor()
|
||||
}
|
||||
return
|
||||
}
|
||||
if n.WidgetType == widget.Window {
|
||||
if n.IsCurrent() {
|
||||
// setCurrentWindow(n)
|
||||
}
|
||||
return
|
||||
}
|
||||
if n.WidgetType == widget.Root {
|
||||
return
|
||||
}
|
||||
n.parent.updateCurrent()
|
||||
}
|
||||
|
||||
// shows the widgets in a window
|
||||
func setCurrentWindow(n *node) {
|
||||
if n.IsCurrent() {
|
||||
return
|
||||
}
|
||||
w := n.tk
|
||||
if n.WidgetType != widget.Window {
|
||||
return
|
||||
}
|
||||
unsetCurrent(me.rootNode)
|
||||
|
||||
if n.hasTabs {
|
||||
// set isCurrent = true on the first tab
|
||||
for _, child := range n.children {
|
||||
child.tk.isCurrent = true
|
||||
break
|
||||
}
|
||||
} else {
|
||||
w.isCurrent = true
|
||||
}
|
||||
}
|
||||
|
||||
// shows the widgets in a tab
|
||||
func setCurrentTab(n *node) {
|
||||
w := n.tk
|
||||
if n.WidgetType != widget.Tab {
|
||||
return
|
||||
}
|
||||
unsetCurrent(me.rootNode)
|
||||
w.isCurrent = true
|
||||
p := n.parent.tk
|
||||
p.isCurrent = true
|
||||
log.Log(NOW, "setCurrent()", n.progname)
|
||||
}
|
||||
|
||||
func (n *node) doWidgetClick() {
|
||||
switch n.WidgetType {
|
||||
case widget.Root:
|
||||
// THIS IS THE BEGINING OF THE LAYOUT
|
||||
log.Log(NOW, "doWidgetClick()", n.progname)
|
||||
redoWindows(0, 0)
|
||||
case widget.Flag:
|
||||
log.Log(NOW, "doWidgetClick() FLAG widget name =", n.progname)
|
||||
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()
|
||||
}
|
||||
n.hideWidgets()
|
||||
me.currentWindow = n
|
||||
// setCurrentWindow(n) // probably delete this
|
||||
n.setColor(&colorActiveW)
|
||||
n.redoTabs(me.TabW, me.TabH)
|
||||
for _, child := range n.children {
|
||||
if child.currentTab == true {
|
||||
log.Log(NOW, "FOUND CURRENT TAB", child.progname)
|
||||
setCurrentTab(child)
|
||||
child.placeWidgets(me.RawW, me.RawH)
|
||||
child.showWidgets()
|
||||
return
|
||||
}
|
||||
}
|
||||
/* FIXME: redo this
|
||||
if ! n.hasTabs {
|
||||
}
|
||||
*/
|
||||
case widget.Tab:
|
||||
if n.IsCurrent() {
|
||||
return // do nothing if you reclick on the already selected tab
|
||||
}
|
||||
// find the window and disable the active tab
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
n.currentTab = true
|
||||
n.setColor(&colorActiveT)
|
||||
setCurrentTab(n)
|
||||
n.placeWidgets(me.RawW, me.RawH)
|
||||
n.showWidgets()
|
||||
case widget.Group:
|
||||
// n.placeWidgets(p.tk.startH, newH)
|
||||
n.toggleTree()
|
||||
case widget.Checkbox:
|
||||
if widget.GetBool(n.value) {
|
||||
n.setCheckbox(false)
|
||||
} else {
|
||||
n.setCheckbox(true)
|
||||
}
|
||||
n.doUserEvent()
|
||||
case widget.Grid:
|
||||
newR := n.realGocuiSize()
|
||||
|
||||
// w,h := n.logicalSize()
|
||||
// w := newR.w1 - newR.w0
|
||||
// h := newR.h1 - newR.h0
|
||||
|
||||
n.placeGrid(newR.w0, newR.h0)
|
||||
n.showWidgets()
|
||||
case widget.Box:
|
||||
// w.showWidgetPlacement(logNow, "drawTree()")
|
||||
if n.direction == widget.Horizontal {
|
||||
log.Log(NOW, "BOX IS HORIZONTAL", n.progname)
|
||||
} else {
|
||||
log.Log(NOW, "BOX IS VERTICAL", n.progname)
|
||||
}
|
||||
// n.placeWidgets()
|
||||
n.toggleTree()
|
||||
case widget.Button:
|
||||
n.doUserEvent()
|
||||
case widget.Dropdown:
|
||||
log.Log(NOW, "do the dropdown here")
|
||||
if me.ddview == nil {
|
||||
me.ddview = addDropdown()
|
||||
tk := me.ddview.tk
|
||||
tk.gocuiSize.w0 = 20
|
||||
tk.gocuiSize.w1 = 40
|
||||
tk.gocuiSize.h0 = 10
|
||||
tk.gocuiSize.h1 = 25
|
||||
tk.v, _ = me.baseGui.SetView("ddview",
|
||||
tk.gocuiSize.w0,
|
||||
tk.gocuiSize.h0,
|
||||
tk.gocuiSize.w1,
|
||||
tk.gocuiSize.h1, 0)
|
||||
if tk.v == nil {
|
||||
return
|
||||
}
|
||||
tk.v.Wrap = true
|
||||
tk.v.Frame = true
|
||||
tk.v.Clear()
|
||||
fmt.Fprint(tk.v, "example.com\nwit.com")
|
||||
me.ddview.SetVisible(true)
|
||||
return
|
||||
}
|
||||
log.Log(NOW, "doWidgetClick() visible =", me.ddview.Visible())
|
||||
if me.ddview.Visible() {
|
||||
me.ddview.SetVisible(false)
|
||||
me.baseGui.DeleteView("ddview")
|
||||
me.ddview.tk.v = nil
|
||||
} else {
|
||||
var dnsList string
|
||||
for i, s := range n.vals {
|
||||
log.Log(NOW, "AddText()", n.progname, 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)
|
||||
}
|
||||
for i, s := range n.vals {
|
||||
log.Log(NOW, "AddText()", n.progname, i, s)
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
var toggle bool = true
|
||||
|
||||
func (n *node) toggleTree() {
|
||||
if toggle {
|
||||
n.drawTree(toggle)
|
||||
toggle = false
|
||||
} else {
|
||||
n.hideWidgets()
|
||||
toggle = true
|
||||
}
|
||||
}
|
||||
|
||||
// display the widgets in the binary tree
|
||||
func (n *node) drawTree(draw bool) {
|
||||
w := n.tk
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
n.showWidgetPlacement(true, "drawTree()")
|
||||
if draw {
|
||||
// w.textResize()
|
||||
n.showView()
|
||||
} else {
|
||||
n.deleteView()
|
||||
}
|
||||
|
||||
for _, child := range n.children {
|
||||
child.drawTree(draw)
|
||||
}
|
||||
}
|
||||
|
||||
func click(g *gocui.Gui, v *gocui.View) error {
|
||||
// var l string
|
||||
// var err error
|
||||
|
||||
log.Log(INFO, "click() START", v.Name())
|
||||
// n := me.rootNode.findWidgetName(v.Name())
|
||||
n := findUnderMouse()
|
||||
if n != nil {
|
||||
log.Log(NOW, "click() Found widget =", n.WidgetId, n.progname, ",", n.label)
|
||||
if n.progname == "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()
|
||||
} else {
|
||||
log.Log(NOW, "click() could not find node name =", v.Name())
|
||||
}
|
||||
|
||||
if _, err := g.SetCurrentView(v.Name()); err != nil {
|
||||
log.Log(NOW, "click() END err =", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Log(NOW, "click() END")
|
||||
return nil
|
||||
}
|
||||
|
||||
func findUnderMouse() *node {
|
||||
var found *node
|
||||
var widgets []*node
|
||||
var f func(n *node)
|
||||
w, h := me.baseGui.MousePosition()
|
||||
|
||||
// find buttons that are below where the mouse button click
|
||||
f = func(n *node) {
|
||||
widget := n.tk
|
||||
// ignore widgets that are not visible
|
||||
if n.Visible() {
|
||||
if (widget.gocuiSize.w0 <= w) && (w <= widget.gocuiSize.w1) &&
|
||||
(widget.gocuiSize.h0 <= h) && (h <= widget.gocuiSize.h1) {
|
||||
widgets = append(widgets, n)
|
||||
found = n
|
||||
}
|
||||
}
|
||||
if n == me.ddview {
|
||||
log.Log(NOW, "findUnderMouse() found ddview")
|
||||
if n.Visible() {
|
||||
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
|
||||
me.baseGui.DeleteView("ddview")
|
||||
} else {
|
||||
log.Log(NOW, "findUnderMouse() I was lying, actually it's not found")
|
||||
}
|
||||
}
|
||||
|
||||
for _, child := range n.children {
|
||||
f(child)
|
||||
}
|
||||
}
|
||||
f(me.rootNode)
|
||||
// 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")
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
// find the widget under the mouse click
|
||||
func ctrlDown(g *gocui.Gui, v *gocui.View) error {
|
||||
var found *node
|
||||
// var widgets []*node
|
||||
// var f func (n *node)
|
||||
found = findUnderMouse()
|
||||
if me.ctrlDown == nil {
|
||||
setupCtrlDownWidget()
|
||||
me.ctrlDown.label = found.progname
|
||||
me.ctrlDown.tk.cuiName = "ctrlDown"
|
||||
// me.ctrlDown.parent = me.rootNode
|
||||
}
|
||||
cd := me.ctrlDown.tk
|
||||
if found == nil {
|
||||
found = me.rootNode
|
||||
}
|
||||
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()
|
||||
} else {
|
||||
me.ctrlDown.showView()
|
||||
}
|
||||
me.ctrlDown.showWidgetPlacement(true, "ctrlDown:")
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"math/rand"
|
||||
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
//w.v.SelBgColor = gocui.ColorCyan
|
||||
//color.go: w.v.SelFgColor = gocui.ColorBlack
|
||||
//color.go: w.v.BgColor = gocui.ColorGreen
|
||||
|
||||
type colorT struct {
|
||||
frame gocui.Attribute
|
||||
fg gocui.Attribute
|
||||
bg gocui.Attribute
|
||||
selFg gocui.Attribute
|
||||
selBg gocui.Attribute
|
||||
name string
|
||||
}
|
||||
|
||||
var none gocui.Attribute = gocui.AttrNone
|
||||
var lightPurple gocui.Attribute = gocui.GetColor("#DDDDDD") // light purple
|
||||
var darkPurple gocui.Attribute = gocui.GetColor("#FFAA55") // Dark Purple
|
||||
var heavyPurple gocui.Attribute = gocui.GetColor("#88AA55") // heavy purple
|
||||
var powdererBlue gocui.Attribute = gocui.GetColor("#B0E0E6") // w3c 'powerder blue'
|
||||
var superLightGrey gocui.Attribute = gocui.GetColor("#55AAFF") // super light grey
|
||||
|
||||
// Standard defined colors from gocui:
|
||||
// ColorBlack ColorRed ColorGreen ColorYellow ColorBlue ColorMagenta ColorCyan ColorWhite
|
||||
|
||||
// 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
|
||||
|
||||
// Normal Text On mouseover
|
||||
//
|
||||
// Widget Frame Text background Text background
|
||||
var colorWindow colorT = colorT{none, gocui.ColorBlue, none, none, powdererBlue, "normal window"}
|
||||
var colorActiveW colorT = colorT{none, none, powdererBlue, none, powdererBlue, "active window"}
|
||||
|
||||
var colorTab colorT = colorT{gocui.ColorBlue, gocui.ColorBlue, none, none, powdererBlue, "normal tab"}
|
||||
var colorActiveT colorT = colorT{gocui.ColorBlue, none, powdererBlue, none, powdererBlue, "active tab"}
|
||||
|
||||
var colorButton colorT = colorT{gocui.ColorGreen, none, gocui.ColorWhite, gocui.ColorGreen, gocui.ColorBlack, "normal button"}
|
||||
var colorLabel colorT = colorT{none, none, superLightGrey, none, superLightGrey, "normal label"}
|
||||
var colorGroup colorT = colorT{none, none, superLightGrey, none, superLightGrey, "normal group"}
|
||||
|
||||
// widget debugging colors. these widgets aren't displayed unless you are debugging
|
||||
var colorRoot colorT = colorT{gocui.ColorRed, none, powdererBlue, none, gocui.ColorBlue, "debug root"}
|
||||
var colorFlag colorT = colorT{gocui.ColorRed, none, powdererBlue, none, gocui.ColorGreen, "debug flag"}
|
||||
var colorBox colorT = colorT{gocui.ColorRed, none, lightPurple, none, gocui.ColorCyan, "debug box"}
|
||||
var colorGrid colorT = colorT{gocui.ColorRed, none, lightPurple, none, gocui.ColorRed, "debug grid"}
|
||||
var colorNone colorT = colorT{none, none, none, none, none, "debug none"}
|
||||
|
||||
// actually sets the colors for the gocui element
|
||||
// the user will see the colors change when this runs
|
||||
// TODO: add black/white only flag for ttyS0
|
||||
// TODO: or fix kvm/qemu serial console & SIGWINCH.
|
||||
// TODO: and minicom and uboot and 5 million other things.
|
||||
// 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
|
||||
if tk.color == newColor {
|
||||
// nothing to do since the colors have nto changed
|
||||
return
|
||||
}
|
||||
tk.color = newColor
|
||||
if tk.v == nil {
|
||||
return
|
||||
}
|
||||
if tk.color == nil {
|
||||
log.Log(NOW, "Set the node to color = nil")
|
||||
tk.color = &colorNone
|
||||
}
|
||||
log.Log(NOW, "Set the node to color =", tk.color.name)
|
||||
n.recreateView()
|
||||
}
|
||||
|
||||
func (n *node) setDefaultWidgetColor() {
|
||||
n.showView()
|
||||
}
|
||||
|
||||
func (n *node) setDefaultHighlight() {
|
||||
w := n.tk
|
||||
if w.v == nil {
|
||||
log.Log(ERROR, "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.Log(NOW, "randColor() i =", i)
|
||||
return gocui.GetColor(colors[i])
|
||||
}
|
||||
|
||||
func (n *node) redoColor(draw bool) {
|
||||
w := n.tk
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.Sleep(.05)
|
||||
n.setDefaultHighlight()
|
||||
n.setDefaultWidgetColor()
|
||||
|
||||
for _, child := range n.children {
|
||||
child.redoColor(draw)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,218 @@
|
|||
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/lib/widget"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go.wit.com/lib/widget"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func (n *node) dumpTree(draw bool) {
|
||||
w := n.tk
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
n.showWidgetPlacement(true, "dumpTree()")
|
||||
|
||||
for _, child := range n.children {
|
||||
child.dumpTree(draw)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) showWidgetPlacement(b bool, s string) {
|
||||
if n == nil {
|
||||
log.Log(ERROR, "WTF w == nil")
|
||||
return
|
||||
}
|
||||
w := n.tk
|
||||
|
||||
var s1 string
|
||||
var pId int
|
||||
if n.parent == nil {
|
||||
log.Log(INFO, "showWidgetPlacement() parent == nil", n.WidgetId, w.cuiName)
|
||||
pId = 0
|
||||
} else {
|
||||
pId = n.parent.WidgetId
|
||||
}
|
||||
s1 = fmt.Sprintf("(wId,pId)=(%2d,%2d) ", n.WidgetId, pId)
|
||||
if n.Visible() {
|
||||
s1 += fmt.Sprintf("gocui=(%2d,%2d)(%2d,%2d,%2d,%2d)",
|
||||
w.gocuiSize.Width(), w.gocuiSize.Height(),
|
||||
w.gocuiSize.w0, w.gocuiSize.h0, w.gocuiSize.w1, w.gocuiSize.h1)
|
||||
} else {
|
||||
s1 += fmt.Sprintf(" ")
|
||||
}
|
||||
if n.parent != nil {
|
||||
if n.parent.WidgetType == widget.Grid {
|
||||
s1 += fmt.Sprintf("At(%2d,%2d) ", n.AtW, n.AtH)
|
||||
}
|
||||
}
|
||||
tmp := "." + n.progname + "."
|
||||
log.Log(INFO, s1, s, n.WidgetType, ",", tmp) // , "text=", w.text)
|
||||
}
|
||||
|
||||
func (n *node) dumpWidget(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() {
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var pad string
|
||||
for i := 0; i < me.depth; i++ {
|
||||
pad = pad + " "
|
||||
}
|
||||
n.dumpWidget(pad)
|
||||
|
||||
for _, child := range n.children {
|
||||
me.depth += 1
|
||||
child.listWidgets()
|
||||
me.depth -= 1
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
type FakeFile struct {
|
||||
reader *bytes.Reader
|
||||
buffer *bytes.Buffer
|
||||
offset int64
|
||||
}
|
||||
|
||||
func (f *FakeFile) Read(p []byte) (n int, err error) {
|
||||
n, err = f.reader.ReadAt(p, f.offset)
|
||||
f.offset += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (f *FakeFile) Write(p []byte) (n int, err error) {
|
||||
n, err = f.buffer.Write(p)
|
||||
f.offset += int64(n)
|
||||
f.reader.Reset(f.buffer.Bytes())
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (f *FakeFile) Seek(offset int64, whence int) (int64, error) {
|
||||
newOffset := f.offset
|
||||
|
||||
switch whence {
|
||||
case io.SeekStart:
|
||||
newOffset = offset
|
||||
case io.SeekCurrent:
|
||||
newOffset += offset
|
||||
case io.SeekEnd:
|
||||
newOffset = int64(f.buffer.Len()) + offset
|
||||
default:
|
||||
return 0, errors.New("Seek: whence not at start,current or end")
|
||||
}
|
||||
// never can get here right?
|
||||
|
||||
if newOffset < 0 {
|
||||
return 0, errors.New("Seek: offset < 0")
|
||||
}
|
||||
|
||||
f.offset = newOffset
|
||||
return f.offset, nil
|
||||
}
|
||||
|
||||
func NewFakeFile() *FakeFile {
|
||||
buf := &bytes.Buffer{}
|
||||
return &FakeFile{
|
||||
reader: bytes.NewReader(buf.Bytes()),
|
||||
buffer: buf,
|
||||
offset: 0,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
module go.wit.com/gui/toolkits/gocui
|
||||
|
||||
go 1.21.4
|
||||
|
||||
require (
|
||||
github.com/awesome-gocui/gocui v1.1.0
|
||||
go.wit.com/gui/widget v1.1.3
|
||||
go.wit.com/log v0.5.4
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gdamore/encoding v1.0.0 // indirect
|
||||
github.com/gdamore/tcell/v2 v2.4.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.10 // indirect
|
||||
github.com/rivo/uniseg v0.1.0 // indirect
|
||||
go.wit.com/dev/davecgh/spew v1.1.4 // indirect
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
|
||||
golang.org/x/text v0.3.3 // indirect
|
||||
)
|
|
@ -0,0 +1,30 @@
|
|||
github.com/awesome-gocui/gocui v1.1.0 h1:db2j7yFEoHZjpQFeE2xqiatS8bm1lO3THeLwE6MzOII=
|
||||
github.com/awesome-gocui/gocui v1.1.0/go.mod h1:M2BXkrp7PR97CKnPRT7Rk0+rtswChPtksw/vRAESGpg=
|
||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell/v2 v2.4.0 h1:W6dxJEmaxYvhICFoTY3WrLLEXsQ11SaFnKGVEXW57KM=
|
||||
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
go.wit.com/dev/davecgh/spew v1.1.3 h1:hqnB5qsPgC2cLZaJXqQJspQ5n/Ugry9kyL3tLk0hVzQ=
|
||||
go.wit.com/dev/davecgh/spew v1.1.3/go.mod h1:sihvWmnQ/09FWplnEmozt90CCVqBtGuPXM811tgfhFA=
|
||||
go.wit.com/dev/davecgh/spew v1.1.4 h1:C9hj/rjlUpdK+E6aroyLjCbS5MFcyNUOuP1ICLWdNek=
|
||||
go.wit.com/dev/davecgh/spew v1.1.4/go.mod h1:sihvWmnQ/09FWplnEmozt90CCVqBtGuPXM811tgfhFA=
|
||||
go.wit.com/gui/widget v1.1.3 h1:GvLzGSOF9tfmoh6HNbFdN+NSlBo2qeS/Ba2TnQQ1A1U=
|
||||
go.wit.com/gui/widget v1.1.3/go.mod h1:A6/FaiFQtAHTjgo7c4FrokXe6bXX1Cowo35b2Lgi31E=
|
||||
go.wit.com/log v0.5.3 h1:/zHkniOPusPEuX1R401rMny9uwSO/nSU/QOMx6qoEnE=
|
||||
go.wit.com/log v0.5.3/go.mod h1:LzIzVxc2xJQxWQBtV9VbV605P4TOxmYDCl+BZF38yGE=
|
||||
go.wit.com/log v0.5.4 h1:vijLRPTUgChb8J5tx/7Uma/lGTUxeSXosFbheAmL914=
|
||||
go.wit.com/log v0.5.4/go.mod h1:BaJBfHFqcJSJLXGQ9RHi3XVhPgsStxSMZRlaRxW4kAo=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
@ -0,0 +1,102 @@
|
|||
// Copyright 2014 The gocui Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/awesome-gocui/gocui"
|
||||
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
// This initializes the gocui package
|
||||
// it runs SetManagerFunc which passes every input
|
||||
// event (keyboard, mouse, etc) to the function "gocuiEvent()"
|
||||
func gocuiMain() {
|
||||
g, err := gocui.NewGui(gocui.OutputNormal, true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer g.Close()
|
||||
|
||||
me.baseGui = g
|
||||
|
||||
g.Cursor = true
|
||||
g.Mouse = true
|
||||
|
||||
// this sets the function that is run on every event. For example:
|
||||
// When you click the mouse, move the mouse, or press a key on the keyboard
|
||||
// This is equivalent to xev or similar to cat /dev/input on linux
|
||||
g.SetManagerFunc(gocuiEvent)
|
||||
|
||||
if err := defaultKeybindings(g); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := g.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Thanks to the gocui developers -- your package kicks ass
|
||||
// This function is called on every event. It is a callback function from the gocui package
|
||||
// which has an excellent implementation. While gocui handles things like text highlighting
|
||||
// and the layout of the text areas -- also things like handling SIGWINCH and lots of really
|
||||
// complicated console handling, it sends events here in a clean way.
|
||||
// This is equivalent to the linux command xev (apt install x11-utils)
|
||||
func gocuiEvent(g *gocui.Gui) error {
|
||||
maxX, maxY := g.Size()
|
||||
mx, my := g.MousePosition()
|
||||
log.Log(NOW, "handleEvent() START", maxX, maxY, mx, my, msgMouseDown)
|
||||
if _, err := g.View("msg"); msgMouseDown && err == nil {
|
||||
moveMsg(g)
|
||||
}
|
||||
if widgetView, _ := g.View("msg"); widgetView == nil {
|
||||
log.Log(NOW, "handleEvent() create output widget now", maxX, maxY, mx, my)
|
||||
makeOutputWidget(g, "this is a create before a mouse click")
|
||||
if me.logStdout != nil {
|
||||
// setOutput(me.logStdout)
|
||||
}
|
||||
} else {
|
||||
log.Log(INFO, "output widget already exists", maxX, maxY, mx, my)
|
||||
}
|
||||
mouseMove(g)
|
||||
log.Log(INFO, "handleEvent() END ", maxX, maxY, mx, my, msgMouseDown)
|
||||
return nil
|
||||
}
|
||||
|
||||
func dragOutputWindow() {
|
||||
}
|
||||
|
||||
// turns off the frame on the global window
|
||||
func setFrame(b bool) {
|
||||
// TODO: figure out what this might be useful for
|
||||
// what is this do? I made it just 2 lines for now. Is this useful for something?
|
||||
v := SetView("global", 5, 10, 5, 10, 0) // x0, x1, y1, y2, overlap
|
||||
if v == nil {
|
||||
log.Log(ERROR, "setFrame() global failed")
|
||||
}
|
||||
v.Frame = b
|
||||
}
|
||||
|
||||
func quit(g *gocui.Gui, v *gocui.View) error {
|
||||
return gocui.ErrQuit
|
||||
}
|
||||
|
||||
func SetView(name string, x0, y0, x1, y1 int, overlaps byte) *gocui.View {
|
||||
if me.baseGui == nil {
|
||||
log.Log(ERROR, "SetView() ERROR: me.baseGui == nil")
|
||||
return nil
|
||||
}
|
||||
|
||||
v, err := me.baseGui.SetView(name, x0, y0, x1, y1, overlaps)
|
||||
if err != nil {
|
||||
if !errors.Is(err, gocui.ErrUnknownView) {
|
||||
log.Log(ERROR, "SetView() global failed on name =", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return v
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
// 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"
|
||||
)
|
||||
|
||||
var helpText []string = []string{"KEYBINDINGS",
|
||||
"",
|
||||
"?: toggle help",
|
||||
"d: toggle debugging",
|
||||
"r: redraw widgets",
|
||||
"s/h: show/hide all widgets",
|
||||
"L: list all widgets",
|
||||
"M: list all widgets positions",
|
||||
"q: quit()",
|
||||
"p: panic()",
|
||||
"o: show Stdout",
|
||||
"l: log to /tmp/witgui.log",
|
||||
"Ctrl-D: Toggle Debugging",
|
||||
"Ctrl-V: Toggle Verbose Debugging",
|
||||
"Ctrl-C: Exit",
|
||||
"",
|
||||
}
|
||||
|
||||
func hidehelplayout() {
|
||||
me.baseGui.DeleteView("help")
|
||||
// n.deleteView()
|
||||
// child.hideFake()
|
||||
}
|
||||
|
||||
func helplayout() error {
|
||||
g := me.baseGui
|
||||
var err error
|
||||
maxX, _ := g.Size()
|
||||
|
||||
var newW int = 8
|
||||
for _, s := range helpText {
|
||||
if newW < len(s) {
|
||||
newW = len(s)
|
||||
}
|
||||
}
|
||||
|
||||
help, err := g.SetView("help", maxX-(newW+me.FramePadW), 0, maxX-1, len(helpText)+me.FramePadH, 0)
|
||||
if err != nil {
|
||||
if !errors.Is(err, gocui.ErrUnknownView) {
|
||||
return err
|
||||
}
|
||||
help.SelBgColor = gocui.ColorGreen
|
||||
help.SelFgColor = gocui.ColorBlack
|
||||
// fmt.Fprintln(help, "Enter: Click Button")
|
||||
// fmt.Fprintln(help, "Tab/Space: Switch Buttons")
|
||||
// fmt.Fprintln(help, "Backspace: Delete Button")
|
||||
// fmt.Fprintln(help, "Arrow keys: Move Button")
|
||||
|
||||
fmt.Fprintln(help, strings.Join(helpText, "\n"))
|
||||
|
||||
if _, err := g.SetCurrentView("help"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
me.helpLabel = help
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
// 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 (
|
||||
"github.com/awesome-gocui/gocui"
|
||||
|
||||
"go.wit.com/lib/widget"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func defaultKeybindings(g *gocui.Gui) error {
|
||||
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); 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.MouseRelease, gocui.ModNone, mouseUp); err != nil {
|
||||
return err
|
||||
}
|
||||
// mouseDown() runs whenever you click on an unknown view (?)
|
||||
if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, mouseDown); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModMouseCtrl, ctrlDown); err != nil {
|
||||
return err
|
||||
}
|
||||
// if err := g.SetKeybinding(w.v.Name(), gocui.MouseLeft, gocui.ModNone, click); 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
|
||||
}
|
||||
addDebugKeys(g)
|
||||
return nil
|
||||
}
|
||||
|
||||
func addDebugKeys(g *gocui.Gui) {
|
||||
// show debugging buttons
|
||||
g.SetKeybinding("", 'd', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
fakeStartWidth = me.FakeW
|
||||
fakeStartHeight = me.TabH + me.FramePadH
|
||||
if showDebug {
|
||||
me.rootNode.showFake()
|
||||
showDebug = false
|
||||
} else {
|
||||
me.rootNode.hideFake()
|
||||
showDebug = true
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// display the help menu
|
||||
g.SetKeybinding("", '?', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
if showHelp {
|
||||
helplayout()
|
||||
showHelp = false
|
||||
} else {
|
||||
me.baseGui.DeleteView("help")
|
||||
showHelp = true
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// redraw all the widgets
|
||||
g.SetKeybinding("", 'r', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
if redoWidgets {
|
||||
redoWindows(0, 0)
|
||||
redoWidgets = false
|
||||
} else {
|
||||
me.rootNode.hideWidgets()
|
||||
redoWidgets = true
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// hide all widgets
|
||||
g.SetKeybinding("", 'h', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
me.rootNode.hideWidgets()
|
||||
return nil
|
||||
})
|
||||
|
||||
// show all widgets
|
||||
g.SetKeybinding("", 's', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
me.rootNode.showWidgets()
|
||||
return nil
|
||||
})
|
||||
|
||||
// list all widgets
|
||||
g.SetKeybinding("", 'L', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
me.rootNode.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)
|
||||
return nil
|
||||
})
|
||||
|
||||
// log to output window
|
||||
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)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// exit
|
||||
g.SetKeybinding("", 'q', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
standardExit()
|
||||
return nil
|
||||
})
|
||||
g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
standardExit()
|
||||
return nil
|
||||
})
|
||||
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
|
||||
}
|
||||
return nil
|
||||
})
|
||||
g.SetKeybinding("", gocui.KeyCtrlV, gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
// panic
|
||||
g.SetKeybinding("", 'p', gocui.ModNone,
|
||||
func(g *gocui.Gui, v *gocui.View) error {
|
||||
standardExit()
|
||||
panic("forced panic in gocui")
|
||||
return nil
|
||||
})
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
// 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"
|
||||
|
||||
"go.wit.com/lib/widget"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
// sets defaults and establishes communication
|
||||
// to this toolkit from the wit/gui golang package
|
||||
func init() {
|
||||
log.Log(INFO, "Init() of awesome-gocui")
|
||||
|
||||
// init the config struct default values
|
||||
Set(&me, "default")
|
||||
|
||||
pluginChan = make(chan widget.Action)
|
||||
|
||||
log.Log(NOW, "Init() start pluginChan")
|
||||
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()")
|
||||
standardExit()
|
||||
}
|
||||
|
||||
func standardExit() {
|
||||
log.Log(NOW, "standardExit() doing baseGui.Close()")
|
||||
me.baseGui.Close()
|
||||
log.Log(NOW, "standardExit() doing outf.Close()")
|
||||
outf.Close()
|
||||
// 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
|
||||
// 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
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
log.Log(INFO, "main() start Init()")
|
||||
|
||||
outf, err = os.OpenFile("/tmp/witgui.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
log.Error(err, "error opening file: %v")
|
||||
os.Exit(0)
|
||||
}
|
||||
os.Stdout = outf
|
||||
defer outf.Close()
|
||||
|
||||
// setOutput(outf)
|
||||
// log("This is a test log entry")
|
||||
|
||||
ferr, _ := os.OpenFile("/tmp/witgui.err", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664)
|
||||
os.Stderr = ferr
|
||||
gocuiMain()
|
||||
|
||||
log.Log(NOW, "MouseMain() closed")
|
||||
standardExit()
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
// 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"
|
||||
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
// this function uses the mouse position to highlight & unhighlight things
|
||||
// this is run every time the user moves the mouse over the terminal window
|
||||
func mouseMove(g *gocui.Gui) {
|
||||
mx, my := g.MousePosition()
|
||||
for _, view := range g.Views() {
|
||||
view.Highlight = false
|
||||
}
|
||||
if v, err := g.ViewByPosition(mx, my); err == nil {
|
||||
v.Highlight = true
|
||||
}
|
||||
}
|
||||
|
||||
func msgDown(g *gocui.Gui, v *gocui.View) error {
|
||||
initialMouseX, initialMouseY = g.MousePosition()
|
||||
log.Log(NOW, "msgDown() X,Y", initialMouseX, initialMouseY)
|
||||
if vx, vy, _, _, err := g.ViewPosition("msg"); err == nil {
|
||||
xOffset = initialMouseX - vx
|
||||
yOffset = initialMouseY - vy
|
||||
msgMouseDown = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func hideDDview() error {
|
||||
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 {
|
||||
return gocui.ErrUnknownView
|
||||
}
|
||||
me.ddview.SetVisible(false)
|
||||
return nil
|
||||
}
|
||||
|
||||
func showDDview() error {
|
||||
w, h := me.baseGui.MousePosition()
|
||||
log.Log(NOW, "show dropdown menu() view msgMouseDown (w,h) =", w, h)
|
||||
if me.ddview == nil {
|
||||
return gocui.ErrUnknownView
|
||||
}
|
||||
if me.ddview.tk.v == nil {
|
||||
return gocui.ErrUnknownView
|
||||
}
|
||||
me.ddview.SetVisible(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func mouseUp(g *gocui.Gui, v *gocui.View) error {
|
||||
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)
|
||||
|
||||
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)
|
||||
log.Log(NOW, "mouseUp() value =", value, "valsLen =", valsLen)
|
||||
log.Log(NOW, "mouseUp() me.ddNode.vals =", me.ddNode.vals)
|
||||
if (value >= 0) && (value < valsLen) {
|
||||
newZone = me.ddNode.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()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
/*
|
||||
// if there is a drop down view active, treat it like a dialog box and close it
|
||||
if (hideDDview() == nil) {
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
if msgMouseDown {
|
||||
msgMouseDown = false
|
||||
if movingMsg {
|
||||
movingMsg = false
|
||||
return nil
|
||||
} else {
|
||||
g.DeleteView("msg")
|
||||
}
|
||||
} else if globalMouseDown {
|
||||
globalMouseDown = false
|
||||
g.DeleteView("globalDown")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mouseDown(g *gocui.Gui, v *gocui.View) error {
|
||||
mx, my := g.MousePosition()
|
||||
if vx0, vy0, vx1, vy1, err := g.ViewPosition("msg"); err == nil {
|
||||
if mx >= vx0 && mx <= vx1 && my >= vy0 && my <= vy1 {
|
||||
return msgDown(g, v)
|
||||
}
|
||||
}
|
||||
globalMouseDown = true
|
||||
maxX, _ := g.Size()
|
||||
test := findUnderMouse()
|
||||
msg := fmt.Sprintf("Mouse really down at: %d,%d", mx, my) + "foobar"
|
||||
if test == me.ddview {
|
||||
if me.ddview.Visible() {
|
||||
log.Log(NOW, "hide DDview() Mouse really down at:", mx, my)
|
||||
hideDDview()
|
||||
} else {
|
||||
log.Log(NOW, "show DDview() Mouse really down at:", mx, my)
|
||||
showDDview()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
x := mx - len(msg)/2
|
||||
if x < 0 {
|
||||
x = 0
|
||||
} else if x+len(msg)+1 > maxX-1 {
|
||||
x = maxX - 1 - len(msg) - 1
|
||||
}
|
||||
log.Log(NOW, "mouseDown() about to write out message to 'globalDown' view. msg =", msg)
|
||||
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
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"go.wit.com/lib/widget"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func (n *node) placeBox(startW int, startH int) {
|
||||
if n.WidgetType != widget.Box {
|
||||
return
|
||||
}
|
||||
n.showWidgetPlacement(true, "boxS()")
|
||||
|
||||
newW := startW
|
||||
newH := startH
|
||||
for _, child := range n.children {
|
||||
child.placeWidgets(newW, newH)
|
||||
// n.showWidgetPlacement(logNow, "boxS()")
|
||||
newR := child.realGocuiSize()
|
||||
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)
|
||||
// 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)
|
||||
// expand based on the child height
|
||||
newH += h
|
||||
}
|
||||
}
|
||||
|
||||
// just compute this every time?
|
||||
// newR := n.realGocuiSize()
|
||||
|
||||
n.showWidgetPlacement(true, "boxE()")
|
||||
}
|
||||
|
||||
func (n *node) placeWidgets(startW int, startH int) {
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
if me.rootNode == nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch n.WidgetType {
|
||||
case widget.Window:
|
||||
for _, child := range n.children {
|
||||
child.placeWidgets(me.RawW, me.RawH)
|
||||
return
|
||||
}
|
||||
case widget.Tab:
|
||||
for _, child := range n.children {
|
||||
child.placeWidgets(me.RawW, me.RawH)
|
||||
return
|
||||
}
|
||||
case widget.Grid:
|
||||
n.placeGrid(startW, startH)
|
||||
case widget.Box:
|
||||
n.placeBox(startW, startH)
|
||||
case widget.Group:
|
||||
// move the group to the parent's next location
|
||||
n.gocuiSetWH(startW, startH)
|
||||
n.showWidgetPlacement(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()
|
||||
// w := newR.w1 - newR.w0
|
||||
h := newR.h1 - newR.h0
|
||||
|
||||
// increment straight down
|
||||
newH += h
|
||||
}
|
||||
default:
|
||||
n.gocuiSetWH(startW, startH)
|
||||
// n.moveTo(startW, startH)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) placeGrid(startW int, startH int) {
|
||||
w := n.tk
|
||||
n.showWidgetPlacement(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()
|
||||
childW := newR.w1 - newR.w0
|
||||
childH := newR.h1 - newR.h0
|
||||
|
||||
// set the child's realWidth, and grid offset
|
||||
if w.widths[child.AtW] < childW {
|
||||
w.widths[child.AtW] = childW
|
||||
}
|
||||
if w.heights[child.AtH] < childH {
|
||||
w.heights[child.AtH] = childH
|
||||
}
|
||||
// child.showWidgetPlacement(logInfo, "grid: ")
|
||||
log.Log(INFO, "placeGrid:", child.progname, "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:")
|
||||
|
||||
var totalW, totalH int
|
||||
for i, w := range w.widths {
|
||||
if i < child.AtW {
|
||||
totalW += w
|
||||
}
|
||||
}
|
||||
for i, h := range w.heights {
|
||||
if i < child.AtH {
|
||||
totalH += h
|
||||
}
|
||||
}
|
||||
|
||||
// the new corner to move the child to
|
||||
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:")
|
||||
}
|
||||
n.showWidgetPlacement(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)
|
||||
newR := new(rectType)
|
||||
// initialize the values to opposite
|
||||
newR.w0 = 80
|
||||
newR.h0 = 24
|
||||
if me.baseGui != nil {
|
||||
maxW, maxH := me.baseGui.Size()
|
||||
newR.w0 = maxW
|
||||
newR.h0 = maxH
|
||||
}
|
||||
newR.w1 = 0
|
||||
newR.h1 = 0
|
||||
|
||||
// expand the rectangle to the biggest thing displayed
|
||||
f = func(n *node, r *rectType) {
|
||||
newR := n.tk.gocuiSize
|
||||
if !n.tk.isFake {
|
||||
if r.w0 > newR.w0 {
|
||||
r.w0 = newR.w0
|
||||
}
|
||||
if r.h0 > newR.h0 {
|
||||
r.h0 = newR.h0
|
||||
}
|
||||
if r.w1 < newR.w1 {
|
||||
r.w1 = newR.w1
|
||||
}
|
||||
if r.h1 < newR.h1 {
|
||||
r.h1 = newR.h1
|
||||
}
|
||||
}
|
||||
for _, child := range n.children {
|
||||
f(child, r)
|
||||
}
|
||||
}
|
||||
f(n, newR)
|
||||
return newR
|
||||
}
|
||||
|
||||
func (n *node) textSize() (int, int) {
|
||||
var width, height int
|
||||
|
||||
for _, s := range strings.Split(widget.GetString(n.value), "\n") {
|
||||
if width < len(s) {
|
||||
width = len(s)
|
||||
}
|
||||
height += 1
|
||||
}
|
||||
return width, height
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
package main
|
||||
|
||||
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)
|
||||
"go.wit.com/lib/widget"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func action(a *widget.Action) {
|
||||
log.Log(INFO, "action() START", a.WidgetId, a.ActionType, a.WidgetType, a.ProgName)
|
||||
n := me.rootNode.findWidgetId(a.WidgetId)
|
||||
var w *guiWidget
|
||||
if n != nil {
|
||||
w = n.tk
|
||||
}
|
||||
switch a.ActionType {
|
||||
case widget.Add:
|
||||
if w == nil {
|
||||
n := addNode(a)
|
||||
// w = n.tk
|
||||
n.addWidget()
|
||||
} else {
|
||||
// this is done to protect the plugin being 'refreshed' with the
|
||||
// widget binary tree. TODO: find a way to keep them in sync
|
||||
log.Log(ERROR, "action() Add ignored for already defined widget",
|
||||
a.WidgetId, a.ActionType, a.WidgetType, a.ProgName)
|
||||
}
|
||||
case widget.Show:
|
||||
if widget.GetBool(a.Value) {
|
||||
n.showView()
|
||||
} else {
|
||||
n.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)
|
||||
} 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)
|
||||
} else {
|
||||
n.Set(a.Value)
|
||||
}
|
||||
}
|
||||
case widget.SetText:
|
||||
n.SetText(widget.GetString(a.Value))
|
||||
case widget.AddText:
|
||||
n.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() {
|
||||
// widget was already shown
|
||||
} else {
|
||||
log.Log(INFO, "Setting Visable to true", a.ProgName)
|
||||
n.SetVisible(true)
|
||||
}
|
||||
case widget.Disable:
|
||||
if n.Visible() {
|
||||
log.Log(INFO, "Setting Visable to false", a.ProgName)
|
||||
n.SetVisible(false)
|
||||
} else {
|
||||
// widget was already hidden
|
||||
}
|
||||
default:
|
||||
log.Log(ERROR, "action() ActionType =", a.ActionType, "WidgetType =", a.WidgetType, "Name =", a.ProgName)
|
||||
}
|
||||
log.Log(INFO, "action() END")
|
||||
}
|
||||
|
||||
func (n *node) AddText(text string) {
|
||||
if n == 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)
|
||||
}
|
||||
n.SetText(text)
|
||||
}
|
||||
|
||||
func (n *node) SetText(text string) {
|
||||
var changed bool = false
|
||||
if n == nil {
|
||||
log.Log(NOW, "widget is nil")
|
||||
return
|
||||
}
|
||||
if widget.GetString(n.value) != text {
|
||||
n.value = text
|
||||
changed = true
|
||||
}
|
||||
if !changed {
|
||||
return
|
||||
}
|
||||
|
||||
if n.Visible() {
|
||||
n.textResize()
|
||||
n.deleteView()
|
||||
n.showView()
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) Set(val any) {
|
||||
// w := n.tk
|
||||
log.Log(INFO, "Set() value =", val)
|
||||
|
||||
n.value = val
|
||||
if n.WidgetType != widget.Checkbox {
|
||||
n.setCheckbox(val)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
|
||||
"go.wit.com/lib/widget"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
var outputW int = 180
|
||||
var outputH int = 24
|
||||
|
||||
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+outputW, my-yOffset+outputH+me.FramePadH, 0)
|
||||
g.SetViewOnBottom("msg")
|
||||
}
|
||||
|
||||
func showMsg(g *gocui.Gui, v *gocui.View) error {
|
||||
var l string
|
||||
var err error
|
||||
|
||||
log.Log(NOW, "showMsg() v.name =", v.Name())
|
||||
if _, err := g.SetCurrentView(v.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, cy := v.Cursor()
|
||||
if l, err = v.Line(cy); err != nil {
|
||||
l = ""
|
||||
}
|
||||
|
||||
makeOutputWidget(g, l)
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeOutputWidget(g *gocui.Gui, stringFromMouseClick string) *gocui.View {
|
||||
maxX, maxY := g.Size()
|
||||
|
||||
if me.rootNode == nil {
|
||||
// keep skipping this until the binary tree is initialized
|
||||
return nil
|
||||
}
|
||||
|
||||
if me.logStdout == nil {
|
||||
a := new(widget.Action)
|
||||
a.ProgName = "stdout"
|
||||
a.WidgetType = widget.Stdout
|
||||
a.WidgetId = -3
|
||||
a.ParentId = 0
|
||||
n := addNode(a)
|
||||
me.logStdout = n
|
||||
me.logStdout.tk.gocuiSize.w0 = maxX - 32
|
||||
me.logStdout.tk.gocuiSize.h0 = maxY / 2
|
||||
me.logStdout.tk.gocuiSize.w1 = me.logStdout.tk.gocuiSize.w0 + outputW
|
||||
me.logStdout.tk.gocuiSize.h1 = me.logStdout.tk.gocuiSize.h0 + outputH
|
||||
}
|
||||
v, err := g.View("msg")
|
||||
if v == nil {
|
||||
log.Log(NOW, "makeoutputwindow() this is supposed to happen. v == nil", err)
|
||||
} else {
|
||||
log.Log(NOW, "makeoutputwindow() msg != nil. WTF now? err =", err)
|
||||
}
|
||||
|
||||
// help, err := g.SetView("help", maxX-32, 0, maxX-1, 13, 0)
|
||||
// v, err = g.SetView("msg", 3, 3, 30, 30, 0)
|
||||
|
||||
v, err = g.SetView("msg", maxX-32, maxY/2, maxX/2+outputW, maxY/2+outputH, 0)
|
||||
if errors.Is(err, gocui.ErrUnknownView) {
|
||||
log.Log(NOW, "makeoutputwindow() this is supposed to happen?", err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Log(NOW, "makeoutputwindow() create output window failed", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if v == nil {
|
||||
log.Log(NOW, "makeoutputwindow() msg == nil. WTF now? err =", err)
|
||||
return nil
|
||||
} else {
|
||||
me.logStdout.tk.v = v
|
||||
}
|
||||
|
||||
v.Clear()
|
||||
v.SelBgColor = gocui.ColorCyan
|
||||
v.SelFgColor = gocui.ColorBlack
|
||||
fmt.Fprintln(v, "figure out how to capture STDOUT to here\n"+stringFromMouseClick)
|
||||
g.SetViewOnBottom("msg")
|
||||
// g.SetViewOnBottom(v.Name())
|
||||
return v
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
// 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"
|
||||
"github.com/awesome-gocui/gocui"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
// It's probably a terrible idea to call this 'me'
|
||||
var me config
|
||||
|
||||
var showDebug bool = true
|
||||
var showHelp bool = true
|
||||
var redoWidgets bool = true
|
||||
|
||||
// This is the window that is currently active
|
||||
var currentWindow *node
|
||||
|
||||
type config struct {
|
||||
baseGui *gocui.Gui // the main gocui handle
|
||||
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
|
||||
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
|
||||
|
||||
/*
|
||||
// this is the channel we send user events like
|
||||
// mouse clicks or keyboard events back to the program
|
||||
callback chan toolkit.Action
|
||||
|
||||
// this is the channel we get requests to make widgets
|
||||
pluginChan chan toolkit.Action
|
||||
*/
|
||||
|
||||
// When the widget has a frame, like a button, it adds 2 lines runes on each side
|
||||
// so you need 3 char spacing in each direction to not have them overlap
|
||||
// the amount of padding when there is a frame
|
||||
FramePadW int `default:"1" dense:"0"`
|
||||
FramePadH int `default:"1" dense:"0"`
|
||||
|
||||
PadW int `default:"1" dense:"0"`
|
||||
PadH int `default:"1" dense:"0"`
|
||||
|
||||
// how far down to start Window or Tab headings
|
||||
WindowW int `default:"8" dense:"0"`
|
||||
WindowH int `default:"-1"`
|
||||
TabW int `default:"5" dense:"0"`
|
||||
TabH int `default:"1" dense:"0"`
|
||||
|
||||
// additional amount of space to put between window & tab widgets
|
||||
WindowPadW int `default:"8" dense:"0"`
|
||||
TabPadW int `default:"4" dense:"0"`
|
||||
|
||||
// additional amount of space to indent on a group
|
||||
GroupPadW int `default:"6" dense:"2"`
|
||||
|
||||
// the raw beginning of each window (or tab)
|
||||
RawW int `default:"1"`
|
||||
RawH int `default:"5"`
|
||||
|
||||
// offset for the hidden widgets
|
||||
FakeW int `default:"20"`
|
||||
|
||||
padded bool // add space between things like buttons
|
||||
bookshelf bool // do you want things arranged in the box like a bookshelf or a stack?
|
||||
canvas bool // if set to true, the windows are a raw canvas
|
||||
menubar bool // for windows
|
||||
stretchy bool // expand things like buttons to the maximum size
|
||||
margin bool // add space around the frames of windows
|
||||
|
||||
// writeMutex protects locks the write process
|
||||
writeMutex sync.Mutex
|
||||
|
||||
// used for listWidgets() debugging
|
||||
depth int
|
||||
}
|
||||
|
||||
// deprecate these
|
||||
var (
|
||||
initialMouseX, initialMouseY, xOffset, yOffset int
|
||||
globalMouseDown, msgMouseDown, movingMsg bool
|
||||
)
|
||||
|
||||
// this is the gocui way
|
||||
// corner starts at in the upper left corner
|
||||
type rectType struct {
|
||||
w0, h0, w1, h1 int // left top right bottom
|
||||
}
|
||||
|
||||
func (r *rectType) Width() int {
|
||||
return r.w1 - r.w0
|
||||
}
|
||||
|
||||
func (r *rectType) Height() int {
|
||||
return r.h1 - r.h0
|
||||
}
|
||||
|
||||
type guiWidget struct {
|
||||
// the gocui package variables
|
||||
v *gocui.View // this is nil if the widget is not displayed
|
||||
cuiName string // what gocui uses to reference the widget
|
||||
|
||||
// the actual text to display in the console
|
||||
label string
|
||||
|
||||
// the logical size of the widget
|
||||
// For example, 40x12 would be the center of a normal terminal
|
||||
// size rectType
|
||||
|
||||
// the actual gocui display view of this widget
|
||||
// sometimes this isn't visible like with a Box or Grid
|
||||
gocuiSize rectType
|
||||
|
||||
isCurrent bool // is this the currently displayed Window or Tab?
|
||||
isFake bool // widget types like 'box' are 'false'
|
||||
|
||||
// used to track the size of grids
|
||||
widths map[int]int // how tall each row in the grid is
|
||||
heights map[int]int // how wide each column in the grid is
|
||||
|
||||
tainted bool
|
||||
frame bool
|
||||
|
||||
// for a window, this is currently selected tab
|
||||
selectedTab *node
|
||||
|
||||
// what color to use
|
||||
color *colorT
|
||||
}
|
||||
|
||||
// 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 *guiWidget) Write(p []byte) (n int, err error) {
|
||||
w.tainted = true
|
||||
me.writeMutex.Lock()
|
||||
defer me.writeMutex.Unlock()
|
||||
if me.logStdout.tk.v == nil {
|
||||
// optionally write the output to /tmp
|
||||
s := fmt.Sprint(string(p))
|
||||
s = strings.TrimSuffix(s, "\n")
|
||||
fmt.Fprintln(outf, s)
|
||||
v, _ := me.baseGui.View("msg")
|
||||
if v != nil {
|
||||
// fmt.Fprintln(outf, "found msg")
|
||||
me.logStdout.tk.v = v
|
||||
}
|
||||
} else {
|
||||
// display the output in the gocui window
|
||||
me.logStdout.tk.v.Clear()
|
||||
|
||||
s := fmt.Sprint(string(p))
|
||||
s = strings.TrimSuffix(s, "\n")
|
||||
tmp := strings.Split(s, "\n")
|
||||
outputS = append(outputS, tmp...)
|
||||
if len(outputS) > outputH {
|
||||
l := len(outputS) - outputH
|
||||
outputS = outputS[l:]
|
||||
}
|
||||
fmt.Fprintln(me.logStdout.tk.v, strings.Join(outputS, "\n"))
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func Set(ptr interface{}, tag string) error {
|
||||
if reflect.TypeOf(ptr).Kind() != reflect.Ptr {
|
||||
log.Log(ERROR, "Set() Not a pointer", ptr, "with tag =", tag)
|
||||
return fmt.Errorf("Not a pointer")
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(ptr).Elem()
|
||||
t := v.Type()
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
defaultVal := t.Field(i).Tag.Get(tag)
|
||||
name := t.Field(i).Name
|
||||
// log("Set() try name =", name, "defaultVal =", defaultVal)
|
||||
setField(v.Field(i), defaultVal, name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setField(field reflect.Value, defaultVal string, name string) error {
|
||||
|
||||
if !field.CanSet() {
|
||||
// log("setField() Can't set value", field, defaultVal)
|
||||
return fmt.Errorf("Can't set value\n")
|
||||
} else {
|
||||
log.Log(NOW, "setField() Can set value", name, defaultVal)
|
||||
}
|
||||
|
||||
switch field.Kind() {
|
||||
case reflect.Int:
|
||||
val, _ := strconv.Atoi(defaultVal)
|
||||
field.Set(reflect.ValueOf(int(val)).Convert(field.Type()))
|
||||
case reflect.String:
|
||||
field.Set(reflect.ValueOf(defaultVal).Convert(field.Type()))
|
||||
case reflect.Bool:
|
||||
if defaultVal == "true" {
|
||||
field.Set(reflect.ValueOf(true))
|
||||
} else {
|
||||
field.Set(reflect.ValueOf(false))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package main
|
||||
|
||||
// implements widgets 'Window' and 'Tab'
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"go.wit.com/lib/widget"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func (w *guiWidget) Width() int {
|
||||
if w.frame {
|
||||
return w.gocuiSize.w1 - w.gocuiSize.w0
|
||||
}
|
||||
return w.gocuiSize.w1 - w.gocuiSize.w0 - 1
|
||||
}
|
||||
|
||||
func (w *guiWidget) Height() int {
|
||||
if w.frame {
|
||||
return w.gocuiSize.h1 - w.gocuiSize.h0
|
||||
}
|
||||
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")
|
||||
h := len(lines)
|
||||
|
||||
tk := n.tk
|
||||
if tk.isFake {
|
||||
tk.gocuiSize.w0 = sizeW
|
||||
tk.gocuiSize.h0 = sizeH
|
||||
tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + me.FramePadW
|
||||
tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + me.FramePadH
|
||||
return
|
||||
}
|
||||
|
||||
if tk.frame {
|
||||
tk.gocuiSize.w0 = sizeW
|
||||
tk.gocuiSize.h0 = sizeH
|
||||
tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + me.FramePadW
|
||||
tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + me.FramePadH
|
||||
} else {
|
||||
tk.gocuiSize.w0 = sizeW - 1
|
||||
tk.gocuiSize.h0 = sizeH - 1
|
||||
tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + 1
|
||||
tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + 1
|
||||
}
|
||||
}
|
||||
|
||||
func redoWindows(nextW int, nextH int) {
|
||||
for _, n := range me.rootNode.children {
|
||||
if n.WidgetType != widget.Window {
|
||||
continue
|
||||
}
|
||||
w := n.tk
|
||||
var tabs bool
|
||||
for _, child := range n.children {
|
||||
if child.WidgetType == widget.Tab {
|
||||
tabs = true
|
||||
}
|
||||
}
|
||||
if tabs {
|
||||
// window is tabs. Don't show it as a standard button
|
||||
w.frame = false
|
||||
n.hasTabs = true
|
||||
} else {
|
||||
w.frame = false
|
||||
n.hasTabs = false
|
||||
}
|
||||
|
||||
n.gocuiSetWH(nextW, nextH)
|
||||
n.deleteView()
|
||||
n.showView()
|
||||
|
||||
sizeW := w.Width() + me.WindowPadW
|
||||
sizeH := w.Height()
|
||||
nextW += sizeW
|
||||
log.Log(NOW, "redoWindows() start nextW,H =", nextW, nextH, "gocuiSize.W,H =", sizeW, sizeH, n.progname)
|
||||
|
||||
if n.hasTabs {
|
||||
n.redoTabs(me.TabW, me.TabH)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *node) redoTabs(nextW int, nextH int) {
|
||||
for _, n := range p.children {
|
||||
if n.WidgetType != widget.Tab {
|
||||
continue
|
||||
}
|
||||
w := n.tk
|
||||
w.frame = true
|
||||
|
||||
n.gocuiSetWH(nextW, nextH)
|
||||
n.deleteView()
|
||||
// setCurrentTab(n)
|
||||
// if (len(w.cuiName) < 4) {
|
||||
// w.cuiName = "abcd"
|
||||
// }
|
||||
|
||||
n.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)
|
||||
nextW += sizeW
|
||||
}
|
||||
}
|
|
@ -0,0 +1,233 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/awesome-gocui/gocui"
|
||||
|
||||
"go.wit.com/lib/widget"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
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 (n *node) textResize() bool {
|
||||
w := n.tk
|
||||
var width, height int = 0, 0
|
||||
var changed bool = false
|
||||
|
||||
for i, s := range splitLines(n.tk.label) {
|
||||
log.Log(INFO, "textResize() len =", len(s), i, s)
|
||||
if width < len(s) {
|
||||
width = len(s)
|
||||
}
|
||||
height += 1
|
||||
}
|
||||
if w.gocuiSize.w1 != w.gocuiSize.w0+width+me.FramePadW {
|
||||
w.gocuiSize.w1 = w.gocuiSize.w0 + width + me.FramePadW
|
||||
changed = true
|
||||
}
|
||||
if w.gocuiSize.h1 != w.gocuiSize.h0+height+me.FramePadH {
|
||||
w.gocuiSize.h1 = w.gocuiSize.h0 + height + me.FramePadH
|
||||
changed = true
|
||||
}
|
||||
if changed {
|
||||
n.showWidgetPlacement(true, "textResize() changed")
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
func (n *node) hideView() {
|
||||
n.SetVisible(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() {
|
||||
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)
|
||||
}
|
||||
|
||||
// if the gocui element doesn't exist, create it
|
||||
if w.v == nil {
|
||||
n.recreateView()
|
||||
}
|
||||
x0, y0, x1, y1, err := me.baseGui.ViewPosition(w.cuiName)
|
||||
log.Log(INFO, "showView() w.v already defined for widget", n.progname, err)
|
||||
|
||||
// n.smartGocuiSize()
|
||||
changed := n.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)
|
||||
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()
|
||||
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()
|
||||
return
|
||||
}
|
||||
if x1 != w.gocuiSize.w1 {
|
||||
log.Log(ERROR, "showView() too wide", w.cuiName, "w,w", w.gocuiSize.w1, x1)
|
||||
n.recreateView()
|
||||
return
|
||||
}
|
||||
if y1 != w.gocuiSize.h1 {
|
||||
log.Log(ERROR, "showView() too high", w.cuiName, "h,h", w.gocuiSize.h1, y1)
|
||||
n.recreateView()
|
||||
return
|
||||
}
|
||||
|
||||
n.SetVisible(true)
|
||||
}
|
||||
|
||||
// create or recreate the gocui widget visible
|
||||
// deletes the old view if it exists and recreates it
|
||||
func (n *node) recreateView() {
|
||||
var err error
|
||||
w := n.tk
|
||||
log.Log(ERROR, "recreateView() START", n.WidgetType, n.progname)
|
||||
if me.baseGui == nil {
|
||||
log.Log(ERROR, "recreateView() ERROR: me.baseGui == nil", w)
|
||||
return
|
||||
}
|
||||
|
||||
// this deletes the button from gocui
|
||||
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)
|
||||
}
|
||||
|
||||
a := w.gocuiSize.w0
|
||||
b := w.gocuiSize.h0
|
||||
c := w.gocuiSize.w1
|
||||
d := w.gocuiSize.h1
|
||||
|
||||
w.v, err = me.baseGui.SetView(w.cuiName, a, b, c, d, 0)
|
||||
if err == nil {
|
||||
n.showWidgetPlacement(true, "recreateView()")
|
||||
log.Log(ERROR, "recreateView() internal plugin error err = nil")
|
||||
return
|
||||
}
|
||||
if !errors.Is(err, gocui.ErrUnknownView) {
|
||||
n.showWidgetPlacement(true, "recreateView()")
|
||||
log.Log(ERROR, "recreateView() internal plugin error error.IS()", err)
|
||||
return
|
||||
}
|
||||
|
||||
// this sets up the keybinding for the name of the window
|
||||
// does this really need to be done? I think we probably already
|
||||
// know everything about where all the widgets are so we could bypass
|
||||
// the gocui package and just handle all the mouse events internally here (?)
|
||||
// for now, the w.v.Name is a string "1", "2", "3", etc from the widgetId
|
||||
|
||||
// set the binding for this gocui view now that it has been created
|
||||
// gocui handles overlaps of views so it will run on the view that is clicked on
|
||||
me.baseGui.SetKeybinding(w.v.Name(), gocui.MouseLeft, gocui.ModNone, click)
|
||||
|
||||
// this actually sends the text to display to gocui
|
||||
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)
|
||||
// n.dumpWidget("jwc 2")
|
||||
|
||||
// if you don't do this here, it will be black & white only
|
||||
if w.color != nil {
|
||||
w.v.FrameColor = w.color.frame
|
||||
w.v.FgColor = w.color.fg
|
||||
w.v.BgColor = w.color.bg
|
||||
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)
|
||||
}
|
||||
log.Log(ERROR, "recreateView() END")
|
||||
}
|
||||
|
||||
func (n *node) hideWidgets() {
|
||||
w := n.tk
|
||||
w.isCurrent = false
|
||||
switch n.WidgetType {
|
||||
case widget.Root:
|
||||
case widget.Flag:
|
||||
case widget.Window:
|
||||
case widget.Box:
|
||||
case widget.Grid:
|
||||
default:
|
||||
n.hideView()
|
||||
}
|
||||
for _, child := range n.children {
|
||||
child.hideWidgets()
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) hideFake() {
|
||||
w := n.tk
|
||||
if w.isFake {
|
||||
n.hideView()
|
||||
}
|
||||
for _, child := range n.children {
|
||||
child.hideFake()
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) showFake() {
|
||||
w := n.tk
|
||||
if w.isFake {
|
||||
n.setFake()
|
||||
n.showWidgetPlacement(true, "showFake:")
|
||||
n.showView()
|
||||
}
|
||||
for _, child := range n.children {
|
||||
child.showFake()
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) showWidgets() {
|
||||
w := n.tk
|
||||
if w.isFake {
|
||||
// don't display by default
|
||||
} else {
|
||||
n.showWidgetPlacement(true, "current:")
|
||||
n.showView()
|
||||
}
|
||||
for _, child := range n.children {
|
||||
child.showWidgets()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"go.wit.com/lib/widget"
|
||||
"go.wit.com/log"
|
||||
)
|
||||
|
||||
func initWidget(n *node) *guiWidget {
|
||||
var w *guiWidget
|
||||
w = new(guiWidget)
|
||||
// Set(w, "default")
|
||||
|
||||
w.frame = true
|
||||
|
||||
// set the name used by gocui to the id
|
||||
w.cuiName = string(n.WidgetId)
|
||||
|
||||
if n.WidgetType == widget.Root {
|
||||
log.Log(INFO, "setupWidget() FOUND ROOT w.id =", n.WidgetId)
|
||||
n.WidgetId = 0
|
||||
me.rootNode = n
|
||||
return w
|
||||
}
|
||||
|
||||
if n.WidgetType == widget.Grid {
|
||||
w.widths = make(map[int]int) // how tall each row in the grid is
|
||||
w.heights = make(map[int]int) // how wide each column in the grid is
|
||||
}
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
func setupCtrlDownWidget() {
|
||||
a := new(widget.Action)
|
||||
a.ProgName = "ctrlDown"
|
||||
a.WidgetType = widget.Dialog
|
||||
a.WidgetId = -1
|
||||
a.ParentId = 0
|
||||
n := addNode(a)
|
||||
|
||||
me.ctrlDown = n
|
||||
}
|
||||
|
||||
func (n *node) deleteView() {
|
||||
w := n.tk
|
||||
if w.v != nil {
|
||||
w.v.Visible = false
|
||||
return
|
||||
}
|
||||
// make sure the view isn't really there
|
||||
me.baseGui.DeleteView(w.cuiName)
|
||||
w.v = nil
|
||||
}
|
||||
|
||||
// searches the binary tree for a WidgetId
|
||||
func (n *node) findWidgetName(name string) *node {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if n.tk.cuiName == name {
|
||||
return n
|
||||
}
|
||||
|
||||
for _, child := range n.children {
|
||||
newN := child.findWidgetName(name)
|
||||
if newN != nil {
|
||||
return newN
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *node) IsCurrent() bool {
|
||||
w := n.tk
|
||||
if n.WidgetType == widget.Tab {
|
||||
return w.isCurrent
|
||||
}
|
||||
if n.WidgetType == widget.Window {
|
||||
return w.isCurrent
|
||||
}
|
||||
if n.WidgetType == widget.Root {
|
||||
return false
|
||||
}
|
||||
return n.parent.IsCurrent()
|
||||
}
|
||||
|
||||
func (n *node) Visible() bool {
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
if n.tk == nil {
|
||||
return false
|
||||
}
|
||||
if n.tk.v == nil {
|
||||
return false
|
||||
}
|
||||
return n.tk.v.Visible
|
||||
}
|
||||
|
||||
func (n *node) SetVisible(b bool) {
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
if n.tk == nil {
|
||||
return
|
||||
}
|
||||
if n.tk.v == nil {
|
||||
return
|
||||
}
|
||||
n.tk.v.Visible = b
|
||||
}
|
||||
|
||||
func addDropdown() *node {
|
||||
n := new(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
|
||||
|
||||
// set the name used by gocui to the id
|
||||
n.tk.cuiName = "-1 dropbox"
|
||||
|
||||
n.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)
|
||||
}
|
||||
return n
|
||||
}
|
Loading…
Reference in New Issue