From de771dbe985c82a566765e4d58264d7b8abf3c24 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Thu, 14 Dec 2023 10:36:56 -0600 Subject: [PATCH] tabs, windows + gocui dropdown menu (almost) dropdown menu figures out what text was clicked dropdown menu movement changes line colors dropdown menus force user to select a response accidentally committed a binary tab selection works tab and window views almost working tabs and windows almost working window widgets selection works better color handling using gocui view.Visable flag removal of old color setting code still need an artificial delay for andlabs SetText() catching more 'nil' errors fixed the stupid duplicate tab problem in andlabs figured out how andlabs had a tab/box mess works on more than one domain builds and runs again debugging double tabs in andlabs gui GO111MODULE compile notes code reorg further improvements example cloudflare app does first successful dns update add NewEntryLine() for single line entry boxes Signed-off-by: Jeff Carr --- .gitignore | 2 +- Makefile | 38 ++++--- button.go | 17 --- chan.go | 2 + debug.go | 5 +- examples/cloudflare/Makefile | 3 + examples/cloudflare/api.go | 187 +++++++++++++++++++++++++++++++++ examples/cloudflare/curl.sh | 3 + examples/cloudflare/dns.go | 132 ----------------------- examples/cloudflare/gui.go | 122 +++++++++++++++++++++ examples/cloudflare/main.go | 186 ++++++++++++++++++++++++++++---- examples/cloudflare/structs.go | 77 ++++++++++++++ main.go | 2 +- plugin.go | 7 +- tab.go | 3 +- textbox.go | 14 +++ toolkit/andlabs/action.go | 30 +++++- toolkit/andlabs/add.go | 3 + toolkit/andlabs/button.go | 3 +- toolkit/andlabs/debug.go | 42 +++++++- toolkit/andlabs/main.go | 5 +- toolkit/andlabs/setText.go | 16 ++- toolkit/andlabs/structs.go | 3 + toolkit/andlabs/tab.go | 29 +++-- toolkit/andlabs/textbox.go | 25 +++-- toolkit/gocui/Makefile | 4 +- toolkit/gocui/add.go | 13 ++- toolkit/gocui/click.go | 173 +++++++++++++++++++++--------- toolkit/gocui/color.go | 143 +++++++++++++------------ toolkit/gocui/common.go | 97 +++++++++++------ toolkit/gocui/gocui.go | 2 +- toolkit/gocui/help.go | 10 +- toolkit/gocui/keybindings.go | 27 ++++- toolkit/gocui/mouse.go | 66 +++++++++++- toolkit/gocui/showStdout.go | 1 + toolkit/gocui/structs.go | 21 +++- toolkit/gocui/tab.go | 4 + toolkit/gocui/view.go | 46 +++++--- toolkit/widget.go | 3 + 39 files changed, 1156 insertions(+), 410 deletions(-) create mode 100644 examples/cloudflare/api.go create mode 100644 examples/cloudflare/curl.sh delete mode 100644 examples/cloudflare/dns.go create mode 100644 examples/cloudflare/gui.go create mode 100644 examples/cloudflare/structs.go diff --git a/.gitignore b/.gitignore index 135bf8c..6dafe23 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ # ignore compiled plugins *.so -examples/buttonplugin/buttonplugin +examples/buttons/buttons examples/console-ui-helloworld/console-ui-helloworld examples/debug/debug examples/helloworld/helloworld diff --git a/Makefile b/Makefile index d0e7cfc..d37e551 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,19 @@ all: README.md @echo @echo This Requires working IPv6 @echo - @sleep 1 +ifeq ($(GO111MODULE),) + @echo + @echo If you are compiling this here, you probably want to set GO111MODULE + @echo + @echo Setting GO111MODULE means that the version you are compiling has plugins + @echo that get compiled against this current running version of the code + @echo Otherwise, the GO language plugins can complain about being compiled against + @echo mis-matched versions + @echo + @echo export GO111MODULE=off + @echo + sleep 3 +endif ifeq (,$(wildcard go.mod)) go mod init gui go mod tidy @@ -35,8 +47,11 @@ examples: \ examples-helloworld \ examples-buttons \ examples-console-ui-helloworld \ - examples-textbox \ - examples-debug + examples-cloudflare + +# this is the most basic one. This syntax should always work +examples-helloworld: + make -C examples/helloworld examples-buttons: make -C examples/buttons @@ -44,18 +59,8 @@ examples-buttons: examples-console-ui-helloworld: make -C examples/console-ui-helloworld -# this is the most basic one. This syntax should always work -examples-helloworld: - make -C examples/helloworld - -examples-debug: - -make -C examples/debug - -examples-textbox: - make -C examples/textbox - -examples-helloconsole: - make -C examples/plugin-consoleonly +examples-cloudflare: + -make -C examples/cloudflare # sync repo to the github backup # git remote add github git@github.com:witorg/gui.git @@ -102,3 +107,6 @@ objdump: log: reset tail -f /tmp/witgui.* /tmp/guilogfile + +submit-to-docs: + GOPROXY=https://proxy.golang.org GO111MODULE=on go get go.wit.com/gui@v1.0.0 diff --git a/button.go b/button.go index ccd7670..d4f8464 100644 --- a/button.go +++ b/button.go @@ -11,22 +11,6 @@ func (parent *Node) NewButton(name string, custom func()) *Node { return newNode } -/* -// deprecate this once andlabs is refactored -func callback(i int) bool { - log(debugError, "callback() for widget id =", i) - n := me.rootNode.FindId(i) - log(debugError, "callback() found node =", n) - // running custom here means the button get's clicked twice - if (n.Custom == nil) { - log(debugError, "callback() = nil. SKIPPING") - return false - } - n.Custom() - return true -} -*/ - // find widget by number func (n *Node) FindId(i int) (*Node) { if (n == nil) { @@ -45,4 +29,3 @@ func (n *Node) FindId(i int) (*Node) { } return nil } - diff --git a/chan.go b/chan.go index 54eb4e4..985ee63 100644 --- a/chan.go +++ b/chan.go @@ -14,6 +14,8 @@ import ( "github.com/sourcegraph/conc/panics" ) +// this should never exit +// TODO: clean up all this poorly named code func makeConc() { var wg conc.WaitGroup defer wg.Wait() diff --git a/debug.go b/debug.go index acc249d..1fe2fe8 100644 --- a/debug.go +++ b/debug.go @@ -140,9 +140,8 @@ func (n *Node) dumpWidget(b bool) string { for i := 0; i < listChildrenDepth; i++ { tabs = tabs + defaultPadding } - d = tabs + d - logindent(b, listChildrenDepth, defaultPadding, n.id, info) - return d + logindent(b, listChildrenDepth, defaultPadding, d) + return tabs + d } // func (n *Node) ListChildren(dump bool, dropdown *Node, mapNodes map[string]*Node) { diff --git a/examples/cloudflare/Makefile b/examples/cloudflare/Makefile index bcd88c6..fd82fdc 100644 --- a/examples/cloudflare/Makefile +++ b/examples/cloudflare/Makefile @@ -16,3 +16,6 @@ update: log: reset tail -f /tmp/witgui.* /tmp/guilogfile + +gocui: build + ./cloudflare -gui gocui >/tmp/witgui.log.stderr 2>&1 diff --git a/examples/cloudflare/api.go b/examples/cloudflare/api.go new file mode 100644 index 0000000..8cf550e --- /dev/null +++ b/examples/cloudflare/api.go @@ -0,0 +1,187 @@ +// This is a simple example +package main + +import ( + "os" + "log" + "encoding/json" + "io/ioutil" + "net/http" + "strconv" + "bytes" + + "github.com/davecgh/go-spew/spew" +) + +func doChange(dnsRow *RRT) { + log.Println("Look for changes in row", dnsRow.ID) + log.Println("Proxy", dnsRow.Proxied, "vs", dnsRow.proxyNode.S) + 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) + } + dnsRow.saveNode.Disable() +} + +func getZonefile(c *configT) *DNSRecords { + var url = cloudflareURL + c.zoneID + "/dns_records/" + log.Println("getZonefile()", c.domain, url) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + log.Println("http.NewRequest error:", err) + return nil + } + + // Set headers + req.Header.Set("X-Auth-Key", c.auth) + req.Header.Set("X-Auth-Email", c.email) + + log.Println("getZonefile() auth, email", c.auth, c.email) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Println("http.Client error:", err) + return nil + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Println("ioutil.ReadAll() error", err) + return nil + } + + var records DNSRecords + if err := json.Unmarshal(body, &records); err != nil { + log.Println("json.Unmarshal() error", err) + return nil + } + + log.Println("getZonefile() worked", records) + return &records +} + +/* + pass in a DNS Resource Records (the stuff in a zonefile) + + This will talk to the cloudflare API and generate a resource record in the zonefile: + + For example: + gitea.wit.com. 3600 IN CNAME git.wit.org. + 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") + + // make a json record to send on port 90 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 += `"comment": "WIT DNS Control Panel"` + tmp += `}` + data := []byte(tmp) + + log.Println("http PUT url =", url) + log.Println("http PUT data =", data) + spew.Dump(data) + + req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(data)) + + // Set headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Auth-Key", authKey) + req.Header.Set("X-Auth-Email", email) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Println(err) + return + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Println(err) + return + } + log.Println("http PUT body =", body) + spew.Dump(body) + + return +} + +// https://api.cloudflare.com/client/v4/zones +func getZones(auth, email string) *DNSRecords { + var url = "https://api.cloudflare.com/client/v4/zones" + log.Println("getZones()", url) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + log.Println("http.NewRequest error:", err) + return nil + } + + // Set headers + req.Header.Set("X-Auth-Key", auth) + req.Header.Set("X-Auth-Email", email) + + log.Println("getZones() auth, email", auth, email) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Println("getZones() http.Client error:", err) + return nil + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Println("getZones() ioutil.ReadAll() error", err) + return nil + } + + var records DNSRecords + if err := json.Unmarshal(body, &records); err != nil { + log.Println("getZones() json.Unmarshal() error", err) + return nil + } + + /* Cloudflare API returns struct[] of: + struct { ID string "json:\"id\""; Type string "json:\"type\""; Name string "json:\"name\""; + Content string "json:\"content\""; Proxied bool "json:\"proxied\""; + Proxiable bool "json:\"proxiable\""; TTL int "json:\"ttl\"" } + */ + + // log.Println("getZones() worked", records) + // log.Println("spew dump:") + spew.Dump(records) + for _, record := range records.Result { + log.Println("spew record:", record) + log.Println("record:", record.Name, record.ID) + + var newc *configT + newc = new(configT) + + newc.domain = record.Name + newc.zoneID = record.ID + newc.auth = auth + newc.email = email + + config[record.Name] = newc + zonedrop.AddText(record.Name) + log.Println("zonedrop.AddText:", record.Name, record.ID) + } + for d, _ := range config { + log.Println("config entry:", d) + } + + return &records +} diff --git a/examples/cloudflare/curl.sh b/examples/cloudflare/curl.sh new file mode 100644 index 0000000..ec8c014 --- /dev/null +++ b/examples/cloudflare/curl.sh @@ -0,0 +1,3 @@ +curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \ + -H "Authorization: Bearer AAAPutYourTokenInHereSoYouCanTestItL5Cl3" \ + -H "Content-Type:application/json" diff --git a/examples/cloudflare/dns.go b/examples/cloudflare/dns.go deleted file mode 100644 index eb8de23..0000000 --- a/examples/cloudflare/dns.go +++ /dev/null @@ -1,132 +0,0 @@ -// This is a simple example -package main - -import ( - "os" - "log" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "strconv" -) - -// Define a struct to match the JSON structure of the response. -// This structure should be adjusted based on the actual format of the response. -type DNSRecords struct { - Result []struct { - ID string `json:"id"` - Type string `json:"type"` - Name string `json:"name"` - Content string `json:"content"` - Proxied bool `json:"proxied"` - Proxiable bool `json:"proxiable"` - TTL int `json:"ttl"` - } `json:"result"` -} - -// var domain string = "wit.org" -// var os.Getenv("CLOUDFLARE_DOMAIN") - -func loadDNS(hostname string) { - log.Println("adding DNS record") - - newt := mainWindow.NewTab(hostname) - newg := newt.NewGroup("more") - grid := newg.NewGrid("gridnuts", 5, gridH) - -// 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) - } - grid.NewButton("Name", func () { - log.Println("sort by Name") - }) - grid.NewButton("Protection", func () { - log.Println("sort proxied") - }) - grid.NewButton("TTL", func () { - log.Println("sort by TTL") - }) - grid.NewButton("Value", func () { - log.Println("sort by Value") - }) - - newt.NewButton("Save", func () { - log.Println("save stuff to cloudflare") - }) - - records := getRecords() - for _, record := range records.Result { - grid.NewLabel(record.Type) - textbox := grid.NewTextbox(record.Name) - textbox.SetText(record.Name) - if (record.Proxied) { - grid.NewLabel("Proxied") - } else { - grid.NewLabel("DNS") - } - var ttl, short string - if (record.TTL == 1) { - ttl = "Auto" - } else { - ttl = strconv.Itoa(record.TTL) - } - grid.NewLabel(ttl) - // short = fmt.Sprintf("%80s", record.Content) - short = record.Content - if len(short) > 40 { - short = short[:40] // Slice the first 20 characters - } - - namebox := grid.NewTextbox(short) - namebox.SetText(short) - - fmt.Printf("ID: %s, Type: %s, Name: %s, short Content: %s\n", record.ID, record.Type, record.Name, short) - fmt.Printf("\tproxied: %b, %b, string TTL: %i\n", record.Proxied, record.Proxiable, ttl) - } -} - -func getRecords() *DNSRecords { - var url string = os.Getenv("CLOUDFLARE_URL") - req, err := http.NewRequest("GET", url, nil) - if err != nil { - fmt.Println(err) - return nil - } - - var authKey string = os.Getenv("CLOUDFLARE_AUTHKEY") - var email string = os.Getenv("CLOUDFLARE_EMAIL") - - // Set headers - req.Header.Set("X-Auth-Key", authKey) - req.Header.Set("X-Auth-Email", email) - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - fmt.Println(err) - return nil - } - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - fmt.Println(err) - return nil - } - - var records DNSRecords - if err := json.Unmarshal(body, &records); err != nil { - fmt.Println(err) - return nil - } - - return &records -} diff --git a/examples/cloudflare/gui.go b/examples/cloudflare/gui.go new file mode 100644 index 0000000..60fd5fe --- /dev/null +++ b/examples/cloudflare/gui.go @@ -0,0 +1,122 @@ +// This is a simple example +package main + +import ( + "log" + "strconv" +) + +func loadDNS(c *configT) { + hostname := c.domain + log.Println("adding DNS record", hostname) + + newt := mainWindow.NewTab(hostname) + newg := newt.NewGroup("more") + + // make a grid 6 things wide + grid := newg.NewGrid("gridnuts", 6, gridH) + +// 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.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() + + masterSave = newt.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 + + // copy all the JSON values into the row record. + rr.ID = record.ID + rr.Type = record.Type + rr.Name = record.Name + rr.Content = record.Content + rr.Proxied = record.Proxied + rr.Proxiable = record.Proxiable + rr.TTL = record.TTL + + rr.typeNode = grid.NewLabel(record.Type) + rr.nameNode = grid.NewEntryLine(record.Name) + rr.nameNode.SetText(record.Name) + rr.nameNode.Disable() + + // set proxy or unproxied + rr.proxyNode = grid.NewDropdown("proxy") + if (record.Proxied) { + rr.proxyNode.AddText("Proxied") + rr.proxyNode.AddText("DNS") + } 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() + } + + var ttl, short 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 + } + + rr.valueNode = grid.NewEntryLine(short) + rr.valueNode.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 () { + name := "save stuff to cloudflare for " + rr.ID + log.Println(name) + doChange(&rr) + } + } + + grid.Pad() +} diff --git a/examples/cloudflare/main.go b/examples/cloudflare/main.go index b83d276..badf97a 100644 --- a/examples/cloudflare/main.go +++ b/examples/cloudflare/main.go @@ -5,11 +5,14 @@ import ( "os" "fmt" "log" + "bufio" + "strings" "git.wit.org/wit/gui" ) var title string = "Cloudflare DNS Control Panel" var outfile string = "/tmp/guilogfile" +var configfile string = ".config/wit/cloudflare" var myGui *gui.Node var buttonCounter int = 5 @@ -19,8 +22,10 @@ var gridH int = 3 var mainWindow, more, more2 *gui.Node func main() { + config = make(map[string]*configT) + readConfig() myGui = gui.New().Default() - buttonWindow() + makeCloudflareWindow() // This is just a optional goroutine to watch that things are alive gui.Watchdog() @@ -28,30 +33,90 @@ func main() { } // This creates a window -func buttonWindow() { - var t, g *gui.Node +func makeCloudflareWindow() { + var t *gui.Node log.Println("buttonWindow() START") mainWindow = myGui.NewWindow(title).SetText(title) - t = mainWindow.NewTab("Cloudflare") - g = t.NewGroup("buttons") - g1 := t.NewGroup("buttonGroup 2") - more = g1.NewGroup("more") - showCloudflareCredentials(more) + // this tab has the master cloudflare API credentials + makeConfigTab(mainWindow) - // more2 = g1.NewGrid("gridnuts", gridW, gridH) + t = mainWindow.NewTab("Zones") + g1 := t.NewGroup("zones") - var domain string = os.Getenv("CLOUDFLARE_DOMAIN") - if (domain == "") { - domain = "example.org" + // make dropdown list of zones + zonedrop = g1.NewDropdown("zone") + zonedrop.AddText("example.org") + for d, _ := range config { + zonedrop.AddText(d) } - g.NewButton("Load " + domain + " DNS", func () { - loadDNS(domain) + zonedrop.Custom = func () { + domain := zonedrop.S + log.Println("custom dropdown() zone (domain name) =", zonedrop.Name, domain) + if (config[domain] == nil) { + log.Println("custom dropdown() config[domain] = nil for domain =", domain) + domainWidget.SetText(domain) + zoneWidget.SetText("") + authWidget.SetText("") + emailWidget.SetText("") + } else { + log.Println("custom dropdown() a =", domain, config[domain].zoneID, config[domain].auth, config[domain].email) + domainWidget.SetText(config[domain].domain) + zoneWidget.SetText(config[domain].zoneID) + authWidget.SetText(config[domain].auth) + emailWidget.SetText(config[domain].email) + } + } + + more = g1.NewGroup("data") + showCloudflareCredentials(more) + + makeDebugTab(mainWindow) +} + +func makeConfigTab(window *gui.Node) { + t := window.NewTab("Get Zones") + 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") + + // make grid to display credentials + grid := g1.NewGrid("credsGrid", 2, 4) // width = 2 + + grid.NewLabel("Auth Key") + aw := grid.NewEntryLine(os.Getenv("CLOUDFLARE_AUTHKEY")) + aw.SetText(os.Getenv("CLOUDFLARE_AUTHKEY")) + + grid.NewLabel("Email") + ew := grid.NewEntryLine(os.Getenv("CLOUDFLARE_EMAIL")) + ew.SetText(os.Getenv("CLOUDFLARE_EMAIL")) + + var url string = "https://api.cloudflare.com/client/v4/zones/" + grid.NewLabel("Cloudflare API") + grid.NewLabel(url) + + grid.Pad() + + vb.NewButton("getZones()", func () { + log.Println("getZones()") + getZones(aw.S, ew.S) }) + t.Pad() + t.Margin() + vb.Pad() + vb.Margin() + g1.Pad() + g1.Margin() +} + +func makeDebugTab(window *gui.Node) { + t2 := window.NewTab("debug") + g := t2.NewGroup("debug") g.NewButton("Load 'gocui'", func () { // this set the xterm and mate-terminal window title. maybe works generally? fmt.Println("\033]0;" + title + "blah \007") @@ -65,20 +130,103 @@ func buttonWindow() { g.NewButton("gui.DebugWindow()", func () { gui.DebugWindow() }) + + g.NewButton("List all Widgets", func () { + myGui.ListChildren(true) + }) + g.NewButton("Dump all Widgets", func () { + myGui.Dump() + }) } func showCloudflareCredentials(box *gui.Node) { + // make grid to display credentials grid := box.NewGrid("credsGrid", 2, 4) // width = 2 grid.NewLabel("Domain") - grid.NewLabel(os.Getenv("CLOUDFLARE_DOMAIN")) + domainWidget = grid.NewEntryLine(os.Getenv("CLOUDFLARE_DOMAIN")) + + grid.NewLabel("Zone ID") + zoneWidget = grid.NewEntryLine(os.Getenv("CLOUDFLARE_ZONEID")) grid.NewLabel("Auth Key") - grid.NewLabel(os.Getenv("CLOUDFLARE_AUTHKEY")) + authWidget = grid.NewEntryLine(os.Getenv("CLOUDFLARE_AUTHKEY")) grid.NewLabel("Email") - grid.NewLabel(os.Getenv("CLOUDFLARE_EMAIL")) + emailWidget = grid.NewEntryLine(os.Getenv("CLOUDFLARE_EMAIL")) - grid.NewLabel("URL") - grid.NewLabel(os.Getenv("CLOUDFLARE_URL")) + var url string = "https://api.cloudflare.com/client/v4/zones/" + grid.NewLabel("Cloudflare API") + grid.NewLabel(url) + + 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 + domain.zoneID = zoneWidget.S + domain.auth = authWidget.S + domain.email = emailWidget.S + loadDNS(&domain) + }) +} + +func readConfig() { + homeDir, err := os.UserHomeDir() + if err != nil { + log.Println("searchPaths() error. exiting here?") + } + filename := homeDir + "/" + configfile + log.Println("filename =", filename) + + readFileLineByLine(filename) + // os.Exit(0) +} + +// readFileLineByLine opens a file and reads through each line. +func readFileLineByLine(filename string) error { + // Open the file. + file, err := os.Open(filename) + if err != nil { + return err + } + defer file.Close() + + log.Println("readFileLineByLine() =", filename) + + // Create a new Scanner for the file. + scanner := bufio.NewScanner(file) + + // Read through each line using scanner. + for scanner.Scan() { + var newc *configT + newc = new(configT) + + line := scanner.Text() + parts := strings.Fields(line) + + if (len(parts) < 4) { + log.Println("readFileLineByLine() SKIP =", parts) + continue + } + + newc.domain = parts[0] + newc.zoneID = parts[1] + newc.auth = parts[2] + newc.email = parts[3] + + config[parts[0]] = newc + log.Println("readFileLineByLine() =", newc.domain, newc.zoneID, newc.auth, newc.email) + } + + // Check for errors during Scan. + if err := scanner.Err(); err != nil { + return err + } + + return nil } diff --git a/examples/cloudflare/structs.go b/examples/cloudflare/structs.go new file mode 100644 index 0000000..af4d7f3 --- /dev/null +++ b/examples/cloudflare/structs.go @@ -0,0 +1,77 @@ +// This is a simple example +package main + +import ( + "git.wit.org/wit/gui" +) + +var cloudflareURL string = "https://api.cloudflare.com/client/v4/zones/" + +// Define a struct to match the JSON structure of the response. +// This structure should be adjusted based on the actual format of the response. +type DNSRecords struct { + Result []struct { + ID string `json:"id"` + Type string `json:"type"` + Name string `json:"name"` + Content string `json:"content"` + Proxied bool `json:"proxied"` + Proxiable bool `json:"proxiable"` + TTL int `json:"ttl"` + } `json:"result"` +} + +var masterSave *gui.Node + +var domainWidget *gui.Node +var zoneWidget *gui.Node +var authWidget *gui.Node +var emailWidget *gui.Node + +var loadButton *gui.Node +var saveButton *gui.Node +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 + + ID string + Type string + Name string + Content string + Proxied bool + Proxiable bool + TTL int +} + +/* + This is a structure of all the RR's (Resource Records) + in the DNS zonefiile for a hostname. For example: + + For the host test.wit.com: + + test.wit.com A 127.0.0.1 + test.wit.com AAAA + test.wit.com TXT email test@wit.com + test.wit.com TXT phone 212-555-1212 + test.wit.com CNAME real.wit.com +*/ +type hostT struct { + hostname string + RRs []configT +} + +type configT struct { + domain string + zoneID string + auth string + email string +} + +var config map[string]*configT diff --git a/main.go b/main.go index f121cec..03479bba 100644 --- a/main.go +++ b/main.go @@ -61,7 +61,7 @@ func watchCallback() { } // this maybe a good idea? // TODO: Throttle user events somehow - sleep(.01) + // sleep(.01) // hack that throttles user events } } } diff --git a/plugin.go b/plugin.go index c43b4af..1ce900c 100644 --- a/plugin.go +++ b/plugin.go @@ -132,7 +132,6 @@ func searchPaths(name string) *aplug { log(logError, filename, "was not embedded in the binary. Error:", err) } - log(logError, "fuck off") // attempt to write out the file from the internal resource filename = "toolkit/" + name + ".so" p := initToolkit(name, filename) @@ -254,7 +253,7 @@ func sendAction(a *toolkit.Action) { log(logInfo, "Action() SEND to pluginChan", aplug.name) aplug.pluginChan <- *a // added during debugging. might be a good idea in general for a tactile experience - sleep(.02) + sleep(.02) // this delay makes it so SetText() works on initial widget creation } } @@ -303,7 +302,7 @@ func (n *Node) LoadToolkit(name string) *Node { var a toolkit.Action a.ActionType = toolkit.InitToolkit plug.pluginChan <- a - sleep(.5) // temp hack until chan communication is setup + // sleep(.5) // temp hack until chan communication is setup // TODO: find a new way to do this that is locking, safe and accurate me.rootNode.redraw(plug) @@ -320,7 +319,7 @@ func (n *Node) CloseToolkit(name string) bool { var a toolkit.Action a.ActionType = toolkit.CloseToolkit plug.pluginChan <- a - sleep(.5) + // sleep(.5) // is this needed? TODO: properly close channel return true } } diff --git a/tab.go b/tab.go index 95d9fa4..abaf5cb 100644 --- a/tab.go +++ b/tab.go @@ -10,7 +10,6 @@ import ( func (n *Node) NewTab(text string) *Node { // check to make sure n is actually a window - if (n.WidgetType != toolkit.Window) { // figure out what the actual window is log(logError, "NewTab() is being requested on something that isn't a Window. node =", n) @@ -35,7 +34,7 @@ func (n *Node) NewTab(text string) *Node { // by default, create a box inside the tab // TODO: allow this to be configurable - newBox := newNode.NewBox(text, true) + newBox := newNode.NewBox(text + " box", true) return newBox } diff --git a/textbox.go b/textbox.go index 5b416a7..3c9fd36 100644 --- a/textbox.go +++ b/textbox.go @@ -15,3 +15,17 @@ func (parent *Node) NewTextbox(name string) *Node { sendAction(a) return newNode } + +func (parent *Node) NewEntryLine(name string) *Node { + newNode := parent.newNode(name, toolkit.Textbox) + + newNode.X = 1 + + newNode.Custom = func() { + log(debugGui, "NewTextbox changed =", name) + } + + a := newAction(newNode, toolkit.Add) + sendAction(a) + return newNode +} diff --git a/toolkit/andlabs/action.go b/toolkit/andlabs/action.go index c792a09..16a895b 100644 --- a/toolkit/andlabs/action.go +++ b/toolkit/andlabs/action.go @@ -1,6 +1,7 @@ package main import ( + "strconv" "github.com/andlabs/ui" "git.wit.org/wit/gui/toolkit" ) @@ -20,6 +21,9 @@ func (n *node) show(b bool) { } func (n *node) enable(b bool) { + if n == nil { + panic("WHAT? enable was passed nil. How does this even happen?") + } if n.tk == nil { return } @@ -193,12 +197,32 @@ func rawAction(a toolkit.Action) { n := rootNode.findWidgetId(a.WidgetId) - switch a.ActionType { - case toolkit.Add: + if (a.ActionType == toolkit.Add) { ui.QueueMain(func() { add(a) }) - sleep(.05) + // TODO: remove this artificial delay + // sleep(.001) + return + } + + if (a.ActionType == toolkit.Dump) { + log(debugNow, "rawAction() Dump =", a.ActionType, a.WidgetType, n.Name) + rootNode.listChildren(true) + return + } + + if (n == nil) { + rootNode.listChildren(true) + log(true, "rawAction() ERROR findWidgetId found nil", a.ActionType, a.WidgetType) + log(true, "rawAction() ERROR findWidgetId found nil for id =", a.WidgetId) + log(true, "rawAction() ERROR findWidgetId found nil", a.ActionType, a.WidgetType) + log(true, "rawAction() ERROR findWidgetId found nil for id =", a.WidgetId) + return + panic("findWidgetId found nil for id = " + strconv.Itoa(a.WidgetId)) + } + + switch a.ActionType { case toolkit.Show: n.show(true) case toolkit.Hide: diff --git a/toolkit/andlabs/add.go b/toolkit/andlabs/add.go index fcdc56b..b01dd20 100644 --- a/toolkit/andlabs/add.go +++ b/toolkit/andlabs/add.go @@ -134,6 +134,9 @@ func (p *node) place(n *node) bool { log(logError, "n.tk.uiControl == nil", 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) + // panic("n.tk.uiControl == nil") p.tk.uiTab.Append(n.Text, n.tk.uiControl) p.tk.boxC += 1 return true diff --git a/toolkit/andlabs/button.go b/toolkit/andlabs/button.go index a6260b5..d61c0ea 100644 --- a/toolkit/andlabs/button.go +++ b/toolkit/andlabs/button.go @@ -6,7 +6,7 @@ import ( ) func (p *node) newButton(n *node) { - log(debugToolkit, "newButton()", n.Name) + log(debugToolkit, "newButton() START", n.Name) t := p.tk if (t == nil) { @@ -27,4 +27,5 @@ func (p *node) newButton(n *node) { n.tk = newt p.place(n) + log(debugToolkit, "newButton() END", n.Name) } diff --git a/toolkit/andlabs/debug.go b/toolkit/andlabs/debug.go index 7abd2d1..87e875d 100644 --- a/toolkit/andlabs/debug.go +++ b/toolkit/andlabs/debug.go @@ -1,6 +1,9 @@ package main -import "git.wit.org/wit/gui/toolkit" +import ( + "strconv" + "git.wit.org/wit/gui/toolkit" +) var defaultBehavior bool = true @@ -126,3 +129,40 @@ func flag(a *toolkit.Action) { log(debugError, "Can't set unknown flag", a.S) } } + +func (n *node) dumpWidget(b bool) { + var info, d string + + if (n == nil) { + log(debugError, "dumpWidget() node == nil") + return + } + info = n.WidgetType.String() + + d = strconv.Itoa(n.WidgetId) + " " + info + " " + n.Name + + var tabs string + for i := 0; i < listChildrenDepth; i++ { + tabs = tabs + defaultPadding + } + log(b, tabs + d) +} + +var defaultPadding string = " " +var listChildrenDepth int = 0 + +func (n *node) listChildren(dump bool) { + if (n == nil) { + return + } + + n.dumpWidget(dump) + if len(n.children) == 0 { + return + } + for _, child := range n.children { + listChildrenDepth += 1 + child.listChildren(dump) + listChildrenDepth -= 1 + } +} diff --git a/toolkit/andlabs/main.go b/toolkit/andlabs/main.go index f66849d..3ba3079 100644 --- a/toolkit/andlabs/main.go +++ b/toolkit/andlabs/main.go @@ -30,7 +30,8 @@ func catchActionChannel() { muAction.Lock() // TODO ui.QueueMain(f) // TODO ui.QueueMain( func() {rawAction(a)} ) - rawAction(a) + ui.QueueMain( func() {rawAction(a)} ) + // rawAction(a) muAction.Unlock() log(logInfo, "catchActionChannel() STUFF END", a.WidgetId, a.ActionType, a.WidgetType) } @@ -57,7 +58,7 @@ func init() { log(logNow, "Init() START") log(debugToolkit, "Init()") // Can you pass values to a plugin init() ? Otherwise, there is no way to safely print - // log(debugToolkit, "gui/toolkit init() Setting defaultBehavior = true") + // log(debugToolkit, "init() Setting defaultBehavior = true") setDefaultBehavior(true) // andlabs = make(map[int]*andlabsT) diff --git a/toolkit/andlabs/setText.go b/toolkit/andlabs/setText.go index 11de327..a3332f5 100644 --- a/toolkit/andlabs/setText.go +++ b/toolkit/andlabs/setText.go @@ -5,6 +5,7 @@ import ( ) func (n *node) setText(a *toolkit.Action) { + log(debugChange, "setText() START with a.S =", a.S) t := n.tk if (t == nil) { log(debugError, "setText error. tk == nil", n.Name, n.WidgetId) @@ -34,9 +35,19 @@ func (n *node) setText(a *toolkit.Action) { case toolkit.Textbox: switch a.ActionType { case toolkit.Set: - t.uiMultilineEntry.SetText(a.S) + if (t.uiEntry != nil) { + t.uiEntry.SetText(a.S) + } + if (t.uiMultilineEntry != nil) { + t.uiMultilineEntry.SetText(a.S) + } case toolkit.SetText: - t.uiMultilineEntry.SetText(a.S) + if (t.uiEntry != nil) { + t.uiEntry.SetText(a.S) + } + if (t.uiMultilineEntry != nil) { + t.uiMultilineEntry.SetText(a.S) + } default: log(debugError, "setText() unknown", a.ActionType, "on checkbox", n.Name) } @@ -113,4 +124,5 @@ func (n *node) setText(a *toolkit.Action) { default: log(debugError, "plugin Send() Don't know how to setText on", n.WidgetType, "yet", a.ActionType) } + log(debugChange, "setText() END with a.S =", a.S) } diff --git a/toolkit/andlabs/structs.go b/toolkit/andlabs/structs.go index c71732d..507a50c 100644 --- a/toolkit/andlabs/structs.go +++ b/toolkit/andlabs/structs.go @@ -51,6 +51,9 @@ type andlabsT struct { parent *andlabsT children []*andlabsT + // used to track if a tab has a child widget yet + child bool + uiControl ui.Control uiBox *ui.Box diff --git a/toolkit/andlabs/tab.go b/toolkit/andlabs/tab.go index eecebfa..2f44b03 100644 --- a/toolkit/andlabs/tab.go +++ b/toolkit/andlabs/tab.go @@ -23,7 +23,7 @@ func (p *node) newTab(n *node) { var newt *andlabsT if (p.WidgetType != toolkit.Window) { - log(debugToolkit, "newTab() uiWindow == nil. I can't add a toolbar without window", n.WidgetId, n.ParentId) + log(debugError, "newTab() uiWindow == nil. I can't add a toolbar without window", n.WidgetId, n.ParentId) return } t := p.tk @@ -38,7 +38,15 @@ func (p *node) newTab(n *node) { } else { // this means you have to append a tab log(debugToolkit, "newTab() GOOD. This should be an additional tab:", n.WidgetId, n.ParentId) - newt = t.appendTab(n.Text) + if (n.WidgetType == toolkit.Tab) { + // andlabs doesn't have multiple tab widgets so make a fake one? + // this makes a andlabsT internal structure with the parent values + newt = new(andlabsT) + newt.uiWindow = t.uiWindow + newt.uiTab = t.uiTab + } else { + newt = t.appendTab(n.Text) + } } n.tk = newt @@ -63,7 +71,7 @@ func rawTab(w *ui.Window, name string) *andlabsT { log(debugError, "UiWindow == nil. I can't add a tab without a window") log(debugError, "UiWindow == nil. I can't add a tab without a window") log(debugError, "UiWindow == nil. I can't add a tab without a window") - sleep(1) + // sleep(1) return nil } @@ -77,13 +85,13 @@ func rawTab(w *ui.Window, name string) *andlabsT { func (t *andlabsT) appendTab(name string) *andlabsT { var newT andlabsT - log(debugToolkit, "gui.toolkit.NewTab() ADD", name) + log(debugToolkit, "appendTab() ADD", name) if (t.uiTab == nil) { - log(debugToolkit, "gui.Toolkit.UiWindow == nil. I can't add a widget without a place to put it") + log(debugToolkit, "UiWindow == nil. I can't add a widget without a place to put it") panic("should never have happened. wit/gui/toolkit has ui.Tab == nil") } - log(debugToolkit, "gui.toolkit.AddTab() START name =", name) + log(debugToolkit, "appendTab() START name =", name) var hbox *ui.Box if (defaultBehavior) { @@ -103,12 +111,3 @@ func (t *andlabsT) appendTab(name string) *andlabsT { newT.uiBox = hbox return &newT } - -/* -func newTab(n *node) { - log(logInfo, "newTab() add to parent id:", n.ParentId) - - p := n.parent - p.newTab(n) -} -*/ diff --git a/toolkit/andlabs/textbox.go b/toolkit/andlabs/textbox.go index 56788d8..1745d11 100644 --- a/toolkit/andlabs/textbox.go +++ b/toolkit/andlabs/textbox.go @@ -8,14 +8,25 @@ import ( func (p *node) newTextbox(n *node) { newt := new(andlabsT) - e := ui.NewNonWrappingMultilineEntry() - newt.uiMultilineEntry = e - newt.uiControl = e + if (n.X == 1) { + e := ui.NewEntry() + newt.uiEntry = e + newt.uiControl = e - e.OnChanged(func(spin *ui.MultilineEntry) { - n.S = spin.Text() - n.doUserEvent() - }) + e.OnChanged(func(spin *ui.Entry) { + n.S = spin.Text() + n.doUserEvent() + }) + } else { + e := ui.NewNonWrappingMultilineEntry() + newt.uiMultilineEntry = e + newt.uiControl = e + + e.OnChanged(func(spin *ui.MultilineEntry) { + n.S = spin.Text() + n.doUserEvent() + }) + } n.tk = newt p.place(n) } diff --git a/toolkit/gocui/Makefile b/toolkit/gocui/Makefile index 6c4f7d5..4f2a6ee 100644 --- a/toolkit/gocui/Makefile +++ b/toolkit/gocui/Makefile @@ -2,10 +2,10 @@ all: plugin ldd ../gocui.so goget: - GO111MODULE="off" go get -v -t -u + go get -v -t -u plugin: - GO111MODULE="off" go build -v -x -buildmode=plugin -o ../gocui.so + go build -v -x -buildmode=plugin -o ../gocui.so objdump: objdump -t ../gocui.so |less diff --git a/toolkit/gocui/add.go b/toolkit/gocui/add.go index 97d65d3..446d70c 100644 --- a/toolkit/gocui/add.go +++ b/toolkit/gocui/add.go @@ -31,29 +31,40 @@ func (n *node) addWidget() { switch n.WidgetType { case toolkit.Root: log(logInfo, "setStartWH() rootNode w.id =", n.WidgetId, "w.name", n.Name) + nw.color = &colorRoot n.setFake() return case toolkit.Flag: + nw.color = &colorFlag n.setFake() return case toolkit.Window: nw.frame = false - redoWindows(0,0) + nw.color = &colorWindow + // redoWindows(0,0) return case toolkit.Tab: + nw.color = &colorTab + // redoWindows(0,0) return + case toolkit.Button: + nw.color = &colorButton case toolkit.Box: + nw.color = &colorBox nw.isFake = true n.setFake() return case toolkit.Grid: + nw.color = &colorGrid nw.isFake = true n.setFake() return case toolkit.Group: + nw.color = &colorGroup nw.frame = false return case toolkit.Label: + nw.color = &colorLabel nw.frame = false return default: diff --git a/toolkit/gocui/click.go b/toolkit/gocui/click.go index 380b0a3..cb58bc3 100644 --- a/toolkit/gocui/click.go +++ b/toolkit/gocui/click.go @@ -1,17 +1,23 @@ package main import ( + "fmt" "github.com/awesome-gocui/gocui" "git.wit.org/wit/gui/toolkit" ) // set isCurrent = false everywhere -func UnsetCurrent(n *node) { +func unsetCurrent(n *node) { w := n.tk w.isCurrent = false + if n.WidgetType == toolkit.Tab { + // n.tk.color = &colorTab + // n.setColor() + } + for _, child := range n.children { - UnsetCurrent(child) + unsetCurrent(child) } } @@ -22,7 +28,14 @@ func (n *node) updateCurrent() { log("updateCurrent()", n.Name) if n.WidgetType == toolkit.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 } @@ -47,7 +60,7 @@ func setCurrentWindow(n *node) { if n.WidgetType != toolkit.Window { return } - UnsetCurrent(me.rootNode) + unsetCurrent(me.rootNode) if n.hasTabs { // set isCurrent = true on the first tab @@ -66,7 +79,7 @@ func setCurrentTab(n *node) { if n.WidgetType != toolkit.Tab { return } - UnsetCurrent(me.rootNode) + unsetCurrent(me.rootNode) w.isCurrent = true p := n.parent.tk p.isCurrent = true @@ -83,14 +96,51 @@ func (n *node) doWidgetClick() { // me.rootNode.redoColor(true) me.rootNode.dumpTree(true) case toolkit.Window: - me.rootNode.hideWidgets() - n.redoTabs(me.TabW, me.TabH) - if ! n.hasTabs { - setCurrentWindow(n) - n.placeWidgets(me.RawW, me.RawH) - n.showWidgets() + 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(true, "FOUND CURRENT TAB", child.Name) + setCurrentTab(child) + child.placeWidgets(me.RawW, me.RawH) + child.showWidgets() + return + } + } + /* FIXME: redo this + if ! n.hasTabs { + } + */ case toolkit.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 == toolkit.Tab { + child.setColor(&colorTab) + n.currentTab = false + } + } + } + n.currentTab = true + n.setColor(&colorActiveT) setCurrentTab(n) n.placeWidgets(me.RawW, me.RawH) n.showWidgets() @@ -118,6 +168,50 @@ func (n *node) doWidgetClick() { n.toggleTree() case toolkit.Button: n.doUserEvent() + case toolkit.Dropdown: + log(true, "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.org\nwit.com") + me.ddview.SetVisible(true) + return + } + log(true, "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(logNow, "AddText()", n.Name, i, s) + dnsList += s + "\n" + } + me.ddNode = n + log(logNow, "new dns list should be set to:", dnsList) + me.ddview.Text = dnsList + me.ddview.SetText(dnsList) + me.ddview.SetVisible(true) + } + for i, s := range n.vals { + log(logNow, "AddText()", n.Name, i, s) + } default: } } @@ -162,26 +256,15 @@ func click(g *gocui.Gui, v *gocui.View) error { n := findUnderMouse() if (n != nil) { log(logNow, "click() Found widget =", n.WidgetId, n.Name, ",", n.Text) + if (n.Name == "DropBox") { + log(logNow, "click() this is the dropdown menu. set a flag here what did I click? where is the mouse?") + log(logNow, "click() set a global dropdown clicked flag=true here") + me.ddClicked = true + } n.doWidgetClick() } else { log(logNow, "click() could not find node name =", v.Name()) } - /* - i, err := strconv.Atoi(v.Name()) - if (err != nil) { - log(logError, "click() Can't find widget. error =", err) - } else { - log(logVerbose, "click() ok v.Name() =", v.Name()) - n := me.rootNode.findWidgetId(i) - if (n == nil) { - log(logError, "click() CANT FIND VIEW in binary tree. v.Name =", v.Name()) - return nil - } - log(logNow, "click() Found widget =", n.WidgetId, n.Name, ",", n.Text) - n.doWidgetClick() - return nil - } - */ if _, err := g.SetCurrentView(v.Name()); err != nil { return err @@ -207,6 +290,17 @@ func findUnderMouse() *node { found = n } } + if (n == me.ddview) { + log(true, "findUnderMouse() found ddview") + if n.Visible() { + log(true, "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(true, "findUnderMouse() I was lying, actually it's not found") + } + } for _, child := range n.children { f(child) @@ -217,7 +311,7 @@ func findUnderMouse() *node { // TODO: pop up menu with a list of them for _, n := range widgets { //log(logNow, "ctrlDown() FOUND widget", widget.id, widget.name) - n.showWidgetPlacement(logNow, "ctrlDown() FOUND") + n.showWidgetPlacement(logNow, "findUnderMouse() FOUND") } return found } @@ -228,27 +322,6 @@ func ctrlDown(g *gocui.Gui, v *gocui.View) error { // var widgets []*node // var f func (n *node) found = findUnderMouse() - /* - w, h := g.MousePosition() - - // find buttons that are below where the mouse button click - f = func(n *node) { - widget := n.tk - // ignore widgets that are not visible - if n.Visible() { - if ((widget.gocuiSize.w0 <= w) && (w <= widget.gocuiSize.w1) && - (widget.gocuiSize.h0 <= h) && (h <= widget.gocuiSize.h1)) { - widgets = append(widgets, n) - found = n - } - } - - for _, child := range n.children { - f(child) - } - } - f(me.rootNode) - */ if (me.ctrlDown == nil) { setupCtrlDownWidget() me.ctrlDown.Text = found.Name @@ -266,9 +339,9 @@ func ctrlDown(g *gocui.Gui, v *gocui.View) error { cd.gocuiSize.w1 = newR.w1 cd.gocuiSize.h1 = newR.h1 if me.ctrlDown.Visible() { - me.ctrlDown.deleteView() + me.ctrlDown.hideView() } else { - me.ctrlDown.updateView() + me.ctrlDown.showView() } me.ctrlDown.showWidgetPlacement(logNow, "ctrlDown:") return nil diff --git a/toolkit/gocui/color.go b/toolkit/gocui/color.go index 5dbed05..4c2ea76 100644 --- a/toolkit/gocui/color.go +++ b/toolkit/gocui/color.go @@ -3,86 +3,85 @@ package main import ( "math/rand" "github.com/awesome-gocui/gocui" - "git.wit.org/wit/gui/toolkit" ) -// ColorBlack ColorRed ColorGreen ColorYellow ColorBlue ColorMagenta ColorCyan ColorWhite -// gocui.GetColor("#FFAA55") // Dark Purple -func (n *node) setDefaultWidgetColor() { - w := n.tk - log(logInfo, "setDefaultWidgetColor() on", n.WidgetType, n.Name) - v, _ := me.baseGui.View(w.cuiName) - if (v == nil) { - log(logError, "setDefaultWidgetColor() failed on view == nil") - return - } - sleep(.05) - // v.BgColor = gocui.GetColor("#FFAA55") // Dark Purple - // v.BgColor = gocui.GetColor("#88AA55") // heavy purple - // v.BgColor = gocui.GetColor("#111111") // crazy red - // v.BgColor = gocui.GetColor("#FF9911") // heavy red - // v.SelBgColor = gocui.GetColor("#FFEE11") // blood red +//w.v.SelBgColor = gocui.ColorCyan +//color.go: w.v.SelFgColor = gocui.ColorBlack +//color.go: w.v.BgColor = gocui.ColorGreen - // v.BgColor = gocui.GetColor("#55AAFF") // super light grey - // v.BgColor = gocui.GetColor("#FFC0CB") // 'w3c pink' yellow - switch n.WidgetType { - case toolkit.Root: - v.FrameColor = gocui.ColorRed - v.BgColor = gocui.GetColor("#B0E0E6") // w3c 'powerder blue' - case toolkit.Flag: - v.FrameColor = gocui.ColorRed - v.BgColor = gocui.GetColor("#B0E0E6") // w3c 'powerder blue' - case toolkit.Window: - v.FgColor = gocui.ColorCyan - v.SelBgColor = gocui.ColorBlue - v.FrameColor = gocui.ColorBlue - case toolkit.Tab: - v.SelBgColor = gocui.ColorBlue - v.FrameColor = gocui.ColorBlue - case toolkit.Button: - v.BgColor = gocui.ColorWhite - v.FrameColor = gocui.ColorGreen - v.SelBgColor = gocui.ColorBlack - v.SelFgColor = gocui.ColorGreen - case toolkit.Label: - v.BgColor = gocui.GetColor("#55AAFF") // super light grey - v.SelBgColor = gocui.GetColor("#55AAFF") // super light grey - case toolkit.Box: - v.FrameColor = gocui.ColorRed - // v.BgColor = gocui.GetColor("#FFC0CB") // 'w3c pink' yellow - v.BgColor = gocui.GetColor("#DDDDDD") // light purple - case toolkit.Grid: - // v.FgColor = gocui.ColorCyan - // v.SelBgColor = gocui.ColorBlue - // v.FrameColor = gocui.ColorBlue - case toolkit.Group: - v.BgColor = gocui.GetColor("#55AAFF") // super light grey - default: - } +type colorT struct { + frame gocui.Attribute + fg gocui.Attribute + bg gocui.Attribute + selFg gocui.Attribute + selBg gocui.Attribute + name string } -// SetColor("#FFAA55") // purple -func (w *cuiWidget) SetColor(c string) { - if (w.v == nil) { - log(logError, "SetColor() failed on view == nil") +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 } - w.v.SelBgColor = gocui.ColorCyan - w.v.SelFgColor = gocui.ColorBlack - switch c { - case "Green": - w.v.BgColor = gocui.ColorGreen - case "Purple": - w.v.BgColor = gocui.GetColor("#FFAA55") - case "Yellow": - w.v.BgColor = gocui.ColorYellow - case "Blue": - w.v.BgColor = gocui.ColorBlue - case "Red": - w.v.BgColor = gocui.ColorRed - default: - w.v.BgColor = gocui.GetColor(c) + tk.color = newColor + if (tk.v == nil) { + return } + if (tk.color == nil) { + log(true, "Set the node to color = nil") + tk.color = &colorNone + } + log(true, "Set the node to color =", tk.color.name) + n.recreateView() +} + +func (n *node) setDefaultWidgetColor() { + n.showView() } func (n *node) setDefaultHighlight() { diff --git a/toolkit/gocui/common.go b/toolkit/gocui/common.go index 05de64b..d3de34c 100644 --- a/toolkit/gocui/common.go +++ b/toolkit/gocui/common.go @@ -98,6 +98,46 @@ func (n *node) findWidgetName(name string) *node { return nil } +func (n *node) IsCurrent() bool { + w := n.tk + if (n.WidgetType == toolkit.Tab) { + return w.isCurrent + } + if (n.WidgetType == toolkit.Window) { + return w.isCurrent + } + if (n.WidgetType == toolkit.Root) { + return false + } + return n.parent.IsCurrent() +} + +func (n *node) Visible() bool { + if (n == nil) { + return false + } + if (n.tk == nil) { + return false + } + if (n.tk.v == nil) { + return false + } + return n.tk.v.Visible +} + +func (n *node) SetVisible(b bool) { + if (n == nil) { + return + } + if (n.tk == nil) { + return + } + if (n.tk.v == nil) { + return + } + n.tk.v.Visible = b +} + func addNode(a *toolkit.Action) *node { n := new(node) n.WidgetType = a.WidgetType @@ -143,42 +183,29 @@ func addNode(a *toolkit.Action) *node { return n } -func (n *node) IsCurrent() bool { - w := n.tk - if (n.WidgetType == toolkit.Tab) { - return w.isCurrent - } - if (n.WidgetType == toolkit.Window) { - return w.isCurrent - } - if (n.WidgetType == toolkit.Root) { - return false - } - return n.parent.IsCurrent() -} +func addDropdown() *node { + n := new(node) + n.WidgetType = toolkit.Flag + n.WidgetId = -2 + n.ParentId = 0 -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 -} + // copy the data from the action message + n.Name = "DropBox" + n.Text = "DropBox text" -func (n *node) SetVisible(b bool) { - if (n == nil) { - return + // store the internal toolkit information + n.tk = new(cuiWidget) + 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) } - if (n.tk == nil) { - return - } - if (n.tk.v == nil) { - return - } - n.tk.v.Visible = b + return n } diff --git a/toolkit/gocui/gocui.go b/toolkit/gocui/gocui.go index 85e6ea5..d2877d7 100644 --- a/toolkit/gocui/gocui.go +++ b/toolkit/gocui/gocui.go @@ -72,7 +72,7 @@ func dragOutputWindow() { func setFrame(b bool) { // TODO: figure out what this might be useful for // what is this do? I made it just 2 lines for now. Is this useful for something? - v := SetView("global", 15, 5, 80, 8, 10) + v := SetView("global", 5, 10, 5, 10, 0) // x0, x1, y1, y2, overlap if (v == nil) { log(logError, "setFrame() global failed") } diff --git a/toolkit/gocui/help.go b/toolkit/gocui/help.go index 66719bb..6113257 100644 --- a/toolkit/gocui/help.go +++ b/toolkit/gocui/help.go @@ -14,7 +14,9 @@ import ( var helpText []string = []string{"KEYBINDINGS", "", - "d: show/hide debugging", + "?: toggle help", + "d: toggle debugging", + "r: redraw widgets", "s/h: show/hide all widgets", "L: list all widgets", "q: quit()", @@ -27,6 +29,12 @@ var helpText []string = []string{"KEYBINDINGS", "", } +func hidehelplayout() { + me.baseGui.DeleteView("help") + // n.deleteView() + // child.hideFake() +} + func helplayout() error { g := me.baseGui var err error diff --git a/toolkit/gocui/keybindings.go b/toolkit/gocui/keybindings.go index dbe1fe2..658d09a 100644 --- a/toolkit/gocui/keybindings.go +++ b/toolkit/gocui/keybindings.go @@ -22,6 +22,7 @@ func defaultKeybindings(g *gocui.Gui) error { 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 } @@ -40,10 +41,8 @@ func defaultKeybindings(g *gocui.Gui) error { return nil } -var showDebug bool = true - func addDebugKeys(g *gocui.Gui) { - // dump all widget info to the log + // show debugging buttons g.SetKeybinding("", 'd', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { log(logNow, "gocui.SetKeyBinding() dumpTree() START") @@ -63,9 +62,29 @@ func addDebugKeys(g *gocui.Gui) { // display the help menu g.SetKeybinding("", '?', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { - helplayout() + 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 { diff --git a/toolkit/gocui/mouse.go b/toolkit/gocui/mouse.go index dbe2c6d..64786ab 100644 --- a/toolkit/gocui/mouse.go +++ b/toolkit/gocui/mouse.go @@ -24,6 +24,7 @@ func mouseMove(g *gocui.Gui) { func msgDown(g *gocui.Gui, v *gocui.View) error { initialMouseX, initialMouseY = g.MousePosition() + log(true, "msgDown() X,Y", initialMouseX, initialMouseY) if vx, vy, _, _, err := g.ViewPosition("msg"); err == nil { xOffset = initialMouseX - vx yOffset = initialMouseY - vy @@ -32,7 +33,58 @@ func msgDown(g *gocui.Gui, v *gocui.View) error { return nil } +func hideDDview() error { + w, h := me.baseGui.MousePosition() + log(true, "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(true, "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(true, "mouseUp() view msgMouseDown (check here for dropdown menu click) (w,h) =", w, h) + if (me.ddClicked) { + log(true, "mouseUp() ddview is the thing that was clicked", w, h) + log(true, "mouseUp() find out what the string is here", w, h, me.ddview.tk.gocuiSize.h1) + + if (me.ddNode != nil) { + value := h - me.ddview.tk.gocuiSize.h0 - 1 + log(true, "mouseUp() me.ddview.tk.gocuiSize.h1 =", me.ddview.tk.gocuiSize.h1) + log(true, "mouseUp() me.ddNode.vals =", me.ddNode.vals) + valsLen := len(me.ddNode.vals) + log(true, "mouseUp() value =", value, "valsLen =", valsLen) + log(true, "mouseUp() me.ddNode.vals =", me.ddNode.vals) + if ((value >= 0) && (value < valsLen)) { + str := me.ddNode.vals[value] + log(true, "mouseUp() value =", value, "str =", str) + } + } + } + /* + // 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 { @@ -57,13 +109,25 @@ func mouseDown(g *gocui.Gui, v *gocui.View) error { } globalMouseDown = true maxX, _ := g.Size() - msg := fmt.Sprintf("Mouse really down at: %d,%d", mx, my) + "foo\n" + "bar\n" + test := findUnderMouse() + msg := fmt.Sprintf("Mouse really down at: %d,%d", mx, my) + "foobar" + if (test == me.ddview) { + if (me.ddview.Visible()) { + log(true, "hide DDview() Mouse really down at:", mx, my) + hideDDview() + } else { + log(true, "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(true, "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 diff --git a/toolkit/gocui/showStdout.go b/toolkit/gocui/showStdout.go index 22e95d3..f03dfef 100644 --- a/toolkit/gocui/showStdout.go +++ b/toolkit/gocui/showStdout.go @@ -24,6 +24,7 @@ func showMsg(g *gocui.Gui, v *gocui.View) error { var l string var err error + log(true, "showMsg() v.name =", v.Name()) if _, err := g.SetCurrentView(v.Name()); err != nil { return err } diff --git a/toolkit/gocui/structs.go b/toolkit/gocui/structs.go index 06a2266..05df963 100644 --- a/toolkit/gocui/structs.go +++ b/toolkit/gocui/structs.go @@ -21,14 +21,24 @@ import ( // 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 -// current *cuiWidget // this is the current tab or window to show + 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 @@ -123,6 +133,7 @@ type node struct { horizontal bool `default:false` hasTabs bool // does the window have tabs? + currentTab bool // the visible tab // the internal plugin toolkit structure tk *cuiWidget @@ -152,7 +163,7 @@ type cuiWidget struct { size rectType // the actual gocui display view of this widget - // sometimes this isn't visable like with a Box or Grid + // sometimes this isn't visible like with a Box or Grid gocuiSize rectType isCurrent bool // is this the currently displayed Window or Tab? @@ -164,6 +175,12 @@ type cuiWidget struct { tainted bool frame bool + + // for a window, this is currently selected tab + selectedTab *node + + // what color to use + color *colorT } // from the gocui devs: diff --git a/toolkit/gocui/tab.go b/toolkit/gocui/tab.go index 60ee6b3..d910552 100644 --- a/toolkit/gocui/tab.go +++ b/toolkit/gocui/tab.go @@ -103,6 +103,10 @@ func (p *node) redoTabs(nextW int, nextH int) { n.gocuiSetWH(nextW, nextH) n.deleteView() // setCurrentTab(n) + // if (len(w.cuiName) < 4) { + // w.cuiName = "abcd" + // } + n.showView() sizeW := w.Width() + me.TabPadW diff --git a/toolkit/gocui/view.go b/toolkit/gocui/view.go index e2c76aa..3baeb66 100644 --- a/toolkit/gocui/view.go +++ b/toolkit/gocui/view.go @@ -36,7 +36,12 @@ func (n *node) textResize() { n.showWidgetPlacement(logNow, "textResize()") } +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 @@ -46,31 +51,34 @@ func (n *node) showView() { w.cuiName = strconv.Itoa(n.WidgetId) } + // if the gocui element doesn't exist, create it if (w.v == nil) { - n.updateView() + n.recreateView() } x0, y0, x1, y1, err := me.baseGui.ViewPosition(w.cuiName) log(logInfo, "showView() w.v already defined for widget", n.Name, err) + + // 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) - n.updateView() + 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) - n.updateView() + n.recreateView() return } - if (w.v.Visible == false) { - log(logInfo, "showView() w.v.Visible set to true ", n.Name) - w.v.Visible = true - } + n.SetVisible(true) } -func (n *node) updateView() { +// 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 if (me.baseGui == nil) { @@ -105,8 +113,14 @@ func (n *node) updateView() { fmt.Fprint(w.v, n.Text) n.showWidgetPlacement(logNow, "Window: " + n.Text) - n.setDefaultHighlight() - n.setDefaultWidgetColor() + // 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 + } } func (n *node) hideWidgets() { @@ -119,7 +133,7 @@ func (n *node) hideWidgets() { case toolkit.Box: case toolkit.Grid: default: - n.deleteView() + n.hideView() } for _, child := range n.children { child.hideWidgets() @@ -129,7 +143,7 @@ func (n *node) hideWidgets() { func (n *node) hideFake() { w := n.tk if (w.isFake) { - n.deleteView() + n.hideView() } for _, child := range n.children { child.hideFake() @@ -153,13 +167,13 @@ func (n *node) showWidgets() { if (w.isFake) { // don't display by default } else { - if n.IsCurrent() { + // if n.IsCurrent() { n.showWidgetPlacement(logInfo, "current:") n.showView() - } else { - n.showWidgetPlacement(logInfo, "not:") + // } else { + // n.showWidgetPlacement(logInfo, "not:") // w.drawView() - } + // } } for _, child := range n.children { child.showWidgets() diff --git a/toolkit/widget.go b/toolkit/widget.go index 655a0db..02dbe3f 100644 --- a/toolkit/widget.go +++ b/toolkit/widget.go @@ -69,6 +69,7 @@ const ( Textbox // is this a Label with edit=true Slider // like a progress bar Spinner // like setting the oven temperature + Separator // TODO Image // TODO Area // TODO Form // TODO @@ -138,6 +139,8 @@ func (s WidgetType) String() string { return "Slider" case Spinner: return "Spinner" + case Separator: + return "Separator" case Image: return "Image" case Area: