// Copyright 2017-2025 WIT.COM Inc. All rights reserved. // Use of this source code is governed by the GPL 3.0 package main import ( "slices" "github.com/awesome-gocui/gocui" log "go.wit.com/log" "go.wit.com/widget" ) /* gocui defines the offset like this: width -> increases to the right ---------------------------------- hieght | H = 1 | increases | | | | W = 1 W = 18 | | | | v | H = 5 | downwards ------------------------------------- */ // change over to this name // returns all the widgets under (X,H) that are visible func findByXY(w int, h int) []*guiWidget { rootW := me.treeRoot.TK.(*guiWidget) // this searches the binary tree recursively (function is right below) return rootW.findByXYreal(w, h) } func (r rectType) inRect(w int, h int) bool { if (r.w0 <= w) && (w <= r.w1) && (r.h0 <= h) && (h <= r.h1) { return true } return false } // this checks a widget to see if it is under (W,H), then checks the widget's children // anything that matches is passed back as an array of widgets func (tk *guiWidget) findByXYreal(w int, h int) []*guiWidget { var widgets []*guiWidget // if !tk.Visible() { // ignore widgets that are not visible // } else { // check the location to see if this is under (W,H) // if it is, return this widget // if (tk.gocuiSize.w0 <= w) && (w <= tk.gocuiSize.w1) && // (tk.gocuiSize.h0 <= h) && (h <= tk.gocuiSize.h1) { // if tk.gocuiSize.inRect(w, h) { // widgets = append(widgets, tk) // } else { // if (tk.full.w0 <= w) && (w <= tk.full.w1) && // (tk.full.h0 <= h) && (h <= tk.full.h1) { if tk.full.inRect(w, h) { widgets = append(widgets, tk) } // log.Log(GOCUI, "findByXY() found", widget.node.WidgetType, w, h) // } // } // tk.verifyRect() // search through the children widgets in the binary tree for _, child := range tk.children { widgets = append(widgets, child.findByXYreal(w, h)...) } return widgets } // returns all the windows from the root of the binary tree func findWindows() []*guiWidget { rootW := me.treeRoot.TK.(*guiWidget) return rootW.findWindows() } // walk the binary tree looking for WidgetType == Window func (tk *guiWidget) findWindows() []*guiWidget { var found []*guiWidget if tk.node.WidgetType == widget.Window { found = append(found, tk) } for _, child := range tk.children { found = append(found, child.findWindows()...) } return found } // find the BG widget. // This widget is always in the background and covers the whole screen. // gocui seems to not return mouse events unless there is something there func (tk *guiWidget) findBG() *guiWidget { if tk.node.WidgetType == widget.Stdout { if tk.node.WidgetId != me.stdout.wId { tk.isBG = true return tk } } for _, child := range tk.children { if found := child.findBG(); found != nil { return found } } return nil } // used by gocui.TabKey to rotate through the windows func findNextWindow() *guiWidget { var found bool if len(me.allwin) == 0 { return nil } for _, tk := range me.allwin { if tk.window.active { found = true continue } if found { return tk } } // at the end, loop to the beginning return me.allwin[0] } func findWindowUnderMouse() *guiWidget { w, h := me.baseGui.MousePosition() if len(me.allwin) != len(findWindows()) { me.allwin = findWindows() } // if the stdout window is on top, check it first if me.stdout.outputOnTop { if me.stdout.tk.full.inRect(w, h) { // log.Info(fmt.Sprintf("findWindowUnderMouse() found %s stdout on top (%dx%d)", me.stdout.tk.cuiName, w, h)) return me.stdout.tk } } /* // print out the window list for _, tk := range me.allwin { log.Info("findWindowUnderMouse() print:", tk.labelN, tk.window.active, tk.window.order) } */ // now check if the active window is below the mouse for _, tk := range me.allwin { if tk.window.active { if tk.full.inRect(w, h) { // log.Info(fmt.Sprintf("findWindowUnderMouse() found %s active window (%dx%d)", tk.cuiName, w, h)) return tk } } } // well, just find any window then // sorting by order might work? slices.SortFunc(me.allwin, func(a, b *guiWidget) int { return a.window.order - b.window.order }) /* // print out the window list for _, tk := range me.allwin { log.Info("findWindowUnderMouse() print:", tk.labelN, tk.window.active, tk.window.order) } */ for _, win := range me.allwin { if win.full.inRect(w, h) { // log.Info(fmt.Sprintf("findWindowUnderMouse() found %s window (%dx%d)", win.cuiName, w, h)) return win } } // okay, no window. maybe the stdout is there? if me.stdout.tk.full.inRect(w, h) { // log.Info(fmt.Sprintf("findWindowUnderMouse() found %s stdout (%dx%d)", me.stdout.tk.cuiName, w, h)) return me.stdout.tk } // geez. nothing! maybe auto return stdout? log.Info("findWindowUnderMouse() no window found at", w, h) return nil } // returns the "highest priority widget under the mouse func findUnderMouse() *guiWidget { w, h := me.baseGui.MousePosition() widgets := findByXY(w, h) // search through all the widgets that were below the mouse click var found *guiWidget for _, w := range widgets { // prioritize window buttons. This means if some code covers // up the window widgets, then it will ignore everything else // and allow the user (hopefully) to redraw or switch windows // TODO: display the window widgets on top if w.node.WidgetType == widget.Window { return w } } // return anything else that is interactive for _, w := range widgets { if w.node.WidgetType == widget.Button { return w } if w.node.WidgetType == widget.Combobox { return w } if w.node.WidgetType == widget.Checkbox { return w } w.dumpWidget("findUnderMouse() found something unknown") found = w } // maybe something else was found return found } // panics. todo: fix ctrl-mouse click? // find the widget under the mouse click func ctrlDown(g *gocui.Gui, v *gocui.View) error { var found *guiWidget // var widgets []*node // var f func (n *node) found = findUnderMouse() if me.ctrlDown == nil { setupCtrlDownWidget() var tk *guiWidget tk = me.ctrlDown.TK.(*guiWidget) tk.labelN = found.String() tk.cuiName = "ctrlDown" // me.ctrlDown.parent = me.rootNode } var tk *guiWidget tk = me.ctrlDown.TK.(*guiWidget) if found == nil { found = me.treeRoot.TK.(*guiWidget) } tk.labelN = found.String() newR := found.realGocuiSize() tk.gocuiSize.w0 = newR.w0 tk.gocuiSize.h0 = newR.h0 tk.gocuiSize.w1 = newR.w1 tk.gocuiSize.h1 = newR.h1 return nil }