// Copyright 2017-2025 WIT.COM Inc. All rights reserved. // Use of this source code is governed by the GPL 3.0 package main import ( "fmt" "strings" "syscall" "github.com/awesome-gocui/gocui" "go.wit.com/log" "go.wit.com/toolkits/tree" "go.wit.com/widget" ) // register how the 'gocui' will work as a GO toolkit plugin // all applications will use these keys. they are universal. // tells 'gocui' where to send events func registerHandlers(g *gocui.Gui) { // mouse handlers g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, mouseDown) // normal left mouse down g.SetKeybinding("", gocui.MouseLeft, gocui.ModMouseCtrl, ctrlDown) // mouse with the ctrl key held down g.SetKeybinding("", gocui.MouseRelease, gocui.ModNone, mouseUp) // mouse button release // Ctrl key handlers g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, doExit) // CTRL-C : exits the application g.SetKeybinding("", gocui.KeyCtrlV, gocui.ModNone, doPanic) // CTRL-V : force a panic() g.SetKeybinding("", gocui.KeyCtrlD, gocui.ModNone, openDebuggger) // CTRL-D : open the (D)ebugger keyForced, modForced := gocui.MustParse("ctrl+z") // setup ctrl+z g.SetKeybinding("", keyForced, modForced, handle_ctrl_z) // CTRL-Z :cleverly let's you background gocui (breaks cursor mouse on return) // regular keys g.SetKeybinding("", '?', gocui.ModNone, theHelp) // '?' toggles on and off the help menu g.SetKeybinding("", 'w', gocui.ModNone, doWindow) // 'w' close all windows g.SetKeybinding("", 'r', gocui.ModNone, widgetRefresh) // 'r' screen refresh g.SetKeybinding("", 'q', gocui.ModNone, doExit) // 'q' exit // debugging g.SetKeybinding("", 'f', gocui.ModNone, theFind) // 'f' shows what is under your mouse g.SetKeybinding("", 'S', gocui.ModNone, setSuperMouse) // 'S' Super Mouse mode! g.SetKeybinding("", 'h', gocui.ModNone, theHide) // 'h' hide all widgets g.SetKeybinding("", 'M', gocui.ModNone, dumpWidgetPlacement) // 'M' list all widgets with positions g.SetKeybinding("", 'L', gocui.ModNone, dumpWidgets) // 'L' list all widgets in tree view g.SetKeybinding("", 'd', gocui.ModNone, theLetterD) // 'd' toggles on and off debugging buttons g.SetKeybinding("", '2', gocui.ModNone, theNotsure) // '2' for testing new ideas g.SetKeybinding("", 'q', gocui.ModNone, quit) // 'q' only exits gocui. plugin stays alive (?) } // flips on 'super mouse' mode // while this is turned on, it will print out every widget found under the mouse func setSuperMouse(g *gocui.Gui, v *gocui.View) error { if me.supermouse { log.Log(GOCUI, "supermouse off") me.supermouse = false } else { me.supermouse = true log.Log(GOCUI, "supermouse on") } return nil } var wtf bool func (tk *guiWidget) verifyRect() bool { if !tk.Visible() { // log.Info("verifyRect() tk is not visible", tk.cuiName) return false } vw0, vh0, vw1, vh1, err := me.baseGui.ViewPosition(tk.cuiName) if err != nil { // log.Printf("verifyRect() gocui err=%v cuiName=%s v.Name=%s", err, tk.cuiName, tk.v.Name()) vw0, vh0, vw1, vh1, err = me.baseGui.ViewPosition(tk.v.Name()) if err != nil { log.Printf("verifyRect() ACTUAL FAIL gocui err=%v cuiName=%s v.Name=%s", err, tk.cuiName, tk.v.Name()) return false } // return false } var ok bool = true if vw0 != tk.gocuiSize.w0 { // log.Info("verifyRect() FIXING w0", tk.cuiName, vw0, vw1, vh0, vh1, tk.gocuiSize.w0, "w0 =", vw0) tk.gocuiSize.w0 = vw0 ok = false } if vw1 != tk.gocuiSize.w1 { // log.Info("verifyRect() FIXING w1", tk.cuiName, vw0, vw1, vh0, vh1, tk.gocuiSize.w1, "w1 =", vw1) tk.gocuiSize.w1 = vw1 ok = false } if vh0 != tk.gocuiSize.h0 { // log.Info("verifyRect() FIXING h0", tk.cuiName, vw0, vw1, vh0, vh1, tk.gocuiSize.h0) tk.gocuiSize.h0 = vh0 ok = false } if vh1 != tk.gocuiSize.h1 { // log.Info("verifyRect() FIXING h1", tk.cuiName, vw0, vw1, vh0, vh1, tk.gocuiSize.h1) tk.gocuiSize.h1 = vh1 ok = false } if !ok { // log.Info("verifyRect() NEED TO FIX RECT HERE", tk.cuiName) // tk.dumpWidget("verifyRect() FIXME") } // log.Printf("verifyRect() OK cuiName=%s v.Name=%s", tk.cuiName, tk.v.Name()) return true } func (tk *guiWidget) makeTK(ddItems []string) { items := strings.Join(ddItems, "\n") var err error tk.labelN = items tk.SetText(items) tk.gocuiSize.w0 = 100 tk.gocuiSize.w1 = 120 tk.gocuiSize.h0 = 15 tk.gocuiSize.h1 = 18 tk.v, err = me.baseGui.SetView(tk.cuiName, tk.gocuiSize.w0, tk.gocuiSize.h0, tk.gocuiSize.w1, tk.gocuiSize.h1, 0) if err != nil { log.Info("makeTK() err", err) return } if tk.v == nil { return } tk.v.Wrap = true tk.v.Frame = true tk.v.Clear() fmt.Fprint(tk.v, items) tk.Show() } func addDropdown() *tree.Node { return addDropdownNew(-222) } var notsure *guiWidget // use this to test code ideas func theNotsure(g *gocui.Gui, v *gocui.View) error { log.Info("got keypress 2. now what?") // closes anything under your mouse w, h := g.MousePosition() if notsure == nil { // notsure = makeDropdownView("addWidget() notsure") notsure = addDropdownTK(-118) notsure.makeTK([]string{"apple", "pear"}) } notsure.MoveToOffset(w+10, h+10) // notsure.SetText("theNotsure") notsure.drawView() notsure.Show() for _, tk := range findByXY(w, h) { // vx0, vy0, vx1, vy1, err := g.ViewPosition("msg") log.Log(GOCUI, "verify rect:", tk.v.Name()) tk.verifyRect() if tk.node.WidgetType == widget.Stdout { } } return nil } func theHide(g *gocui.Gui, v *gocui.View) error { var w *guiWidget w = me.treeRoot.TK.(*guiWidget) w.hideWidgets() return nil } func theShow(g *gocui.Gui, v *gocui.View) error { var w *guiWidget w = me.treeRoot.TK.(*guiWidget) w.showWidgets() return nil } func doExit(g *gocui.Gui, v *gocui.View) error { standardExit() return nil } func doPanic(g *gocui.Gui, v *gocui.View) error { log.Log(GOCUI, "do panic() here") standardClose() panic("forced panic in gocui") } func dumpWidgets(g *gocui.Gui, v *gocui.View) error { me.treeRoot.ListWidgets() tk := me.logStdout // msg := fmt.Sprintf("test out kb %d\n", ecount) // tk.Write([]byte(msg)) if tk == nil { log.Log(ERROR, "tk = nil") } if tk.v == nil { log.Log(ERROR, "tk.v = nil") } else { log.Log(ERROR, "setting log.CaptureMode(tk.v)") log.Log(ERROR, "setting log.CaptureMode(tk.v)") log.CaptureMode(tk) } return nil } func dumpWidgetPlacement(g *gocui.Gui, v *gocui.View) error { w := me.treeRoot.TK.(*guiWidget) w.dumpTree("MM") return nil } func openDebuggger(g *gocui.Gui, v *gocui.View) error { me.myTree.SendEnableDebugger() return nil } func theFind(g *gocui.Gui, v *gocui.View) error { w, h := g.MousePosition() for _, tk := range findByXY(w, h) { // tk.v.BgColor = gocui.ColorGreen tk.dumpWidget("theFind()") tk.verifyRect() } return nil } // is run whenever anyone hits 'd' (in an open space) func theLetterD(g *gocui.Gui, v *gocui.View) error { // widgets that don't have physical existance in // a display toolkit are hidden. In the case // of gocui, they are set as not 'visible' and put offscreen // or have the size set to zero // (hopefully anyway) lots of things with the toolkit // still don't work fakeStartWidth = me.FakeW fakeStartHeight = me.TabH + me.FramePadH if showDebug { showFake() showDebug = false } else { hideFake() showDebug = true } return nil } func theHelp(g *gocui.Gui, v *gocui.View) error { if me.showHelp { log.Info("Show the help!") showHelp() } else { log.Info("Hide the help!") hideHelp() } return nil } func widgetRefresh(g *gocui.Gui, v *gocui.View) error { log.Log(GOCUI, "todo: refresh windows here") return nil } func doWindow(g *gocui.Gui, v *gocui.View) error { log.Log(GOCUI, "todo: close all windows here") return nil } // todo: find and give credit to the person that I found this patch in their forked repo // handle ctrl+z func handle_ctrl_z(g *gocui.Gui, v *gocui.View) error { gocui.Suspend() log.Info("got ctrl+z") syscall.Kill(syscall.Getpid(), syscall.SIGSTOP) log.Info("got ctrl+z syscall() done") gocui.Resume() return nil }