diff --git a/.gitignore b/.gitignore index 6dafe23..239f615 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,9 @@ examples/textbox/textbox examples/cloudflare/cloudflare examples/*/helloconsole +# protobuf compiled files +protobuf/*.pb.go + # temporary files when building debian packages /*.deb /files diff --git a/common.go b/common.go index 6f42ce4..462f37d 100644 --- a/common.go +++ b/common.go @@ -170,6 +170,13 @@ func (n *Node) Unpad() *Node { return n } +func (n *Node) Expand() *Node { + a := newAction(n, toolkit.Pad) + a.Expand = true + sendAction(a) + return n +} + // is this better? // yes, this is better. it allows Internationalization very easily // me.window = myGui.New2().Window("DNS and IPv6 Control Panel").Standard() @@ -182,8 +189,9 @@ func (n *Node) Window(title string) *Node { // This should not really do anything. as per the docs, the "Standard()" way // should be the default way +/* func (n *Node) Standard() *Node { - log(debugError, "Standard() not implemented yet") + log(debugInfo, "Standard() not implemented yet") return n } @@ -191,3 +199,4 @@ func (n *Node) SetMargin() *Node { log(debugError, "DoMargin() not implemented yet") return n } +*/ diff --git a/examples/cloudflare/api.go b/examples/cloudflare/api.go index 8cf550e..9522b07 100644 --- a/examples/cloudflare/api.go +++ b/examples/cloudflare/api.go @@ -4,10 +4,11 @@ package main import ( "os" "log" + "fmt" "encoding/json" "io/ioutil" "net/http" - "strconv" +// "strconv" "bytes" "github.com/davecgh/go-spew/spew" @@ -19,7 +20,17 @@ func doChange(dnsRow *RRT) { log.Println("Content", dnsRow.Content, "vs", dnsRow.valueNode.S) if (dnsRow.Content != dnsRow.valueNode.S) { log.Println("UPDATE VALUE", dnsRow.nameNode.Name, dnsRow.typeNode.Name, "to", dnsRow.valueNode.S) - httpPut(dnsRow) + stuff, result := httpPut(dnsRow) + if (dnsRow.curlNode != nil) { + pretty, _ := formatJSON(stuff) + log.Println("http PUT curl =", pretty) + dnsRow.curlNode.SetText(pretty) + } + if (dnsRow.resultNode != nil) { + pretty, _ := formatJSON(result) + log.Println("http PUT result =", pretty) + dnsRow.resultNode.SetText(pretty) + } } dnsRow.saveNode.Disable() } @@ -73,24 +84,26 @@ func getZonefile(c *configT) *DNSRecords { go.wit.com. 3600 IN A 1.1.1.9 test.wit.com. 3600 IN NS ns1.wit.com. */ -func httpPut(dnsRow *RRT) { - var url string = cloudflareURL + os.Getenv("CLOUDFLARE_ZONEID") + "/dns_records/" + dnsRow.ID - var authKey string = os.Getenv("CLOUDFLARE_AUTHKEY") - var email string = os.Getenv("CLOUDFLARE_EMAIL") +func httpPut(dnsRow *RRT) (string, string) { + var url string = cloudflareURL + os.Getenv("CF_API_ZONEID") + "/dns_records/" + dnsRow.ID + var authKey string = os.Getenv("CF_API_KEY") + var email string = os.Getenv("CF_API_EMAIL") - // make a json record to send on port 90 to cloudflare + // make a json record to send on port 80 to cloudflare var tmp string tmp = `{"content": "` + dnsRow.valueNode.S + `", ` tmp += `"name": "` + dnsRow.Name + `", ` tmp += `"type": "` + dnsRow.Type + `", ` - tmp+= `"ttl": "` + strconv.Itoa(dnsRow.TTL) + `", ` + tmp+= `"ttl": "` + "1" + `", ` tmp += `"comment": "WIT DNS Control Panel"` tmp += `}` data := []byte(tmp) log.Println("http PUT url =", url) - log.Println("http PUT data =", data) - spew.Dump(data) + // log.Println("http PUT data =", data) + // spew.Dump(data) + pretty, _ := formatJSON(string(data)) + log.Println("http PUT data =", pretty) req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(data)) @@ -103,19 +116,19 @@ func httpPut(dnsRow *RRT) { resp, err := client.Do(req) if err != nil { log.Println(err) - return + return tmp, fmt.Sprintf("blah err =", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Println(err) - return + return tmp, fmt.Sprintf("blah err =", err) } - log.Println("http PUT body =", body) - spew.Dump(body) + // log.Println("http PUT body =", body) + // spew.Dump(body) - return + return tmp, string(body) } // https://api.cloudflare.com/client/v4/zones @@ -185,3 +198,22 @@ func getZones(auth, email string) *DNSRecords { return &records } + +// formatJSON takes an unformatted JSON string and returns a formatted version. +func formatJSON(unformattedJSON string) (string, error) { + var jsonData interface{} + + // Decode the JSON string into an interface + err := json.Unmarshal([]byte(unformattedJSON), &jsonData) + if err != nil { + return "", err + } + + // Re-encode the JSON with indentation for formatting + formattedJSON, err := json.MarshalIndent(jsonData, "", " ") + if err != nil { + return "", err + } + + return string(formattedJSON), nil +} diff --git a/examples/cloudflare/curl.sh b/examples/cloudflare/curl.sh old mode 100644 new mode 100755 index ec8c014..1edd53e --- a/examples/cloudflare/curl.sh +++ b/examples/cloudflare/curl.sh @@ -1,3 +1,28 @@ -curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \ - -H "Authorization: Bearer AAAPutYourTokenInHereSoYouCanTestItL5Cl3" \ - -H "Content-Type:application/json" +#curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \ +# -H "Authorization: Bearer AAAPutYourTokenInHereSoYouCanTestItL5Cl3" \ +# -H "Content-Type:application/json" + +# https://api.cloudflare.com/client/v4/zones/27b900d9e05cfb9f3a64fecff2497f90/dns_records +# +# { +# "comment": "WIT DNS Control Panel", +# "content": "2001:4860:4860::8888", +# "name": "www", +# "proxied": false, +# "ttl": 3600, +# "type": "AAAA" +#} + +curl --request POST \ + --url https://api.cloudflare.com/client/v4/zones/27b900d9e05cfb9f3a64fecff2497f90/dns_records \ + --header 'Content-Type: application/json' \ + --header 'X-Auth-Key: e08806ad85ef97aebaacd2d7fa462a7d417a7x' \ + --header 'X-Auth-Email: basilarchia@gmail.com' \ + --data '{ + "comment": "WIT DNS Control Panel", + "content": "2001:4860:4860::5555", + "name": "www5", + "proxied": false, + "ttl": 3600, + "type": "AAAA" +}' diff --git a/examples/cloudflare/gui.go b/examples/cloudflare/gui.go index 60fd5fe..ed08482 100644 --- a/examples/cloudflare/gui.go +++ b/examples/cloudflare/gui.go @@ -4,6 +4,8 @@ package main import ( "log" "strconv" + + "git.wit.org/jcarr/control-panel-dns/cloudflare" ) func loadDNS(c *configT) { @@ -11,7 +13,8 @@ func loadDNS(c *configT) { log.Println("adding DNS record", hostname) newt := mainWindow.NewTab(hostname) - newg := newt.NewGroup("more") + vb := newt.NewBox("vBox", false) + newg := vb.NewGroup("more zoneID = " + c.zoneID) // make a grid 6 things wide grid := newg.NewGrid("gridnuts", 6, gridH) @@ -19,41 +22,22 @@ func loadDNS(c *configT) { // grid.NewButton("Type", func () { // log.Println("sort by Type") // }) - typedrop := grid.NewDropdown("type") - typedrop.AddText("A") - typedrop.AddText("AAAA") - typedrop.AddText("CNAME") - typedrop.Custom = func () { - log.Println("custom dropdown() a =", typedrop.Name, typedrop.S) - } - nb := grid.NewButton("Name", func () { - log.Println("sort by Name") - }) - nb.Disable() + grid.NewLabel("RR type") + grid.NewLabel("hostname") - grid.NewButton("Protection", func () { - log.Println("sort proxied") - }) - grid.NewButton("TTL", func () { - log.Println("sort by TTL") - }) - nb = grid.NewButton("Value", func () { - log.Println("sort by Value") - }) - nb.Disable() - nb = grid.NewButton("Save", func () { - log.Println("click below to save") - }) - nb.Disable() + grid.NewLabel("Proxy") + grid.NewLabel("TTL") + grid.NewLabel("Value") + grid.NewLabel("Save") - masterSave = newt.NewButton("Master Save", func () { + masterSave = vb.NewButton("Master Save", func () { log.Println("save stuff to cloudflare") }) masterSave.Disable() records := getZonefile(c) for _, record := range records.Result { - var rr RRT // dns zonefile resource record + var rr cloudflare.RRT // dns zonefile resource record // copy all the JSON values into the row record. rr.ID = record.ID @@ -62,59 +46,34 @@ func loadDNS(c *configT) { rr.Content = record.Content rr.Proxied = record.Proxied rr.Proxiable = record.Proxiable - rr.TTL = record.TTL + // rr.Ttl = record.TTL - rr.typeNode = grid.NewLabel(record.Type) - rr.nameNode = grid.NewEntryLine(record.Name) - rr.nameNode.SetText(record.Name) - rr.nameNode.Disable() + grid.NewLabel(record.Type) + grid.NewLabel(record.Name) - // set proxy or unproxied - rr.proxyNode = grid.NewDropdown("proxy") + proxy := grid.NewLabel("proxy") if (record.Proxied) { - rr.proxyNode.AddText("Proxied") - rr.proxyNode.AddText("DNS") + proxy.SetText("On") } else { - rr.proxyNode.AddText("DNS") - rr.proxyNode.AddText("Proxied") - } - rr.proxyNode.Custom = func () { - log.Println("proxy dropdown() a =", rr.proxyNode.Name, rr.proxyNode.S, rr.ID) - rr.saveNode.Enable() - masterSave.Enable() + proxy.SetText("Off") } - var ttl, short string + var ttl string if (record.TTL == 1) { ttl = "Auto" } else { ttl = strconv.Itoa(record.TTL) } - rr.ttlNode = grid.NewLabel(ttl) - // short = fmt.Sprintf("%80s", record.Content) - short = record.Content - if len(short) > 40 { - short = short[:40] // Slice the first 20 characters - } + grid.NewLabel(ttl) - rr.valueNode = grid.NewEntryLine(short) - rr.valueNode.SetText(record.Content) + val := grid.NewLabel("Value") + val.SetText(record.Content) - rr.valueNode.Custom = func () { - log.Println("value changed =", rr.valueNode.Name, rr.proxyNode.S, rr.ID) - rr.saveNode.Enable() - masterSave.Enable() - } - - // fmt.Printf("ID: %s, Type: %s, Name: %s, short Content: %s\n", record.ID, record.Type, record.Name, short) - // fmt.Printf("\tproxied: %b, %b, string TTL: %i\n", record.Proxied, record.Proxiable, ttl) - - rr.saveNode = grid.NewButton("Save", nil) - rr.saveNode.Disable() - rr.saveNode.Custom = func () { + load := grid.NewButton("Load", nil) + load.Custom = func () { name := "save stuff to cloudflare for " + rr.ID log.Println(name) - doChange(&rr) + // doChange(&rr) } } diff --git a/examples/cloudflare/main.go b/examples/cloudflare/main.go index 4e72668..aeb8570 100644 --- a/examples/cloudflare/main.go +++ b/examples/cloudflare/main.go @@ -7,7 +7,9 @@ import ( "log" "bufio" "strings" + "git.wit.org/wit/gui" + "git.wit.org/jcarr/control-panel-dns/cloudflare" ) var title string = "Cloudflare DNS Control Panel" @@ -44,7 +46,8 @@ func makeCloudflareWindow() { makeConfigTab(mainWindow) t = mainWindow.NewTab("Zones") - g1 := t.NewGroup("zones") + vb := t.NewBox("vBox", false) + g1 := vb.NewGroup("zones") // make dropdown list of zones zonedrop = g1.NewDropdown("zone") @@ -52,6 +55,7 @@ func makeCloudflareWindow() { for d, _ := range config { zonedrop.AddText(d) } + zonedrop.AddText("stablesid.org") zonedrop.Custom = func () { domain := zonedrop.S @@ -82,18 +86,18 @@ func makeConfigTab(window *gui.Node) { vb := t.NewBox("vBox", false) g1 := vb.NewGroup("Cloudflare API Config") - g1.NewLabel("If you have an API key with access to list all of /n your zone files, enter it here. \n \n Alternatively, you can set the enviroment variables: \n env $CLOUDFLARE_AUTHKEY \n env $CLOUDFLARE_EMAIL \n env $CLOUDFLARE_URL \n") + g1.NewLabel("If you have an API key with access to list all of /n your zone files, enter it here. \n \n Alternatively, you can set the enviroment variables: \n env $CF_API_KEY \n env $CF_API_EMAIL\n") // make grid to display credentials grid := g1.NewGrid("credsGrid", 2, 4) // width = 2 grid.NewLabel("Auth Key") - aw := grid.NewEntryLine("CLOUDFLARE_AUTHKEY") - aw.SetText(os.Getenv("CLOUDFLARE_AUTHKEY")) + aw := grid.NewEntryLine("CF_API_KEY") + aw.SetText(os.Getenv("CF_API_KEY")) grid.NewLabel("Email") - ew := grid.NewEntryLine("CLOUDFLARE_EMAIL") - ew.SetText(os.Getenv("CLOUDFLARE_EMAIL")) + ew := grid.NewEntryLine("CF_API_EMAIL") + ew.SetText(os.Getenv("CF_API_EMAIL")) var url string = "https://api.cloudflare.com/client/v4/zones/" grid.NewLabel("Cloudflare API") @@ -106,6 +110,10 @@ func makeConfigTab(window *gui.Node) { getZones(aw.S, ew.S) }) + vb.NewButton("cloudflare wit.com", func () { + cloudflare.CreateRR(myGui, "wit.com", "3777302ac4a78cd7fa4f6d3f72086d06") + }) + t.Pad() t.Margin() vb.Pad() @@ -144,16 +152,16 @@ func showCloudflareCredentials(box *gui.Node) { grid := box.NewGrid("credsGrid", 2, 4) // width = 2 grid.NewLabel("Domain") - domainWidget = grid.NewEntryLine("CLOUDFLARE_DOMAIN") + domainWidget = grid.NewEntryLine("CF_API_DOMAIN") grid.NewLabel("Zone ID") - zoneWidget = grid.NewEntryLine("CLOUDFLARE_ZONEID") + zoneWidget = grid.NewEntryLine("CF_API_ZONEID") grid.NewLabel("Auth Key") - authWidget = grid.NewEntryLine("CLOUDFLARE_AUTHKEY") + authWidget = grid.NewEntryLine("CF_API_KEY") grid.NewLabel("Email") - emailWidget = grid.NewEntryLine("CLOUDFLARE_EMAIL") + emailWidget = grid.NewEntryLine("CF_API_EMAIL") var url string = "https://api.cloudflare.com/client/v4/zones/" grid.NewLabel("Cloudflare API") @@ -161,10 +169,6 @@ func showCloudflareCredentials(box *gui.Node) { grid.Pad() - saveButton = box.NewButton("Save to config", func () { - }) - saveButton.Disable() - loadButton = box.NewButton("Load Cloudflare DNS zonefile", func () { var domain configT domain.domain = domainWidget.S diff --git a/examples/cloudflare/structs.go b/examples/cloudflare/structs.go index af4d7f3..50647d7 100644 --- a/examples/cloudflare/structs.go +++ b/examples/cloudflare/structs.go @@ -34,20 +34,23 @@ var zonedrop *gui.Node // Resource Record (used in a DNS zonefile) type RRT struct { - typeNode *gui.Node - nameNode *gui.Node - proxyNode *gui.Node - ttlNode *gui.Node - valueNode *gui.Node - saveNode *gui.Node + typeNode *gui.Node // CNAME, A, AAAA, ... + nameNode *gui.Node // www, mail, ... + proxyNode *gui.Node // If cloudflare is a port 80 & 443 proxy + ttlNode *gui.Node // just set to 1 which means automatic to cloudflare + valueNode *gui.Node // 4.2.2.2, "dkim stuff", etc + curlNode *gui.Node // shows you what you could run via curl + resultNode *gui.Node // what the cloudflare API returned + saveNode *gui.Node // button to send it to cloudflare ID string Type string Name string Content string + ProxyS string Proxied bool Proxiable bool - TTL int + Ttl string } /* diff --git a/main.go b/main.go index 03479bba..f529539 100644 --- a/main.go +++ b/main.go @@ -37,17 +37,17 @@ func init() { func watchCallback() { log(logInfo, "watchCallback() START") for { - log(logNow, "watchCallback() restarted select for toolkit user events") + log(logInfo, "watchCallback() restarted select for toolkit user events") select { case a := <-me.guiChan: if (a.ActionType == toolkit.UserQuit) { - log(logNow, "doUserEvent() User sent Quit()") + log(logInfo, "doUserEvent() User sent Quit()") me.rootNode.doCustom() exit("wit/gui toolkit.UserQuit") break } if (a.ActionType == toolkit.EnableDebug) { - log(logNow, "doUserEvent() Enable Debugging Window") + log(logInfo, "doUserEvent() Enable Debugging Window") DebugWindow() break } @@ -56,7 +56,7 @@ func watchCallback() { if (n == nil) { log(logError, "watchCallback() UNKNOWN widget id =", a.WidgetId, a.Name) } else { - log(logNow, "watchCallback() FOUND widget id =", n.id, n.Name) + log(logInfo, "watchCallback() FOUND widget id =", n.id, n.Name) n.doUserEvent(a) } // this maybe a good idea? @@ -67,7 +67,7 @@ func watchCallback() { } func (n *Node) doCustom() { - log(logNow, "doUserEvent() widget =", n.id, n.Name, n.WidgetType, n.B) + log(logInfo, "doUserEvent() widget =", n.id, n.Name, n.WidgetType, n.B) if (n.Custom == nil) { log(debugError, "Custom() = nil. SKIPPING") return @@ -76,40 +76,40 @@ func (n *Node) doCustom() { } func (n *Node) doUserEvent(a toolkit.Action) { - log(logNow, "doUserEvent() node =", n.id, n.Name) + log(logInfo, "doUserEvent() node =", n.id, n.Name) switch n.WidgetType { case toolkit.Checkbox: n.B = a.B - log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.B) + log(logInfo, "doUserEvent() node =", n.id, n.Name, "set to:", n.B) n.doCustom() case toolkit.Button: - log(logNow, "doUserEvent() node =", n.id, n.Name, "button clicked") + log(logInfo, "doUserEvent() node =", n.id, n.Name, "button clicked") n.doCustom() case toolkit.Combobox: n.S = a.S - log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.S) + log(logInfo, "doUserEvent() node =", n.id, n.Name, "set to:", n.S) n.doCustom() case toolkit.Dropdown: n.S = a.S - log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.S) + log(logInfo, "doUserEvent() node =", n.id, n.Name, "set to:", n.S) n.doCustom() case toolkit.Textbox: n.S = a.S - log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.S) + log(logInfo, "doUserEvent() node =", n.id, n.Name, "set to:", n.S) n.doCustom() case toolkit.Spinner: n.I = a.I - log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.I) + log(logInfo, "doUserEvent() node =", n.id, n.Name, "set to:", n.I) n.doCustom() case toolkit.Slider: n.I = a.I - log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.I) + log(logInfo, "doUserEvent() node =", n.id, n.Name, "set to:", n.I) n.doCustom() case toolkit.Window: - log(logNow, "doUserEvent() node =", n.id, n.Name, "window closed") + log(logInfo, "doUserEvent() node =", n.id, n.Name, "window closed") n.doCustom() default: - log(logNow, "doUserEvent() type =", n.WidgetType) + log(logInfo, "doUserEvent() type =", n.WidgetType) } } diff --git a/protobuf/Makefile b/protobuf/Makefile new file mode 100644 index 0000000..7dd70be --- /dev/null +++ b/protobuf/Makefile @@ -0,0 +1,34 @@ +all: + # You must use the current protoc-gen-go + # protoc --version 3.6++ does not mean that protoc will generate version3 .go files + # + # apt remove golang-goprotobuf-dev + # apt install protobuf-compiler + # + # Then: + # go get -u github.com/golang/protobuf/protoc-gen-go + # cd ~/go/src/github.com/golang/protobuf/protoc-gen-go + # go install + # + # Then: + protoc --version + make widget.pb.go + +clean: + rm -f *.pb.go + +widget.pb.go: widget.proto + protoc --go_out=. widget.proto + +compile: + protoc --go_out=. *.proto + +deps: + apt install golang-goprotobuf-dev + apt install protobuf-compiler + +push: + git pull + git add --all + git commit -a -s + git push diff --git a/protobuf/widget.proto b/protobuf/widget.proto new file mode 100644 index 0000000..e20354b --- /dev/null +++ b/protobuf/widget.proto @@ -0,0 +1,117 @@ +syntax = "proto3"; +package guiProtobuf; + +message Action { + WidgetType widgetType = 1; + ActionType actionType = 2; + int64 widgetId = 3; + int64 parentId = 4; + string text = 5; // what is visable to the user + string name = 6; // a name useful for programming + + // This is how the values are passed back and forth + // values from things like checkboxes & dropdown's + bool b = 7; + int64 i = 8; + string s = 9; + + // This is used for things like a slider(0,100) + int64 x = 10; + int64 y = 11; + + // This is for the grid size & widget position + int64 w = 12; + int64 h = 13; + int64 atw = 14; + int64 ath = 15; + + bool margin = 16; // Put space around elements to improve look & feel + bool expand = 17; // Make widgets fill up the space available + + repeated Response results = 18; + repeated Network networks = 19; + repeated VM vms = 20; + + enum WidgetType { + Unknown = 0; + Root = 1; // the master 'root' node of the binary tree + Flag = 2; // used to send configuration values to plugins + Window = 3; // in certain gui's (ncurses), these are tabs + Tab = 4; // internally, this is a window + Frame = 5; // deprecate? + Grid = 6; // like drawers in a chest + Group = 7; // like the 'Appetizers' section on a menu + Box = 8; // a vertical or horizontal stack of widgets + Button = 9; + Checkbox = 10; // select 'on' or 'off' + Dropdown = 11; + Combobox = 12; // dropdown with edit=true + Label = 13; + Textbox = 14; // is this a Label with edit=true + Slider = 15; // like a progress bar + Spinner = 16; // like setting the oven temperature + Separator = 17; // TODO + Image = 18; // TODO + Area = 19; // TODO + Form = 20; // TODO + Font = 21; // TODO + Color = 22; // TODO + Dialog = 23; // TODO + Stdout = 24; // can be used to capture and display log output + } + + enum ActionType { + Health = 0; + Add = 1; + Delete = 2; + Get = 3; + Set = 4; + GetText = 5; + SetText = 6; + AddText = 7; + Show = 8; + Hide = 9; + Enable = 10; + Disable = 11; + Margin = 12; + Unmargin = 13; + Pad = 14; + Unpad = 15; + Append = 16; + Move = 17; + Dump = 18; + User = 19; // the user did something (mouse, keyboard, etc) + InitToolkit = 20; // initializes the toolkit + CloseToolkit = 21; // closes the toolkit + UserQuit = 22; // the user closed the GUI + EnableDebug = 23; // open the debugging window + } + + message Response { + // ActionType type = 1; + int64 id = 2; + string name = 3; + string error = 4; + repeated string snippets = 5; + } + + message Network { + int64 id = 1; + string name = 2; + int64 total_cpu = 3; + int64 total_mem = 4; + string login_url = 5; + } + + message VM { + int64 id = 1; + string name = 2; + string hostname = 3; + int64 cpus = 4; + int64 memory = 5; + int64 disk = 6; + string IPv6 = 7; + string role = 8; + string baseImage = 9; + } +} diff --git a/toolkit/andlabs/action.go b/toolkit/andlabs/action.go index 29b797a..5352fc8 100644 --- a/toolkit/andlabs/action.go +++ b/toolkit/andlabs/action.go @@ -38,11 +38,11 @@ func (n *node) enable(b bool) { } func (n *node) pad(at toolkit.ActionType) { - log(debugError, "pad()") + log(logInfo, "pad() on WidgetId =", n.WidgetId) t := n.tk if (t == nil) { - log(debugError, "pad() toolkit struct == nil. for", n.WidgetId) + log(logError, "pad() toolkit struct == nil. for", n.WidgetId) return } diff --git a/toolkit/andlabs/add.go b/toolkit/andlabs/add.go index 9963b79..82d962f 100644 --- a/toolkit/andlabs/add.go +++ b/toolkit/andlabs/add.go @@ -127,15 +127,16 @@ func (p *node) place(n *node) bool { return true case toolkit.Tab: if (p.tk.uiTab == nil) { - log(logError, "p.tk.uiTab == nil", p.tk) + log(logError, "p.tk.uiTab == nil for n.WidgetId =", n.WidgetId, "p.tk =", p.tk) panic("p.tk.uiTab == nil") } if (n.tk.uiControl == nil) { - log(logError, "n.tk.uiControl == nil", n.tk) + log(logError, "n.tk.uiControl == nil for n.WidgetId =", n.WidgetId, "n.tk =", n.tk) panic("n.tk.uiControl == nil") } - log(logError, "THIS SHOULD NEVER HAPPEN ??????? trying to place() node=", n.WidgetId, n.Name, n.Text, n.WidgetType) - log(logError, "THIS SHOULD NEVER HAPPEN ??????? trying to place() on parent=", p.WidgetId, p.Name, p.Text, p.WidgetType) + log(logError, "CHECK LOGIC ON THIS. APPENDING directly into a window without a tab") + // log(logError, "THIS SHOULD NEVER HAPPEN ??????? trying to place() node=", n.WidgetId, n.Name, n.Text, n.WidgetType) + // log(logError, "THIS SHOULD NEVER HAPPEN ??????? trying to place() on parent=", p.WidgetId, p.Name, p.Text, p.WidgetType) // panic("n.tk.uiControl == nil") p.tk.uiTab.Append(n.Text, n.tk.uiControl) p.tk.boxC += 1 diff --git a/toolkit/andlabs/debug.go b/toolkit/andlabs/debug.go index 33f8c44..d9f8e58 100644 --- a/toolkit/andlabs/debug.go +++ b/toolkit/andlabs/debug.go @@ -14,12 +14,12 @@ 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 = true -var debugChange bool = true -var debugPlugin bool = true -var debugAction bool = true -var debugFlags bool = true -var debugGrid bool = true +var debugToolkit bool = false +var debugChange bool = false +var debugPlugin bool = false +var debugAction bool = false +var debugFlags bool = false +var debugGrid bool = false var debugNow bool = true var debugError bool = true diff --git a/toolkit/andlabs/main.go b/toolkit/andlabs/main.go index e2d34a3..3430769 100644 --- a/toolkit/andlabs/main.go +++ b/toolkit/andlabs/main.go @@ -40,13 +40,18 @@ func init() { // log(debugToolkit, "init() Setting defaultBehavior = true") setDefaultBehavior(true) + + // TODO: this is messed up. run ui.Main() from the first add? Initialize it with an empty thing first? + // fake out the OS toolkit by making a fake window. This is probably needed for macos & windows + // actually, this probably breaks the macos build + go ui.Main(func() { + demoUI() + }) + // andlabs = make(map[int]*andlabsT) pluginChan = make(chan toolkit.Action, 1) log(logNow, "Init() start channel reciever") - go ui.Main(func() { - demoUI() - }) go catchActionChannel() log(logNow, "Init() END") } diff --git a/toolkit/andlabs/updateui.go b/toolkit/andlabs/updateui.go index 4752d67..c43e15f 100644 --- a/toolkit/andlabs/updateui.go +++ b/toolkit/andlabs/updateui.go @@ -86,7 +86,8 @@ func demoUI() { messageLabel.SetText("") }) - mainWindow.Show() + // this is messed up. + // mainWindow.Show() } /* diff --git a/toolkit/gocui/debug.go b/toolkit/gocui/debug.go index 7512c1e..2669067 100644 --- a/toolkit/gocui/debug.go +++ b/toolkit/gocui/debug.go @@ -45,7 +45,8 @@ func (n *node) showWidgetPlacement(b bool, s string) { s1 += fmt.Sprintf("At(%2d,%2d) ", n.AtW, n.AtH) } } - log(b, s1, s, n.WidgetType, ",", n.Name) // , "text=", w.text) + tmp := "." + n.Name + "." + log(b, s1, s, n.WidgetType, ",", tmp) // , "text=", w.text) } func (n *node) dumpWidget(pad string) { diff --git a/toolkit/gocui/main.go b/toolkit/gocui/main.go index 04e1255..11f525a 100644 --- a/toolkit/gocui/main.go +++ b/toolkit/gocui/main.go @@ -43,7 +43,7 @@ func catchActionChannel() { log(logError,"ERROR: console did not initialize") continue } - log(logNow, "catchActionChannel()", a.WidgetId, a.ActionType, a.WidgetType, a.Name) + log(logInfo, "catchActionChannel()", a.WidgetId, a.ActionType, a.WidgetType, a.Name) action(&a) } } diff --git a/toolkit/gocui/plugin.go b/toolkit/gocui/plugin.go index eae811d..18cb71f 100644 --- a/toolkit/gocui/plugin.go +++ b/toolkit/gocui/plugin.go @@ -51,8 +51,22 @@ func action(a *toolkit.Action) { case toolkit.CloseToolkit: log(logNow, "attempting to close the plugin and release stdout and stderr") standardExit() + case toolkit.Enable: + if n.Visible() { + // widget was already shown + } else { + log(logInfo, "Setting Visable to true", a.Name) + n.SetVisible(true) + } + case toolkit.Disable: + if n.Visible() { + log(logInfo, "Setting Visable to false", a.Name) + n.SetVisible(false) + } else { + // widget was already hidden + } default: - log(logError, "action() Unknown =", a.ActionType, a.WidgetType, a.Name) + log(logError, "action() ActionType =", a.ActionType, "WidgetType =", a.WidgetType, "Name =", a.Name) } log(logInfo, "action() END") } @@ -70,16 +84,28 @@ func (n *node) AddText(text string) { } func (n *node) SetText(text string) { + var changed bool = false if (n == nil) { log(logNow, "widget is nil") return } - n.S = text - n.Text = text + if (n.Text != text) { + n.Text = text + changed = true + } + if (n.S != text) { + n.S = text + changed = true + } + if (! changed) { + return + } - n.textResize() - n.deleteView() - n.showView() + if (n.Visible()) { + n.textResize() + n.deleteView() + n.showView() + } } func (n *node) Set(val any) { diff --git a/toolkit/gocui/view.go b/toolkit/gocui/view.go index e69bf7c..28b80e0 100644 --- a/toolkit/gocui/view.go +++ b/toolkit/gocui/view.go @@ -20,20 +20,30 @@ func splitLines(s string) []string { return lines } -func (n *node) textResize() { +func (n *node) textResize() bool { w := n.tk var width, height int = 0, 0 + var changed bool = false for i, s := range splitLines(n.Text) { - log(logNow, "textResize() len =", len(s), i, s) + log(logInfo, "textResize() len =", len(s), i, s) if (width < len(s)) { width = len(s) } height += 1 } - w.gocuiSize.w1 = w.gocuiSize.w0 + width + me.FramePadW - w.gocuiSize.h1 = w.gocuiSize.h0 + height + me.FramePadH - n.showWidgetPlacement(logNow, "textResize()") + 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(logNow, "textResize() changed") + } + return changed } func (n *node) hideView() { @@ -58,17 +68,38 @@ func (n *node) showView() { x0, y0, x1, y1, err := me.baseGui.ViewPosition(w.cuiName) log(logInfo, "showView() w.v already defined for widget", n.Name, err) + // n.smartGocuiSize() + changed := n.textResize() + + if (changed) { + log(logNow, "showView() textResize() changed. Should recreateView here wId =", w.cuiName) + } else { + log(logNow, "showView() Clear() and Fprint() here wId =", w.cuiName) + w.v.Clear() + fmt.Fprint(w.v, n.Text) + 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) || (y0 != w.gocuiSize.h0) { - log(logError, "showView() w.v.w0 != x0", n.Name, w.gocuiSize.w0, x0) - log(logError, "showView() w.v.h0 != y0", n.Name, w.gocuiSize.h0, y0) + if (x0 != w.gocuiSize.w0) { n.recreateView() return } - if (x1 != w.gocuiSize.w1) || (y1 != w.gocuiSize.h1) { - log(logError, "showView() w.v.w1 != x1", n.Name, w.gocuiSize.w1, x1) - log(logError, "showView() w.v.h1 != y1", n.Name, w.gocuiSize.h1, y1) + if (y0 != w.gocuiSize.h0) { + log(logError, "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(logError, "showView() too wide", w.cuiName, "w,w", w.gocuiSize.w1, x1) + n.recreateView() + return + } + if (y1 != w.gocuiSize.h1) { + log(logError, "showView() too high", w.cuiName, "h,h", w.gocuiSize.h1, y1) n.recreateView() return }