mirror of https://github.com/rivo/tview.git
Added mouse handling
This commit is contained in:
parent
685bf6da76
commit
96875c75b9
120
application.go
120
application.go
|
@ -38,6 +38,9 @@ type Application struct {
|
|||
// Whether or not the application resizes the root primitive.
|
||||
rootFullscreen bool
|
||||
|
||||
// Enable mouse events?
|
||||
enableMouse bool
|
||||
|
||||
// An optional capture function which receives a key event and returns the
|
||||
// event to be forwarded to the default input handler (nil if nothing should
|
||||
// be forwarded).
|
||||
|
@ -62,6 +65,33 @@ type Application struct {
|
|||
// (screen.Init() and draw() will be called implicitly). A value of nil will
|
||||
// stop the application.
|
||||
screenReplacement chan tcell.Screen
|
||||
|
||||
// An optional capture function which receives a mouse event and returns the
|
||||
// event to be forwarded to the default mouse handler (nil if nothing should
|
||||
// be forwarded).
|
||||
mouseCapture func(event EventMouse) EventMouse
|
||||
}
|
||||
|
||||
// EventKey is the key input event info.
|
||||
// This exists for some consistency with EventMouse,
|
||||
// even though it's just an alias to *tcell.EventKey for backwards compatibility.
|
||||
type EventKey = *tcell.EventKey
|
||||
|
||||
// EventMouse is the mouse event info.
|
||||
type EventMouse struct {
|
||||
*tcell.EventMouse
|
||||
Target Primitive
|
||||
Application *Application
|
||||
}
|
||||
|
||||
// IsZero returns true if this is a zero object.
|
||||
func (e EventMouse) IsZero() bool {
|
||||
return e == EventMouse{}
|
||||
}
|
||||
|
||||
// SetFocus will set focus to the primitive.
|
||||
func (e EventMouse) SetFocus(p Primitive) {
|
||||
e.Application.SetFocus(p)
|
||||
}
|
||||
|
||||
// NewApplication creates and returns a new application.
|
||||
|
@ -93,6 +123,22 @@ func (a *Application) GetInputCapture() func(event *tcell.EventKey) *tcell.Event
|
|||
return a.inputCapture
|
||||
}
|
||||
|
||||
// SetMouseCapture sets a function which captures mouse events before they are
|
||||
// forwarded to the appropriate mouse event handler.
|
||||
// This function can then choose to forward that event (or a
|
||||
// different one) by returning it or stop the event processing by returning
|
||||
// nil.
|
||||
func (a *Application) SetMouseCapture(capture func(event EventMouse) EventMouse) *Application {
|
||||
a.mouseCapture = capture
|
||||
return a
|
||||
}
|
||||
|
||||
// GetMouseCapture returns the function installed with SetMouseCapture() or nil
|
||||
// if no such function has been installed.
|
||||
func (a *Application) GetMouseCapture() func(event EventMouse) EventMouse {
|
||||
return a.mouseCapture
|
||||
}
|
||||
|
||||
// SetScreen allows you to provide your own tcell.Screen object. For most
|
||||
// applications, this is not needed and you should be familiar with
|
||||
// tcell.Screen when using this function.
|
||||
|
@ -121,6 +167,13 @@ func (a *Application) SetScreen(screen tcell.Screen) *Application {
|
|||
return a
|
||||
}
|
||||
|
||||
// EnableMouse enables mouse events.
|
||||
func (a *Application) EnableMouse() {
|
||||
a.Lock()
|
||||
a.enableMouse = true
|
||||
a.Unlock()
|
||||
}
|
||||
|
||||
// Run starts the application and thus the event loop. This function returns
|
||||
// when Stop() was called.
|
||||
func (a *Application) Run() error {
|
||||
|
@ -138,6 +191,9 @@ func (a *Application) Run() error {
|
|||
a.Unlock()
|
||||
return err
|
||||
}
|
||||
if a.enableMouse {
|
||||
a.screen.EnableMouse()
|
||||
}
|
||||
}
|
||||
|
||||
// We catch panics to clean up because they mess up the terminal.
|
||||
|
@ -207,13 +263,15 @@ EventLoop:
|
|||
break EventLoop
|
||||
}
|
||||
|
||||
a.RLock()
|
||||
p := a.focus
|
||||
inputCapture := a.inputCapture
|
||||
mouseCapture := a.mouseCapture
|
||||
screen := a.screen
|
||||
a.RUnlock()
|
||||
|
||||
switch event := event.(type) {
|
||||
case *tcell.EventKey:
|
||||
a.RLock()
|
||||
p := a.focus
|
||||
inputCapture := a.inputCapture
|
||||
a.RUnlock()
|
||||
|
||||
// Intercept keys.
|
||||
if inputCapture != nil {
|
||||
event = inputCapture(event)
|
||||
|
@ -238,14 +296,32 @@ EventLoop:
|
|||
}
|
||||
}
|
||||
case *tcell.EventResize:
|
||||
a.RLock()
|
||||
screen := a.screen
|
||||
a.RUnlock()
|
||||
if screen == nil {
|
||||
continue
|
||||
}
|
||||
screen.Clear()
|
||||
a.draw()
|
||||
case *tcell.EventMouse:
|
||||
atX, atY := event.Position()
|
||||
ptarget := a.GetPrimitiveAtPoint(atX, atY) // p under mouse.
|
||||
if ptarget == nil {
|
||||
ptarget = p // Fallback to focused.
|
||||
}
|
||||
event2 := EventMouse{event, ptarget, a}
|
||||
|
||||
// Intercept event.
|
||||
if mouseCapture != nil {
|
||||
event2 = mouseCapture(event2)
|
||||
if event2.IsZero() {
|
||||
a.draw()
|
||||
continue // Don't forward event.
|
||||
}
|
||||
}
|
||||
|
||||
if handler := ptarget.MouseHandler(); handler != nil {
|
||||
handler(event2)
|
||||
a.draw()
|
||||
}
|
||||
}
|
||||
|
||||
// If we have updates, now is the time to execute them.
|
||||
|
@ -261,6 +337,34 @@ EventLoop:
|
|||
return nil
|
||||
}
|
||||
|
||||
func findAtPoint(atX, atY int, p Primitive) Primitive {
|
||||
x, y, w, h := p.GetRect()
|
||||
if atX < x || atY < y {
|
||||
return nil
|
||||
}
|
||||
if atX >= x+w || atY >= y+h {
|
||||
return nil
|
||||
}
|
||||
bestp := p
|
||||
for _, pchild := range p.GetChildren() {
|
||||
x := findAtPoint(atX, atY, pchild)
|
||||
if x != nil {
|
||||
// Always overwrite if we find another one,
|
||||
// this is because if any overlap, the last one is "on top".
|
||||
bestp = x
|
||||
}
|
||||
}
|
||||
return bestp
|
||||
}
|
||||
|
||||
// GetPrimitiveAtPoint returns the Primitive at the specified point, or nil.
|
||||
// Note that this only works with a valid hierarchy of primitives (children)
|
||||
func (a *Application) GetPrimitiveAtPoint(atX, atY int) Primitive {
|
||||
a.RLock()
|
||||
defer a.RUnlock()
|
||||
return findAtPoint(atX, atY, a.root)
|
||||
}
|
||||
|
||||
// Stop stops the application, causing Run() to return.
|
||||
func (a *Application) Stop() {
|
||||
a.Lock()
|
||||
|
|
49
box.go
49
box.go
|
@ -58,6 +58,11 @@ type Box struct {
|
|||
|
||||
// An optional function which is called before the box is drawn.
|
||||
draw func(screen tcell.Screen, x, y, width, height int) (int, int, int, int)
|
||||
|
||||
// An optional capture function which receives a mouse event and returns the
|
||||
// event to be forwarded to the primitive's default mouse event handler (nil if
|
||||
// nothing should be forwarded).
|
||||
mouseCapture func(event EventMouse) EventMouse
|
||||
}
|
||||
|
||||
// NewBox returns a Box without a border.
|
||||
|
@ -192,6 +197,45 @@ func (b *Box) GetInputCapture() func(event *tcell.EventKey) *tcell.EventKey {
|
|||
return b.inputCapture
|
||||
}
|
||||
|
||||
// WrapMouseHandler wraps a mouse event handler (see MouseHandler()) with the
|
||||
// functionality to capture input (see SetMouseCapture()) before passing it
|
||||
// on to the provided (default) event handler.
|
||||
//
|
||||
// This is only meant to be used by subclassing primitives.
|
||||
func (b *Box) WrapMouseHandler(mouseHandler func(EventMouse)) func(EventMouse) {
|
||||
return func(event EventMouse) {
|
||||
if b.mouseCapture != nil {
|
||||
event = b.mouseCapture(event)
|
||||
}
|
||||
if !event.IsZero() && mouseHandler != nil {
|
||||
mouseHandler(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MouseHandler returns nil.
|
||||
func (b *Box) MouseHandler() func(event EventMouse) {
|
||||
return b.WrapMouseHandler(nil)
|
||||
}
|
||||
|
||||
// SetMouseCapture installs a function which captures events before they are
|
||||
// forwarded to the primitive's default event handler. This function can
|
||||
// then choose to forward that event (or a different one) to the default
|
||||
// handler by returning it. If nil is returned, the default handler will not
|
||||
// be called.
|
||||
//
|
||||
// Providing a nil handler will remove a previously existing handler.
|
||||
func (b *Box) SetMouseCapture(capture func(EventMouse) EventMouse) *Box {
|
||||
b.mouseCapture = capture
|
||||
return b
|
||||
}
|
||||
|
||||
// GetMouseCapture returns the function installed with SetMouseCapture() or nil
|
||||
// if no such function has been installed.
|
||||
func (b *Box) GetMouseCapture() func(EventMouse) EventMouse {
|
||||
return b.mouseCapture
|
||||
}
|
||||
|
||||
// SetBackgroundColor sets the box's background color.
|
||||
func (b *Box) SetBackgroundColor(color tcell.Color) *Box {
|
||||
b.backgroundColor = color
|
||||
|
@ -353,3 +397,8 @@ func (b *Box) HasFocus() bool {
|
|||
func (b *Box) GetFocusable() Focusable {
|
||||
return b.focus
|
||||
}
|
||||
|
||||
// GetChildren gets the children.
|
||||
func (b *Box) GetChildren() []Primitive {
|
||||
return nil
|
||||
}
|
||||
|
|
12
button.go
12
button.go
|
@ -135,3 +135,15 @@ func (b *Button) InputHandler() func(event *tcell.EventKey, setFocus func(p Prim
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
// InputHandler returns the handler for this primitive.
|
||||
func (b *Button) MouseHandler() func(event EventMouse) {
|
||||
return b.WrapMouseHandler(func(event EventMouse) {
|
||||
// Process mouse event.
|
||||
if event.Buttons()&tcell.Button1 != 0 {
|
||||
if b.selected != nil {
|
||||
b.selected()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
8
flex.go
8
flex.go
|
@ -195,3 +195,11 @@ func (f *Flex) HasFocus() bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *Flex) GetChildren() []Primitive {
|
||||
children := make([]Primitive, len(f.items))
|
||||
for i, item := range f.items {
|
||||
children[i] = item.Item
|
||||
}
|
||||
return children
|
||||
}
|
||||
|
|
14
form.go
14
form.go
|
@ -600,3 +600,17 @@ func (f *Form) focusIndex() int {
|
|||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (f *Form) GetChildren() []Primitive {
|
||||
children := make([]Primitive, len(f.items)+len(f.buttons))
|
||||
i := 0
|
||||
for _, item := range f.items {
|
||||
children[i] = item
|
||||
i++
|
||||
}
|
||||
for _, button := range f.buttons {
|
||||
children[i] = button
|
||||
i++
|
||||
}
|
||||
return children
|
||||
}
|
||||
|
|
4
frame.go
4
frame.go
|
@ -155,3 +155,7 @@ func (f *Frame) HasFocus() bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *Frame) GetChildren() []Primitive {
|
||||
return []Primitive{f.primitive}
|
||||
}
|
||||
|
|
8
grid.go
8
grid.go
|
@ -660,3 +660,11 @@ func (g *Grid) Draw(screen tcell.Screen) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Grid) GetChildren() []Primitive {
|
||||
children := make([]Primitive, len(g.items))
|
||||
for i, item := range g.items {
|
||||
children[i] = item.Item
|
||||
}
|
||||
return children
|
||||
}
|
||||
|
|
4
modal.go
4
modal.go
|
@ -169,3 +169,7 @@ func (m *Modal) Draw(screen tcell.Screen) {
|
|||
m.frame.SetRect(x, y, width, height)
|
||||
m.frame.Draw(screen)
|
||||
}
|
||||
|
||||
func (m *Modal) GetChildren() []Primitive {
|
||||
return []Primitive{m.frame}
|
||||
}
|
||||
|
|
12
pages.go
12
pages.go
|
@ -278,3 +278,15 @@ func (p *Pages) Draw(screen tcell.Screen) {
|
|||
page.Item.Draw(screen)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Pages) GetChildren() []Primitive {
|
||||
var children []Primitive
|
||||
for _, page := range p.pages {
|
||||
// Considering invisible pages as not children.
|
||||
// Even though we track all the pages, not all are "children" currently.
|
||||
if page.Visible {
|
||||
children = append(children, page.Item)
|
||||
}
|
||||
}
|
||||
return children
|
||||
}
|
||||
|
|
13
primitive.go
13
primitive.go
|
@ -43,4 +43,17 @@ type Primitive interface {
|
|||
|
||||
// GetFocusable returns the item's Focusable.
|
||||
GetFocusable() Focusable
|
||||
|
||||
// GetChildren gets the children.
|
||||
GetChildren() []Primitive
|
||||
|
||||
// MouseHandler returns a handler which receives mouse events.
|
||||
// It is called by the Application class.
|
||||
//
|
||||
// A zero value of EventMouse{} may also be returned to stop propagation.
|
||||
//
|
||||
// The Box class provides functionality to intercept mouse events. If you
|
||||
// subclass from Box, it is recommended that you wrap your handler using
|
||||
// Box.WrapMouseHandler() so you inherit that functionality.
|
||||
MouseHandler() func(event EventMouse)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue