diff --git a/_examples/mouse.go b/_examples/mouse.go index bc38e47..762f8eb 100644 --- a/_examples/mouse.go +++ b/_examples/mouse.go @@ -33,12 +33,24 @@ func main() { } } +var initialMouseX, initialMouseY, xOffset, yOffset int +var globalMouseDown, msgMouseDown, movingMsg bool + func layout(g *gocui.Gui) error { + maxX, maxY := g.Size() + if _, err := g.View("msg"); msgMouseDown && err == nil { + moveMsg(g) + } + if v, err := g.SetView("global", -1, -1, maxX, maxY, 0); err != nil { + if !errors.Is(err, gocui.ErrUnknownView) { + return err + } + v.Frame = false + } if v, err := g.SetView("but1", 2, 2, 22, 7, 0); err != nil { if !errors.Is(err, gocui.ErrUnknownView) { return err } - v.Highlight = true v.SelBgColor = gocui.ColorGreen v.SelFgColor = gocui.ColorBlack fmt.Fprintln(v, "Button 1 - line 1") @@ -53,11 +65,11 @@ func layout(g *gocui.Gui) error { if !errors.Is(err, gocui.ErrUnknownView) { return err } - v.Highlight = true v.SelBgColor = gocui.ColorGreen v.SelFgColor = gocui.ColorBlack fmt.Fprintln(v, "Button 2 - line 1") } + updateHighlightedView(g) return nil } @@ -70,13 +82,13 @@ func keybindings(g *gocui.Gui) error { return err } } - if err := g.SetKeybinding("msg", gocui.MouseLeft, gocui.ModNone, delMsg); err != nil { + if err := g.SetKeybinding("", gocui.MouseRelease, gocui.ModNone, mouseUp); err != nil { return err } - if err := g.SetKeybinding("", gocui.MouseRight, gocui.ModNone, delMsg); err != nil { + if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, globalDown); err != nil { return err } - if err := g.SetKeybinding("", gocui.MouseMiddle, gocui.ModNone, delMsg); err != nil { + if err := g.SetKeybinding("msg", gocui.MouseLeft, gocui.ModNone, msgDown); err != nil { return err } return nil @@ -100,17 +112,80 @@ func showMsg(g *gocui.Gui, v *gocui.View) error { } maxX, maxY := g.Size() - if v, err := g.SetView("msg", maxX/2-10, maxY/2, maxX/2+10, maxY/2+2, 0); err != nil { + if v, err := g.SetView("msg", maxX/2-10, maxY/2, maxX/2+10, maxY/2+2, 0); err == nil || errors.Is(err, gocui.ErrUnknownView) { + v.Clear() + v.SelBgColor = gocui.ColorCyan + v.SelFgColor = gocui.ColorBlack + fmt.Fprintln(v, l) + } + return nil +} + +func updateHighlightedView(g *gocui.Gui) { + mx, my := g.MousePosition() + for _, view := range g.Views() { + view.Highlight = false + } + if v, err := g.ViewByPosition(mx, my); err == nil { + v.Highlight = true + } +} + +func moveMsg(g *gocui.Gui) { + mx, my := g.MousePosition() + if !movingMsg && (mx != initialMouseX || my != initialMouseY) { + movingMsg = true + } + g.SetView("msg", mx-xOffset, my-yOffset, mx-xOffset+20, my-yOffset+2, 0) +} + +func msgDown(g *gocui.Gui, v *gocui.View) error { + initialMouseX, initialMouseY = g.MousePosition() + if vx, vy, _, _, err := g.ViewPosition("msg"); err == nil { + xOffset = initialMouseX - vx + yOffset = initialMouseY - vy + msgMouseDown = true + } + return nil +} + +func globalDown(g *gocui.Gui, v *gocui.View) error { + mx, my := g.MousePosition() + if vx0, vy0, vx1, vy1, err := g.ViewPosition("msg"); err == nil { + if mx >= vx0 && mx <= vx1 && my >= vy0 && my <= vy1 { + return msgDown(g, v) + } + } + globalMouseDown = true + maxX, _ := g.Size() + msg := fmt.Sprintf("Mouse down at: %d,%d", mx, my) + x := mx - len(msg)/2 + if x < 0 { + x = 0 + } else if x+len(msg)+1 > maxX-1 { + x = maxX - 1 - len(msg) - 1 + } + 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 } - fmt.Fprintln(v, l) + v.WriteString(msg) } return nil } -func delMsg(g *gocui.Gui, v *gocui.View) error { - // Error check removed, because delete could be called multiple times with the above keybindings - g.DeleteView("msg") +func mouseUp(g *gocui.Gui, v *gocui.View) error { + if msgMouseDown { + msgMouseDown = false + if movingMsg { + movingMsg = false + return nil + } else { + g.DeleteView("msg") + } + } else if globalMouseDown { + globalMouseDown = false + g.DeleteView("globalDown") + } return nil } diff --git a/gui.go b/gui.go index 76bcb5c..a847aa0 100644 --- a/gui.go +++ b/gui.go @@ -76,6 +76,9 @@ type Gui struct { testCounter int // used for testing synchronization testNotify chan struct{} + // The position of the mouse + mouseX, mouseY int + // BgColor and FgColor allow to configure the background and foreground // colors of the GUI. BgColor, FgColor, FrameColor Attribute @@ -141,6 +144,7 @@ func NewGui(mode OutputMode, supportOverlaps bool) (*Gui, error) { g.maxX, g.maxY = screen.Size() } + g.mouseX, g.mouseY = -1, -1 g.BgColor, g.FgColor, g.FrameColor = ColorDefault, ColorDefault, ColorDefault g.SelBgColor, g.SelFgColor, g.SelFrameColor = ColorDefault, ColorDefault, ColorDefault @@ -165,6 +169,12 @@ func (g *Gui) Size() (x, y int) { return g.maxX, g.maxY } +// MousePosition returns the last position of the mouse. +// If no mouse event was triggered yet MousePosition will return -1, -1. +func (g *Gui) MousePosition() (x, y int) { + return g.mouseX, g.mouseY +} + // SetRune writes a rune at the given point, relative to the top-left // corner of the terminal. It checks if the position is valid and applies // the given colors. @@ -881,6 +891,8 @@ func (g *Gui) onKey(ev *gocuiEvent) error { } case eventMouse: mx, my := ev.MouseX, ev.MouseY + g.mouseX = mx + g.mouseY = my v, err := g.ViewByPosition(mx, my) if err != nil { break diff --git a/tcell_driver.go b/tcell_driver.go index 38c15ba..768c324 100644 --- a/tcell_driver.go +++ b/tcell_driver.go @@ -174,7 +174,7 @@ func pollEvent() gocuiEvent { case *tcell.EventMouse: x, y := tev.Position() button := tev.Buttons() - mouseKey := MouseRelease + mouseKey := Key(0) mouseMod := ModNone // process mouse wheel if button&tcell.WheelUp != 0 { @@ -199,19 +199,21 @@ func pollEvent() gocuiEvent { if button != tcell.ButtonNone && lastMouseKey == tcell.ButtonNone { lastMouseKey = button lastMouseMod = tev.Modifiers() + switch tev.Buttons() { + case tcell.ButtonPrimary: + mouseKey = MouseLeft + case tcell.ButtonSecondary: + mouseKey = MouseRight + case tcell.ButtonMiddle: + mouseKey = MouseMiddle + } + mouseMod = Modifier(lastMouseMod) } switch tev.Buttons() { case tcell.ButtonNone: if lastMouseKey != tcell.ButtonNone { - switch lastMouseKey { - case tcell.ButtonPrimary: - mouseKey = MouseLeft - case tcell.ButtonSecondary: - mouseKey = MouseRight - case tcell.ButtonMiddle: - mouseKey = MouseMiddle - } + mouseKey = MouseRelease mouseMod = Modifier(lastMouseMod) lastMouseMod = tcell.ModNone lastMouseKey = tcell.ButtonNone