// Copyright 2017-2025 WIT.COM Inc. All rights reserved. // Use of this source code is governed by the GPL 3.0 // this file implements a libnotify-like menu // also there is SIGWINCH resizing package main import ( "fmt" "time" "github.com/awesome-gocui/gocui" log "go.wit.com/log" "go.wit.com/toolkits/tree" "go.wit.com/widget" ) // create a new widget in the binary tree func makeNewInternalWidget(wId int) *guiWidget { if me.treeRoot == nil { log.Info("GOGUI Init ERROR. treeRoot == nil") return nil } n := new(tree.Node) n.WidgetType = widget.Flag n.WidgetId = wId n.ParentId = 0 // store the internal toolkit information tk := new(guiWidget) tk.frame = true tk.node = n tk.node.Parent = me.treeRoot // set the name used by gocui to the id tk.cuiName = fmt.Sprintf("%d DR", wId) tk.setColorInput() // add this new widget on the binary tree tk.parent = me.treeRoot.TK.(*guiWidget) if tk.parent == nil { panic("makeNewFlagWidget() didn't get treeRoot guiWidget") } else { tk.parent.children = append(tk.parent.children, tk) } n.TK = tk return tk } func makeNotifyClock() { if me.treeRoot == nil { log.Info("gogui makeClock() error. treeRoot == nil") return } me.notify.clock.tk = makeNewInternalWidget(me.notify.clock.wId) // me.notify.clock.tk.dumpWidget("init() clock") me.notify.clock.tk.MoveToOffset(0, 0) me.notify.clock.tk.labelN = time.Now().Format("15:04:05") me.notify.clock.tk.frame = false me.notify.clock.tk.setColorLabel() me.notify.clock.tk.Show() me.notify.clock.active = true // me.notify.clock.tk.dumpWidget("notifyClock()") } func makeNotifyIcon() { if me.treeRoot == nil { log.Info("gogui makeClock() error. treeRoot == nil") return } me.notify.icon.tk = makeNewInternalWidget(me.notify.icon.wId) // me.notify.icon.tk.dumpWidget("init() menu") w, _ := me.baseGui.Size() me.notify.icon.tk.MoveToOffset(w-5, me.notify.icon.offsetH) me.notify.icon.tk.labelN = "[ ]" me.notify.icon.tk.frame = false me.notify.icon.tk.setColorNotifyIcon() me.notify.icon.tk.Show() me.notify.icon.active = true // me.notify.icon.tk.dumpWidget("notifyIcon()") } func libNotifyUpdate() { if me.baseGui == nil { log.Info("libNotifyUpdate error baseGui == nil") return } // refresh GOCUI me.baseGui.Update(testRefresh) // me.baseGui.UpdateAsync(testRefresh) // Async option. probably don't need this? if me.notify.clock.tk == nil { log.Info("libNotifyUpdate error clock.tk == nil") return } // check for SIGWINCH. If so, move the libnotify clock w, h := me.baseGui.Size() if me.winchW != w || me.winchH != h { if me.winchW == 0 && me.winchH == 0 { // this isn't really SIGWINCH. This is the app starting } else { log.Printf("gocui: long live SIGWINCH! (w,h) is now (%d,%d)\n", w, h) } me.winchW = w me.winchH = h me.notify.clock.tk.MoveToOffset(w-me.notify.clock.offsetW, me.notify.clock.offsetH) me.notify.clock.tk.Hide() me.notify.clock.tk.Show() sigWinchBG() sigWinchIcon() } // update the time me.notify.clock.tk.v.Clear() me.notify.clock.tk.labelN = time.Now().Format("15:04:05") me.notify.clock.tk.v.WriteString(me.notify.clock.tk.labelN) hardDrawAtgocuiSize(me.notify.clock.tk) // hardDrawUnderMouse(me.notify.clock.tk, "clock") // log.Info("libNotifyUpdate updated clock", me.notify.clock.tk.labelN) if me.notify.icon.tk == nil { log.Info("libNotifyUpdate error menu.tk == nil") return } if me.notify.icon.tk.v == nil { log.Info("libNotifyUpdate error menu.tk.v == nil") return } // update the menu hardDrawAtgocuiSize(me.notify.icon.tk) me.notify.icon.tk.setColorNotifyIcon() me.baseGui.SetViewOnTop(me.notify.icon.tk.v.Name()) me.baseGui.SetViewOnTop(me.notify.clock.tk.v.Name()) } func setNotifyIconText(s string) { me.notify.icon.tk.v.Clear() me.notify.icon.tk.labelN = s me.notify.icon.tk.v.WriteString(me.notify.icon.tk.labelN) hardDrawAtgocuiSize(me.notify.icon.tk) me.notify.icon.tk.setColorNotifyIcon() me.baseGui.SetViewOnTop(me.notify.icon.tk.v.Name()) log.Info("setNotifyIconText() updated menu to:", me.notify.icon.tk.labelN) } // in the very end of redrawing things, this will place the help and stdout on the top or botton // depending on the state the user has chosen func setThingsOnTop() { if me.showHelp { // terrible variable name. FIXME // log.Info("help does not exist") } else { me.baseGui.SetViewOnTop("help") } if me.notify.clock.tk != nil { me.baseGui.SetViewOnTop(me.notify.clock.tk.v.Name()) } if me.notify.icon.tk != nil { if me.notify.icon.tk.v != nil { me.baseGui.SetViewOnTop(me.notify.icon.tk.v.Name()) } } if me.stdout.tk == nil { makeOutputWidget(me.baseGui, "from setThingsOnTop()") } if me.stdout.tk == nil { return } if me.stdout.tk.v == nil { return } if me.dark { me.stdout.tk.v.FgColor = gocui.ColorWhite me.stdout.tk.v.BgColor = gocui.ColorBlack } else { me.stdout.tk.v.FgColor = gocui.ColorBlack me.stdout.tk.v.BgColor = gocui.AttrNone } if me.stdout.outputOnTop { me.baseGui.SetViewOnTop("msg") } else { me.baseGui.SetViewOnBottom("msg") } if me.stdout.startOnscreen { // log.Info("THIS TRIGGERS STDOUT") // todo: make a proper init() & move this there me.stdout.tk.relocateStdout(me.stdout.lastW, me.stdout.lastH) me.stdout.startOnscreen = false } setBottomBG() } // useful for debuggging func hardDrawUnderMouse(tk *guiWidget, name string) { if tk.v != nil { tk.Hide() } w, h := me.baseGui.MousePosition() a := w b := h c := w + 8 d := h + 4 var err error tk.v, err = me.baseGui.SetView(tk.cuiName, a, b, c, d, 0) if err == nil { log.Info("hardDrawUnderMouse() err ok widget", tk.cuiName) tk.dumpWidget("hard draw err") } tk.v.Frame = false tk.v.Clear() tk.v.WriteString(tk.labelN + "\n" + name) } func hardDrawAtgocuiSize(tk *guiWidget) { if tk.v != nil { tk.Hide() } a := tk.gocuiSize.w0 b := tk.gocuiSize.h0 c := tk.gocuiSize.w1 d := tk.gocuiSize.h1 var err error tk.v, err = me.baseGui.SetView(tk.cuiName, a, b, c, d, 0) if err == nil { log.Info("hardDrawAtgocuiSize() err ok widget", tk.cuiName) tk.dumpWidget("hard draw err") } tk.v.Frame = false tk.v.Clear() tk.v.WriteString(tk.labelN) log.Verbose("hardDrawAtgocuiSize() err ok widget", tk.cuiName, a, b, c, d, tk.v.Name()) } func sigWinchIcon() { w, _ := me.baseGui.Size() me.notify.icon.tk.MoveToOffset(w-me.notify.icon.offsetW, me.notify.icon.offsetH) me.notify.icon.tk.Hide() me.notify.icon.tk.Show() } func sigWinchBG() { tk := me.BG.tk w, h := me.baseGui.Size() a := -1 b := -1 c := w + 1 d := h + 1 var err error tk.v, err = me.baseGui.SetView(tk.cuiName, a, b, c, d, 0) if err == nil { if tk.v == nil { tk.dumpWidget("drawView() err") log.Log(ERROR, "drawView() internal plugin error err = nil") } return } log.Log(INFO, "background resized to", a, b, c, d) } // find the "BG" widget and set it to the background on the very very bottom func setBottomBG() { if me.BG.tk == nil { log.Info("background tk widget not initialized") return } tk := me.BG.tk // log.Info("found BG. setting to bottom", tk.cuiName) if tk.v == nil { sigWinchBG() return } if me.dark { tk.v.BgColor = gocui.ColorBlack } else { tk.v.BgColor = gocui.ColorWhite } tk.v.Clear() me.baseGui.SetViewOnBottom(tk.cuiName) w, h := me.baseGui.Size() me.baseGui.SetView(tk.cuiName, -1, -1, w+1, h+1, 0) }