parent
aae1af2a19
commit
1f765c8695
36 changed files with 858 additions and 543 deletions
321
application.go
321
application.go
|
@ -8,11 +8,52 @@ import (
|
|||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// The size of the event/update/redraw channels.
|
||||
const queueSize = 100
|
||||
const (
|
||||
// The size of the event/update/redraw channels.
|
||||
queueSize = 100
|
||||
|
||||
// The minimum duration between resize event callbacks.
|
||||
const resizeEventThrottle = 200 * time.Millisecond
|
||||
// The minimum time between two consecutive redraws.
|
||||
redrawPause = 50 * time.Millisecond
|
||||
|
||||
// The minimum duration between resize event callbacks.
|
||||
resizeEventThrottle = 200 * time.Millisecond
|
||||
)
|
||||
|
||||
// DoubleClickInterval specifies the maximum time between clicks to register a
|
||||
// double click rather than click.
|
||||
var DoubleClickInterval = 500 * time.Millisecond
|
||||
|
||||
// MouseAction indicates one of the actions the mouse is logically doing.
|
||||
type MouseAction int16
|
||||
|
||||
// Available mouse actions.
|
||||
const (
|
||||
MouseMove MouseAction = iota
|
||||
MouseLeftDown
|
||||
MouseLeftUp
|
||||
MouseLeftClick
|
||||
MouseLeftDoubleClick
|
||||
MouseMiddleDown
|
||||
MouseMiddleUp
|
||||
MouseMiddleClick
|
||||
MouseMiddleDoubleClick
|
||||
MouseRightDown
|
||||
MouseRightUp
|
||||
MouseRightClick
|
||||
MouseRightDoubleClick
|
||||
MouseScrollUp
|
||||
MouseScrollDown
|
||||
MouseScrollLeft
|
||||
MouseScrollRight
|
||||
)
|
||||
|
||||
// queuedUpdate represented the execution of f queued by
|
||||
// Application.QueueUpdate(). The "done" channel receives exactly one element
|
||||
// after f has executed.
|
||||
type queuedUpdate struct {
|
||||
f func()
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// Application represents the top node of an application.
|
||||
//
|
||||
|
@ -84,14 +125,13 @@ type Application struct {
|
|||
// 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
|
||||
mouseCapture func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction)
|
||||
|
||||
// A temporary capture function overriding the above.
|
||||
tempMouseCapture func(event *EventMouse) *EventMouse
|
||||
|
||||
lastMouseX, lastMouseY int
|
||||
lastMouseBtn tcell.ButtonMask
|
||||
lastMouseTarget Primitive // nil if none
|
||||
mouseCapturingPrimitive Primitive // A Primitive returned by a MouseHandler which will capture future mouse events.
|
||||
lastMouseX, lastMouseY int // The last position of the mouse.
|
||||
mouseDownX, mouseDownY int // The position of the mouse when its button was last pressed.
|
||||
lastMouseClick time.Time // The time when a mouse button was last clicked.
|
||||
lastMouseButtons tcell.ButtonMask // The last mouse button state.
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
@ -131,45 +171,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
|
||||
// SetMouseCapture sets a function which captures mouse events (consisting of
|
||||
// the original tcell mouse event and the semantic mouse action) 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.Lock()
|
||||
// the event processing by returning a nil mouse event.
|
||||
func (a *Application) SetMouseCapture(capture func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction)) *Application {
|
||||
a.mouseCapture = capture
|
||||
a.Unlock()
|
||||
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 {
|
||||
a.RLock()
|
||||
defer a.RUnlock()
|
||||
|
||||
func (a *Application) GetMouseCapture() func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction) {
|
||||
return a.mouseCapture
|
||||
}
|
||||
|
||||
// SetTemporaryMouseCapture temporarily overrides the normal capture function.
|
||||
// Calling this function from anywhere other than a widget may result in
|
||||
// unexpected behavior.
|
||||
func (a *Application) SetTemporaryMouseCapture(capture func(event *EventMouse) *EventMouse) *Application {
|
||||
a.Lock()
|
||||
a.tempMouseCapture = capture
|
||||
a.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// GetTemporaryMouseCapture returns the function installed with
|
||||
// SetTemporaryMouseCapture() or nil if no such function has been installed.
|
||||
func (a *Application) GetTemporaryMouseCapture() func(event *EventMouse) *EventMouse {
|
||||
a.RLock()
|
||||
defer a.RUnlock()
|
||||
|
||||
return a.tempMouseCapture
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@ -199,10 +216,17 @@ func (a *Application) SetScreen(screen tcell.Screen) *Application {
|
|||
}
|
||||
|
||||
// EnableMouse enables mouse events.
|
||||
func (a *Application) EnableMouse() *Application {
|
||||
func (a *Application) EnableMouse(enable bool) *Application {
|
||||
a.Lock()
|
||||
a.enableMouse = true
|
||||
a.Unlock()
|
||||
defer a.Unlock()
|
||||
if enable != a.enableMouse && a.screen != nil {
|
||||
if enable {
|
||||
a.screen.EnableMouse()
|
||||
} else {
|
||||
a.screen.DisableMouse()
|
||||
}
|
||||
}
|
||||
a.enableMouse = enable
|
||||
return a
|
||||
}
|
||||
|
||||
|
@ -301,10 +325,7 @@ EventLoop:
|
|||
a.RLock()
|
||||
p := a.focus
|
||||
inputCapture := a.inputCapture
|
||||
mouseCapture := a.mouseCapture
|
||||
tempMouseCapture := a.tempMouseCapture
|
||||
screen := a.screen
|
||||
root := a.root
|
||||
a.RUnlock()
|
||||
|
||||
switch event := event.(type) {
|
||||
|
@ -368,85 +389,14 @@ EventLoop:
|
|||
|
||||
a.draw()
|
||||
case *tcell.EventMouse:
|
||||
atX, atY := event.Position()
|
||||
btn := event.Buttons()
|
||||
|
||||
pstack := a.appendStackAtPoint(nil, atX, atY)
|
||||
var punderMouse Primitive
|
||||
if len(pstack) > 0 {
|
||||
punderMouse = pstack[len(pstack)-1]
|
||||
}
|
||||
var ptarget Primitive
|
||||
if a.lastMouseBtn != 0 {
|
||||
// While a button is down, the same primitive gets events.
|
||||
ptarget = a.lastMouseTarget
|
||||
}
|
||||
if ptarget == nil {
|
||||
ptarget = punderMouse
|
||||
if ptarget == nil {
|
||||
ptarget = root // Fallback to root.
|
||||
}
|
||||
}
|
||||
a.lastMouseTarget = ptarget
|
||||
|
||||
// Calculate mouse actions.
|
||||
var act MouseAction
|
||||
if atX != a.lastMouseX || atY != a.lastMouseY {
|
||||
act |= MouseMove
|
||||
a.lastMouseX = atX
|
||||
a.lastMouseY = atY
|
||||
}
|
||||
btnDiff := btn ^ a.lastMouseBtn
|
||||
if btnDiff != 0 {
|
||||
if btn&btnDiff != 0 {
|
||||
act |= MouseDown
|
||||
}
|
||||
if a.lastMouseBtn&btnDiff != 0 {
|
||||
act |= MouseUp
|
||||
}
|
||||
if a.lastMouseBtn == tcell.Button1 && btn == 0 {
|
||||
if ptarget == punderMouse {
|
||||
// Only if Button1 and mouse up over same p.
|
||||
act |= MouseClick
|
||||
}
|
||||
}
|
||||
a.lastMouseBtn = btn
|
||||
}
|
||||
|
||||
event2 := NewEventMouse(event, ptarget, a, act)
|
||||
|
||||
// Intercept event.
|
||||
if tempMouseCapture != nil {
|
||||
event2 = tempMouseCapture(event2)
|
||||
if event2 == nil {
|
||||
a.draw()
|
||||
continue // Don't forward event.
|
||||
}
|
||||
}
|
||||
if mouseCapture != nil {
|
||||
event2 = mouseCapture(event2)
|
||||
if event2 == nil {
|
||||
a.draw()
|
||||
continue // Don't forward event.
|
||||
}
|
||||
}
|
||||
|
||||
if ptarget == punderMouse {
|
||||
// Observe mouse events inward ("capture")
|
||||
for _, pp := range pstack {
|
||||
// If the primitive has this ObserveMouseEvent func.
|
||||
if pp, ok := pp.(interface {
|
||||
ObserveMouseEvent(*EventMouse)
|
||||
}); ok {
|
||||
pp.ObserveMouseEvent(event2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if handler := ptarget.MouseHandler(); handler != nil {
|
||||
handler(event2)
|
||||
consumed, isMouseDownAction := a.fireMouseActions(event)
|
||||
if consumed {
|
||||
a.draw()
|
||||
}
|
||||
a.lastMouseButtons = event.Buttons()
|
||||
if isMouseDownAction {
|
||||
a.mouseDownX, a.mouseDownY = event.Position()
|
||||
}
|
||||
}
|
||||
|
||||
// If we have updates, now is the time to execute them.
|
||||
|
@ -462,48 +412,103 @@ EventLoop:
|
|||
return nil
|
||||
}
|
||||
|
||||
func findAtPoint(atX, atY int, p Primitive, capture func(p Primitive)) Primitive {
|
||||
x, y, w, h := p.GetRect()
|
||||
if atX < x || atY < y {
|
||||
return nil
|
||||
// fireMouseActions analyzes the provided mouse event, derives mouse actions
|
||||
// from it and then forwards them to the corresponding primitives.
|
||||
func (a *Application) fireMouseActions(event *tcell.EventMouse) (consumed, isMouseDownAction bool) {
|
||||
// We want to relay follow-up events to the same target primitive.
|
||||
var targetPrimitive Primitive
|
||||
|
||||
// Helper function to fire a mouse action.
|
||||
fire := func(action MouseAction) {
|
||||
switch action {
|
||||
case MouseLeftDown, MouseMiddleDown, MouseRightDown:
|
||||
isMouseDownAction = true
|
||||
}
|
||||
|
||||
// Intercept event.
|
||||
if a.mouseCapture != nil {
|
||||
event, action = a.mouseCapture(event, action)
|
||||
if event == nil {
|
||||
consumed = true
|
||||
return // Don't forward event.
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the target primitive.
|
||||
var primitive, capturingPrimitive Primitive
|
||||
if a.mouseCapturingPrimitive != nil {
|
||||
primitive = a.mouseCapturingPrimitive
|
||||
targetPrimitive = a.mouseCapturingPrimitive
|
||||
} else if targetPrimitive != nil {
|
||||
primitive = targetPrimitive
|
||||
} else {
|
||||
primitive = a.root
|
||||
}
|
||||
if primitive != nil {
|
||||
if handler := primitive.MouseHandler(); handler != nil {
|
||||
var wasConsumed bool
|
||||
wasConsumed, capturingPrimitive = handler(action, event, func(p Primitive) {
|
||||
a.SetFocus(p)
|
||||
})
|
||||
if wasConsumed {
|
||||
consumed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
a.mouseCapturingPrimitive = capturingPrimitive
|
||||
}
|
||||
if atX >= x+w || atY >= y+h {
|
||||
return nil
|
||||
|
||||
x, y := event.Position()
|
||||
buttons := event.Buttons()
|
||||
clickMoved := x != a.mouseDownX || y != a.mouseDownY
|
||||
buttonChanges := buttons ^ a.lastMouseButtons
|
||||
|
||||
if x != a.lastMouseX || y != a.lastMouseY {
|
||||
fire(MouseMove)
|
||||
a.lastMouseX = x
|
||||
a.lastMouseY = y
|
||||
}
|
||||
if capture != nil {
|
||||
capture(p)
|
||||
}
|
||||
bestp := p
|
||||
for _, pchild := range p.GetChildren() {
|
||||
x := findAtPoint(atX, atY, pchild, capture)
|
||||
if x != nil {
|
||||
// Always overwrite if we find another one,
|
||||
// this is because if any overlap, the last one is "on top".
|
||||
bestp = x
|
||||
|
||||
for _, buttonEvent := range []struct {
|
||||
button tcell.ButtonMask
|
||||
down, up, click, dclick MouseAction
|
||||
}{
|
||||
{tcell.Button1, MouseLeftDown, MouseLeftUp, MouseLeftClick, MouseLeftDoubleClick},
|
||||
{tcell.Button2, MouseMiddleDown, MouseMiddleUp, MouseMiddleClick, MouseMiddleDoubleClick},
|
||||
{tcell.Button3, MouseRightDown, MouseRightUp, MouseRightClick, MouseRightDoubleClick},
|
||||
} {
|
||||
if buttonChanges&buttonEvent.button != 0 {
|
||||
if buttons&buttonEvent.button != 0 {
|
||||
fire(buttonEvent.down)
|
||||
} else {
|
||||
fire(buttonEvent.up)
|
||||
if !clickMoved {
|
||||
if a.lastMouseClick.Add(DoubleClickInterval).Before(time.Now()) {
|
||||
fire(buttonEvent.click)
|
||||
a.lastMouseClick = time.Now()
|
||||
} else {
|
||||
fire(buttonEvent.dclick)
|
||||
a.lastMouseClick = time.Time{} // reset
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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()
|
||||
for _, wheelEvent := range []struct {
|
||||
button tcell.ButtonMask
|
||||
action MouseAction
|
||||
}{
|
||||
{tcell.WheelUp, MouseScrollUp},
|
||||
{tcell.WheelDown, MouseScrollDown},
|
||||
{tcell.WheelLeft, MouseScrollLeft},
|
||||
{tcell.WheelRight, MouseScrollRight}} {
|
||||
if buttons&wheelEvent.button != 0 {
|
||||
fire(wheelEvent.action)
|
||||
}
|
||||
}
|
||||
|
||||
return findAtPoint(atX, atY, a.root, nil)
|
||||
}
|
||||
|
||||
// The last element appended to buf is the primitive clicked,
|
||||
// the preceeding are its parents.
|
||||
func (a *Application) appendStackAtPoint(buf []Primitive, atX, atY int) []Primitive {
|
||||
a.RLock()
|
||||
defer a.RUnlock()
|
||||
|
||||
findAtPoint(atX, atY, a.root, func(p Primitive) {
|
||||
buf = append(buf, p)
|
||||
})
|
||||
return buf
|
||||
return consumed, isMouseDownAction
|
||||
}
|
||||
|
||||
// Stop stops the application, causing Run() to return.
|
||||
|
|
60
box.go
60
box.go
|
@ -59,9 +59,9 @@ type Box struct {
|
|||
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
|
||||
// event to be forwarded to the primitive's default mouse event handler (at
|
||||
// least one nil if nothing should be forwarded).
|
||||
mouseCapture func(action MouseAction, event *tcell.EventMouse) (MouseAction, *tcell.EventMouse)
|
||||
|
||||
l sync.RWMutex
|
||||
}
|
||||
|
@ -229,50 +229,56 @@ func (b *Box) GetInputCapture() func(event *tcell.EventKey) *tcell.EventKey {
|
|||
}
|
||||
|
||||
// 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.
|
||||
// functionality to capture mouse events (see SetMouseCapture()) before passing
|
||||
// them 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) {
|
||||
func (b *Box) WrapMouseHandler(mouseHandler func(MouseAction, *tcell.EventMouse, func(p Primitive)) (bool, Primitive)) func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
return func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
if b.mouseCapture != nil {
|
||||
event = b.mouseCapture(event)
|
||||
action, event = b.mouseCapture(action, event)
|
||||
}
|
||||
if event != nil && mouseHandler != nil {
|
||||
mouseHandler(event)
|
||||
consumed, capture = mouseHandler(action, event, setFocus)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// MouseHandler returns nil.
|
||||
func (b *Box) MouseHandler() func(event *EventMouse) {
|
||||
b.l.RLock()
|
||||
defer b.l.RUnlock()
|
||||
|
||||
return b.WrapMouseHandler(nil)
|
||||
func (b *Box) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
return b.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
if action == MouseLeftClick && b.InRect(event.Position()) {
|
||||
setFocus(b)
|
||||
consumed = true
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// 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.
|
||||
// SetMouseCapture sets a function which captures mouse events (consisting of
|
||||
// the original tcell mouse event and the semantic mouse action) before they are
|
||||
// forwarded to the primitive's default mouse event handler. This function can
|
||||
// then choose to forward that event (or a different one) by returning it or
|
||||
// returning a nil mouse event, in which case 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.l.Lock()
|
||||
defer b.l.Unlock()
|
||||
|
||||
func (b *Box) SetMouseCapture(capture func(action MouseAction, event *tcell.EventMouse) (MouseAction, *tcell.EventMouse)) *Box {
|
||||
b.mouseCapture = capture
|
||||
return b
|
||||
}
|
||||
|
||||
// InRect returns true if the given coordinate is within the bounds of the box's
|
||||
// rectangle.
|
||||
func (b *Box) InRect(x, y int) bool {
|
||||
rectX, rectY, width, height := b.GetRect()
|
||||
return x >= rectX && x < rectX+width && y >= rectY && y < rectY+height
|
||||
}
|
||||
|
||||
// GetMouseCapture returns the function installed with SetMouseCapture() or nil
|
||||
// if no such function has been installed.
|
||||
func (b *Box) GetMouseCapture() func(*EventMouse) *EventMouse {
|
||||
b.l.RLock()
|
||||
defer b.l.RUnlock()
|
||||
|
||||
func (b *Box) GetMouseCapture() func(action MouseAction, event *tcell.EventMouse) (MouseAction, *tcell.EventMouse) {
|
||||
return b.mouseCapture
|
||||
}
|
||||
|
||||
|
|
14
button.go
14
button.go
|
@ -167,13 +167,21 @@ func (b *Button) InputHandler() func(event *tcell.EventKey, setFocus func(p Prim
|
|||
}
|
||||
|
||||
// MouseHandler returns the mouse handler for this primitive.
|
||||
func (b *Button) MouseHandler() func(event *EventMouse) {
|
||||
return b.WrapMouseHandler(func(event *EventMouse) {
|
||||
func (b *Button) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
return b.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
if !b.InRect(event.Position()) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Process mouse event.
|
||||
if event.Action()&MouseClick != 0 {
|
||||
if action == MouseLeftClick {
|
||||
setFocus(b)
|
||||
if b.selected != nil {
|
||||
b.selected()
|
||||
}
|
||||
consumed = true
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
}
|
||||
|
|
18
checkbox.go
18
checkbox.go
|
@ -279,16 +279,24 @@ func (c *Checkbox) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
|
|||
}
|
||||
|
||||
// MouseHandler returns the mouse handler for this primitive.
|
||||
func (c *Checkbox) MouseHandler() func(event *EventMouse) {
|
||||
return c.WrapMouseHandler(func(event *EventMouse) {
|
||||
func (c *Checkbox) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
return c.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
x, y := event.Position()
|
||||
_, rectY, _, _ := c.GetInnerRect()
|
||||
if !c.InRect(x, y) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Process mouse event.
|
||||
if event.Action()&MouseClick != 0 {
|
||||
c.Lock()
|
||||
if action == MouseLeftClick && y == rectY {
|
||||
setFocus(c)
|
||||
c.checked = !c.checked
|
||||
c.Unlock()
|
||||
if c.changed != nil {
|
||||
c.changed(c.checked)
|
||||
}
|
||||
consumed = true
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ func main() {
|
|||
app.Stop()
|
||||
})
|
||||
button.SetBorder(true).SetRect(0, 0, 22, 3)
|
||||
if err := app.SetRoot(button, false).Run(); err != nil {
|
||||
if err := app.SetRoot(button, false).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
// Demo code for the Checkbox primitive.
|
||||
package main
|
||||
|
||||
import "gitlab.com/tslocum/cview"
|
||||
import (
|
||||
"gitlab.com/tslocum/cview"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cview.NewApplication()
|
||||
checkbox := cview.NewCheckbox().SetLabel("Hit Enter to check box: ")
|
||||
if err := app.SetRoot(checkbox, true).Run(); err != nil {
|
||||
if err := app.SetRoot(checkbox, true).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ func main() {
|
|||
dropdown := cview.NewDropDown().
|
||||
SetLabel("Select an option (hit Enter): ").
|
||||
SetOptions([]string{"First", "Second", "Third", "Fourth", "Fifth"}, nil)
|
||||
if err := app.SetRoot(dropdown, true).Run(); err != nil {
|
||||
if err := app.SetRoot(dropdown, true).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ func main() {
|
|||
AddItem(cview.NewBox().SetBorder(true).SetTitle("Middle (3 x height of Top)"), 0, 3, false).
|
||||
AddItem(cview.NewBox().SetBorder(true).SetTitle("Bottom (5 rows)"), 5, 1, false), 0, 2, false).
|
||||
AddItem(cview.NewBox().SetBorder(true).SetTitle("Right (20 cols)"), 20, 1, false)
|
||||
if err := app.SetRoot(flex, true).Run(); err != nil {
|
||||
if err := app.SetRoot(flex, true).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ func main() {
|
|||
app.Stop()
|
||||
})
|
||||
form.SetBorder(true).SetTitle("Enter some data").SetTitleAlign(cview.AlignLeft)
|
||||
if err := app.SetRoot(form, true).Run(); err != nil {
|
||||
if err := app.SetRoot(form, true).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ func main() {
|
|||
AddText("Header second middle", true, cview.AlignCenter, tcell.ColorRed).
|
||||
AddText("Footer middle", false, cview.AlignCenter, tcell.ColorGreen).
|
||||
AddText("Footer second middle", false, cview.AlignCenter, tcell.ColorGreen)
|
||||
if err := app.SetRoot(frame, true).Run(); err != nil {
|
||||
if err := app.SetRoot(frame, true).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ func main() {
|
|||
AddItem(main, 1, 1, 1, 1, 0, 100, false).
|
||||
AddItem(sideBar, 1, 2, 1, 1, 0, 100, false)
|
||||
|
||||
if err := cview.NewApplication().SetRoot(grid, true).Run(); err != nil {
|
||||
if err := cview.NewApplication().SetRoot(grid, true).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ func main() {
|
|||
SetDoneFunc(func(key tcell.Key) {
|
||||
app.Stop()
|
||||
})
|
||||
if err := app.SetRoot(inputField, true).Run(); err != nil {
|
||||
if err := app.SetRoot(inputField, true).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,7 @@ func main() {
|
|||
AddItem("Quit", "Press to exit", 'q', func() {
|
||||
app.Stop()
|
||||
})
|
||||
app.EnableMouse()
|
||||
if err := app.SetRoot(list, true).Run(); err != nil {
|
||||
if err := app.SetRoot(list, true).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ func main() {
|
|||
app.Stop()
|
||||
}
|
||||
})
|
||||
if err := app.SetRoot(modal, false).Run(); err != nil {
|
||||
if err := app.SetRoot(modal, false).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ func main() {
|
|||
page == 0)
|
||||
}(page)
|
||||
}
|
||||
if err := app.SetRoot(pages, true).Run(); err != nil {
|
||||
if err := app.SetRoot(pages, true).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ const logo = `
|
|||
const (
|
||||
subtitle = `Terminal-based user interface toolkit`
|
||||
navigation = `Ctrl-N: Next slide Ctrl-P: Previous slide Ctrl-C: Exit`
|
||||
mouse = `(or use your mouse)`
|
||||
)
|
||||
|
||||
// Cover returns the cover page.
|
||||
|
@ -44,7 +45,8 @@ func Cover(nextSlide func()) (title string, content cview.Primitive) {
|
|||
SetBorders(0, 0, 0, 0, 0, 0).
|
||||
AddText(subtitle, true, cview.AlignCenter, tcell.ColorWhite).
|
||||
AddText("", true, cview.AlignCenter, tcell.ColorWhite).
|
||||
AddText(navigation, true, cview.AlignCenter, tcell.ColorDarkMagenta)
|
||||
AddText(navigation, true, cview.AlignCenter, tcell.ColorDarkMagenta).
|
||||
AddText(mouse, true, cview.AlignCenter, tcell.ColorDarkMagenta)
|
||||
|
||||
// Create a Flex layout that centers the logo and subtitle.
|
||||
flex := cview.NewFlex().
|
||||
|
|
|
@ -62,27 +62,29 @@ func main() {
|
|||
End,
|
||||
}
|
||||
|
||||
pages := cview.NewPages()
|
||||
|
||||
// The bottom row has some info on where we are.
|
||||
info := cview.NewTextView().
|
||||
SetDynamicColors(true).
|
||||
SetRegions(true).
|
||||
SetWrap(false)
|
||||
SetWrap(false).
|
||||
SetHighlightedFunc(func(added, removed, remaining []string) {
|
||||
pages.SwitchToPage(added[0])
|
||||
})
|
||||
|
||||
// Create the pages for all slides.
|
||||
currentSlide := 0
|
||||
info.Highlight(strconv.Itoa(currentSlide))
|
||||
pages := cview.NewPages()
|
||||
previousSlide := func() {
|
||||
currentSlide = (currentSlide - 1 + len(slides)) % len(slides)
|
||||
info.Highlight(strconv.Itoa(currentSlide)).
|
||||
slide, _ := strconv.Atoi(info.GetHighlights()[0])
|
||||
slide = (slide - 1 + len(slides)) % len(slides)
|
||||
info.Highlight(strconv.Itoa(slide)).
|
||||
ScrollToHighlight()
|
||||
pages.SwitchToPage(strconv.Itoa(currentSlide))
|
||||
}
|
||||
nextSlide := func() {
|
||||
currentSlide = (currentSlide + 1) % len(slides)
|
||||
info.Highlight(strconv.Itoa(currentSlide)).
|
||||
slide, _ := strconv.Atoi(info.GetHighlights()[0])
|
||||
slide = (slide + 1) % len(slides)
|
||||
info.Highlight(strconv.Itoa(slide)).
|
||||
ScrollToHighlight()
|
||||
pages.SwitchToPage(strconv.Itoa(currentSlide))
|
||||
}
|
||||
|
||||
cursor := 0
|
||||
|
@ -91,11 +93,12 @@ func main() {
|
|||
slideRegions = append(slideRegions, cursor)
|
||||
|
||||
title, primitive := slide(nextSlide)
|
||||
pages.AddPage(strconv.Itoa(index), primitive, true, index == currentSlide)
|
||||
pages.AddPage(strconv.Itoa(index), primitive, true, index == 0)
|
||||
fmt.Fprintf(info, `%d ["%d"][darkcyan]%s[white][""] `, index+1, index, title)
|
||||
|
||||
cursor += len(title) + 4
|
||||
}
|
||||
info.Highlight("0")
|
||||
|
||||
// Create the main layout.
|
||||
layout := cview.NewFlex().
|
||||
|
@ -113,37 +116,8 @@ func main() {
|
|||
return event
|
||||
})
|
||||
|
||||
app.EnableMouse()
|
||||
|
||||
var screenHeight int
|
||||
|
||||
app.SetAfterResizeFunc(func(_ int, height int) {
|
||||
screenHeight = height
|
||||
})
|
||||
|
||||
app.SetMouseCapture(func(event *cview.EventMouse) *cview.EventMouse {
|
||||
atX, atY := event.Position()
|
||||
if event.Action()&cview.MouseDown != 0 && atY == screenHeight-1 {
|
||||
slideClicked := -1
|
||||
for i, region := range slideRegions {
|
||||
if atX >= region {
|
||||
slideClicked = i
|
||||
}
|
||||
}
|
||||
if slideClicked >= 0 {
|
||||
currentSlide = slideClicked
|
||||
info.Highlight(strconv.Itoa(currentSlide)).
|
||||
ScrollToHighlight()
|
||||
pages.SwitchToPage(strconv.Itoa(currentSlide))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return event
|
||||
})
|
||||
|
||||
// Start the application.
|
||||
if err := app.SetRoot(layout, true).Run(); err != nil {
|
||||
if err := app.SetRoot(layout, true).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ func main() {
|
|||
table.GetCell(row, column).SetTextColor(tcell.ColorRed)
|
||||
table.SetSelectable(false, false)
|
||||
})
|
||||
if err := app.SetRoot(table, true).Run(); err != nil {
|
||||
if err := app.SetRoot(table, true).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ func main() {
|
|||
}
|
||||
})
|
||||
textView.SetBorder(true)
|
||||
if err := app.SetRoot(textView, true).Run(); err != nil {
|
||||
if err := app.SetRoot(textView, true).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ func main() {
|
|||
}
|
||||
})
|
||||
|
||||
if err := cview.NewApplication().SetRoot(tree, true).Run(); err != nil {
|
||||
if err := cview.NewApplication().SetRoot(tree, true).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
4
doc.go
4
doc.go
|
@ -59,10 +59,6 @@ Application.EnableMouse documentation.
|
|||
|
||||
Mouse events are passed to:
|
||||
|
||||
- The handler set with SetTemporaryMouseCapture, which is reserved for use by
|
||||
widgets to temporarily intercept mouse events, such as to close a Dropdown when
|
||||
the user clicks outside of the list.
|
||||
|
||||
- The handler set with SetMouseCapture, which is reserved for use by application
|
||||
developers to permanently intercept mouse events.
|
||||
|
||||
|
|
20
doc_test.go
20
doc_test.go
|
@ -61,27 +61,33 @@ func ExampleNewApplication() {
|
|||
// Example of an application with mouse support.
|
||||
func ExampleApplication_EnableMouse() {
|
||||
// Initialize application and enable mouse support.
|
||||
app := NewApplication().EnableMouse()
|
||||
app := NewApplication()
|
||||
|
||||
// Create a textview.
|
||||
tv := NewTextView().SetText("Click somewhere!")
|
||||
|
||||
// Set a mouse capture function which prints where the mouse was clicked.
|
||||
app.SetMouseCapture(func(event *EventMouse) *EventMouse {
|
||||
if event.Action()&MouseDown != 0 && event.Buttons()&tcell.Button1 != 0 {
|
||||
app.SetMouseCapture(func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction) {
|
||||
if action == MouseLeftClick || action == MouseLeftDoubleClick {
|
||||
actionLabel := "click"
|
||||
if action == MouseLeftDoubleClick {
|
||||
actionLabel = "double-click"
|
||||
}
|
||||
|
||||
x, y := event.Position()
|
||||
fmt.Fprintf(tv, "\nYou clicked at %d,%d! Amazing!", x, y)
|
||||
|
||||
fmt.Fprintf(tv, "\nYou %sed at %d,%d! Amazing!", actionLabel, x, y)
|
||||
|
||||
// Return nil to stop propagating the event to any remaining handlers.
|
||||
return nil
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
// Return the event to continue propagating it.
|
||||
return event
|
||||
return event, action
|
||||
})
|
||||
|
||||
// Run the application.
|
||||
if err := app.SetRoot(tv, true).Run(); err != nil {
|
||||
if err := app.EnableMouse(true).SetRoot(tv, true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
113
dropdown.go
113
dropdown.go
|
@ -81,6 +81,9 @@ type DropDown struct {
|
|||
// selection.
|
||||
selected func(text string, index int)
|
||||
|
||||
// Set to true when mouse dragging is in progress.
|
||||
dragging bool
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
|
@ -482,7 +485,7 @@ func (d *DropDown) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
|
|||
d.evalPrefix()
|
||||
}
|
||||
|
||||
d.openList(setFocus, nil)
|
||||
d.openList(setFocus)
|
||||
case tcell.KeyEscape, tcell.KeyTab, tcell.KeyBacktab:
|
||||
if d.done != nil {
|
||||
d.done(key)
|
||||
|
@ -494,8 +497,7 @@ func (d *DropDown) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
|
|||
})
|
||||
}
|
||||
|
||||
// A helper function which selects an item in the drop-down list based on
|
||||
// the current prefix.
|
||||
// evalPrefix selects an item in the drop-down list based on the current prefix.
|
||||
func (d *DropDown) evalPrefix() {
|
||||
if len(d.prefix) > 0 {
|
||||
for index, option := range d.options {
|
||||
|
@ -504,31 +506,33 @@ func (d *DropDown) evalPrefix() {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Prefix does not match any item. Remove last rune.
|
||||
r := []rune(d.prefix)
|
||||
d.prefix = string(r[:len(r)-1])
|
||||
}
|
||||
}
|
||||
|
||||
// Hand control over to the list.
|
||||
func (d *DropDown) openList(setFocus func(Primitive), app *Application) {
|
||||
// openList hands control over to the embedded List primitive.
|
||||
func (d *DropDown) openList(setFocus func(Primitive)) {
|
||||
d.open = true
|
||||
optionBefore := d.currentOption
|
||||
|
||||
d.list.SetSelectedFunc(func(index int, mainText, secondaryText string, shortcut rune) {
|
||||
if d.dragging {
|
||||
return // If we're dragging the mouse, we don't want to trigger any events.
|
||||
}
|
||||
|
||||
// An option was selected. Close the list again.
|
||||
d.currentOption = index
|
||||
d.closeList(setFocus, app)
|
||||
d.closeList(setFocus)
|
||||
|
||||
// Trigger "selected" event.
|
||||
if d.selected != nil {
|
||||
d.Unlock()
|
||||
d.selected(d.options[d.currentOption].Text, d.currentOption)
|
||||
d.Lock()
|
||||
}
|
||||
if d.options[d.currentOption].Selected != nil {
|
||||
d.Unlock()
|
||||
d.options[d.currentOption].Selected()
|
||||
d.Lock()
|
||||
}
|
||||
}).SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if event.Key() == tcell.KeyRune {
|
||||
|
@ -542,50 +546,20 @@ func (d *DropDown) openList(setFocus func(Primitive), app *Application) {
|
|||
d.evalPrefix()
|
||||
} else if event.Key() == tcell.KeyEscape {
|
||||
d.currentOption = optionBefore
|
||||
d.closeList(setFocus, app)
|
||||
d.closeList(setFocus)
|
||||
} else {
|
||||
d.prefix = ""
|
||||
}
|
||||
|
||||
return event
|
||||
})
|
||||
if app != nil {
|
||||
app.SetTemporaryMouseCapture(func(event *EventMouse) *EventMouse {
|
||||
if d.open {
|
||||
// Forward the mouse event to the list.
|
||||
atX, atY := event.Position()
|
||||
x, y, w, h := d.list.GetInnerRect()
|
||||
if atX >= x && atY >= y && atX < x+w && atY < y+h {
|
||||
// Mouse is within the list.
|
||||
if handler := d.list.MouseHandler(); handler != nil {
|
||||
if event.Action()&MouseUp != 0 {
|
||||
// Treat mouse up as click here.
|
||||
// This allows you to expand and select in one go.
|
||||
event = NewEventMouse(event.EventMouse,
|
||||
event.Target(), event.Application(),
|
||||
event.Action()|MouseClick)
|
||||
}
|
||||
handler(event)
|
||||
return nil // handled
|
||||
}
|
||||
} else {
|
||||
// Mouse not within the list.
|
||||
if event.Action()&MouseDown != 0 {
|
||||
// If a mouse button was pressed, cancel this capture.
|
||||
d.closeList(event.SetFocus, app)
|
||||
}
|
||||
}
|
||||
}
|
||||
return event
|
||||
})
|
||||
}
|
||||
|
||||
setFocus(d.list)
|
||||
}
|
||||
|
||||
func (d *DropDown) closeList(setFocus func(Primitive), app *Application) {
|
||||
if app != nil {
|
||||
app.SetTemporaryMouseCapture(nil)
|
||||
}
|
||||
|
||||
// closeList closes the embedded List element by hiding it and removing focus
|
||||
// from it.
|
||||
func (d *DropDown) closeList(setFocus func(Primitive)) {
|
||||
d.open = false
|
||||
if d.list.HasFocus() {
|
||||
setFocus(d)
|
||||
|
@ -612,20 +586,43 @@ func (d *DropDown) HasFocus() bool {
|
|||
}
|
||||
|
||||
// MouseHandler returns the mouse handler for this primitive.
|
||||
func (d *DropDown) MouseHandler() func(event *EventMouse) {
|
||||
return d.WrapMouseHandler(func(event *EventMouse) {
|
||||
// Process mouse event.
|
||||
if event.Action()&MouseDown != 0 && event.Buttons()&tcell.Button1 != 0 {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
func (d *DropDown) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
return d.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
// Was the mouse event in the drop-down box itself (or on its label)?
|
||||
x, y := event.Position()
|
||||
_, rectY, _, _ := d.GetInnerRect()
|
||||
inRect := y == rectY
|
||||
if !d.open && !inRect {
|
||||
return d.InRect(x, y), nil // No, and it's not expanded either. Ignore.
|
||||
}
|
||||
|
||||
//d.open = !d.open
|
||||
//event.SetFocus(d)
|
||||
if d.open {
|
||||
d.closeList(event.SetFocus, event.Application())
|
||||
} else {
|
||||
d.openList(event.SetFocus, event.Application())
|
||||
// Handle dragging. Clicks are implicitly handled by this logic.
|
||||
switch action {
|
||||
case MouseLeftDown:
|
||||
consumed = d.open || inRect
|
||||
capture = d
|
||||
if !d.open {
|
||||
d.openList(setFocus)
|
||||
d.dragging = true
|
||||
} else if consumed, _ := d.list.MouseHandler()(MouseLeftClick, event, setFocus); !consumed {
|
||||
d.closeList(setFocus) // Close drop-down if clicked outside of it.
|
||||
}
|
||||
case MouseMove:
|
||||
if d.dragging {
|
||||
// We pretend it's a left click so we can see the selection during
|
||||
// dragging. Because we don't act upon it, it's not a problem.
|
||||
d.list.MouseHandler()(MouseLeftClick, event, setFocus)
|
||||
consumed = true
|
||||
capture = d
|
||||
}
|
||||
case MouseLeftUp:
|
||||
if d.dragging {
|
||||
d.dragging = false
|
||||
d.list.MouseHandler()(MouseLeftClick, event, setFocus)
|
||||
consumed = true
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
}
|
||||
|
|
47
events.go
47
events.go
|
@ -1,47 +0,0 @@
|
|||
package cview
|
||||
|
||||
import "github.com/gdamore/tcell"
|
||||
|
||||
// MouseAction are bit flags indicating what the mouse is logically doing.
|
||||
type MouseAction int
|
||||
|
||||
// All MouseActions
|
||||
const (
|
||||
MouseDown MouseAction = 1 << iota
|
||||
MouseUp
|
||||
MouseClick // Button1 only.
|
||||
MouseMove // The mouse position changed.
|
||||
)
|
||||
|
||||
// EventMouse is the mouse event info.
|
||||
type EventMouse struct {
|
||||
*tcell.EventMouse
|
||||
target Primitive
|
||||
app *Application
|
||||
action MouseAction
|
||||
}
|
||||
|
||||
// Target gets the target Primitive of the mouse event.
|
||||
func (e *EventMouse) Target() Primitive {
|
||||
return e.target
|
||||
}
|
||||
|
||||
// Application gets the event originating *Application.
|
||||
func (e *EventMouse) Application() *Application {
|
||||
return e.app
|
||||
}
|
||||
|
||||
// Action gets the mouse action of this event.
|
||||
func (e *EventMouse) Action() MouseAction {
|
||||
return e.action
|
||||
}
|
||||
|
||||
// SetFocus will set focus to the primitive.
|
||||
func (e *EventMouse) SetFocus(p Primitive) {
|
||||
e.app.SetFocus(p)
|
||||
}
|
||||
|
||||
// NewEventMouse creates a new mouse event.
|
||||
func NewEventMouse(base *tcell.EventMouse, target Primitive, app *Application, action MouseAction) *EventMouse {
|
||||
return &EventMouse{base, target, app, action}
|
||||
}
|
25
flex.go
25
flex.go
|
@ -226,14 +226,21 @@ func (f *Flex) HasFocus() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// GetChildren returns all primitives that have been added.
|
||||
func (f *Flex) GetChildren() []Primitive {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
// MouseHandler returns the mouse handler for this primitive.
|
||||
func (f *Flex) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
return f.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
if !f.InRect(event.Position()) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
children := make([]Primitive, len(f.items))
|
||||
for i, item := range f.items {
|
||||
children[i] = item.Item
|
||||
}
|
||||
return children
|
||||
// Pass mouse events along to the first child item that takes it.
|
||||
for _, item := range f.items {
|
||||
consumed, capture = item.Item.MouseHandler()(action, event, setFocus)
|
||||
if consumed {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
}
|
||||
|
|
48
form.go
48
form.go
|
@ -754,20 +754,38 @@ func (f *Form) focusIndex() int {
|
|||
return -1
|
||||
}
|
||||
|
||||
// GetChildren returns all primitives that have been added.
|
||||
func (f *Form) GetChildren() []Primitive {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
// MouseHandler returns the mouse handler for this primitive.
|
||||
func (f *Form) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
return f.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
if !f.InRect(event.Position()) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
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
|
||||
// Determine items to pass mouse events to.
|
||||
for _, item := range f.items {
|
||||
consumed, capture = item.MouseHandler()(action, event, setFocus)
|
||||
if consumed {
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, button := range f.buttons {
|
||||
consumed, capture = button.MouseHandler()(action, event, setFocus)
|
||||
if consumed {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// A mouse click anywhere else will return the focus to the last selected
|
||||
// element.
|
||||
if action == MouseLeftClick {
|
||||
if f.focusedElement < len(f.items) {
|
||||
setFocus(f.items[f.focusedElement])
|
||||
} else if f.focusedElement < len(f.items)+len(f.buttons) {
|
||||
setFocus(f.buttons[f.focusedElement-len(f.items)])
|
||||
}
|
||||
consumed = true
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
}
|
||||
|
|
18
frame.go
18
frame.go
|
@ -14,8 +14,8 @@ type frameText struct {
|
|||
Color tcell.Color // The text color.
|
||||
}
|
||||
|
||||
// Frame is a wrapper which adds a border around another primitive. The top area
|
||||
// (header) and the bottom area (footer) may also contain text.
|
||||
// Frame is a wrapper which adds space around another primitive. In addition,
|
||||
// the top area (header) and the bottom area (footer) may also contain text.
|
||||
//
|
||||
// See https://gitlab.com/tslocum/cview/wiki/Frame for an example.
|
||||
type Frame struct {
|
||||
|
@ -179,10 +179,14 @@ func (f *Frame) HasFocus() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// GetChildren returns all primitives that have been added.
|
||||
func (f *Frame) GetChildren() []Primitive {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
// MouseHandler returns the mouse handler for this primitive.
|
||||
func (f *Frame) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
return f.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
if !f.InRect(event.Position()) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return []Primitive{f.primitive}
|
||||
// Pass mouse events on to contained primitive.
|
||||
return f.primitive.MouseHandler()(action, event, setFocus)
|
||||
})
|
||||
}
|
||||
|
|
25
grid.go
25
grid.go
|
@ -720,14 +720,21 @@ func (g *Grid) Draw(screen tcell.Screen) {
|
|||
}
|
||||
}
|
||||
|
||||
// GetChildren returns all primitives that have been added.
|
||||
func (g *Grid) GetChildren() []Primitive {
|
||||
g.Lock()
|
||||
defer g.Unlock()
|
||||
// MouseHandler returns the mouse handler for this primitive.
|
||||
func (g *Grid) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
return g.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
||||
if !g.InRect(event.Position()) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
children := make([]Primitive, len(g.items))
|
||||
for i, item := range g.items {
|
||||
children[i] = item.Item
|
||||
}
|
||||
return children
|
||||
// Pass mouse events along to the first child item that takes it.
|
||||
for _, item := range g.items {
|
||||
consumed, capture = item.Item.MouseHandler()(action, event, setFocus)
|
||||
if consumed {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
}
|
||||
|
|
|
@ -69,9 +69,6 @@ type InputField struct {
|
|||
// The cursor position as a byte index into the text string.
|
||||
cursorPos int
|
||||
|
||||
// The number of bytes of the text string skipped ahead while drawing.
|
||||
offset int
|
||||
|
||||
// An optional autocomplete function which receives the current text of the
|
||||
// input field and returns a slice of strings to be displayed in a drop-down
|
||||
// selection.
|
||||
|
@ -96,6 +93,12 @@ type InputField struct {
|
|||
// this form item.
|
||||
finished func(tcell.Key)
|
||||
|
||||
// The x-coordinate of the input field as determined during the last call to Draw().
|
||||
fieldX int
|
||||
|
||||
// The number of bytes of the text string skipped ahead while drawing.
|
||||
offset int
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
|
@ -396,6 +399,7 @@ func (i *InputField) Draw(screen tcell.Screen) {
|
|||
}
|
||||
|
||||
// Draw input area.
|
||||
i.fieldX = x
|
||||
fieldWidth := i.fieldWidth
|
||||
if fieldWidth == 0 {
|
||||
fieldWidth = math.MaxInt32
|
||||
|
@ -681,11 +685,32 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p
|
|||
}
|
||||
|
||||
// MouseHandler returns the mouse handler for this primitive.
|
||||
func (i *InputField) MouseHandler() func(event *EventMouse) {
|
||||
return i.WrapMouseHandler(func(event *EventMouse) {
|
||||
// Process mouse event.
|
||||
if event |