Handle events before executing queued updates

This commit is contained in:
Trevor Slocum 2020-11-07 09:01:41 -08:00
parent fa31102abf
commit 79a35fe4de
3 changed files with 100 additions and 86 deletions

View File

@ -1,3 +1,6 @@
v1.5.2 (WIP)
- Handle events before executing queued updates
v1.5.1 (2020-11-05)
- Add FocusManager
- Add Slider

View File

@ -28,11 +28,10 @@ maintainers and allowing code changes which may be outside of tview's scope.
## Primitive methods do not return the primitive they belong to
When chaining multiple method calls on a primitive together, application
developers might accidentally end the chain with a different return type than
the first method call. This could result in unexpected return types. For
example, ending a chain with `SetTitle` would result in a `Box` rather than the
original primitive.
When chaining multiple primitive method calls together, application developers
might accidentally end the chain with a different return type than the first
method call. This could result in unexpected return types. For example, ending
a chain with `SetTitle` would result in a `Box` rather than the original primitive.
## cview is [thread-safe](https://docs.rocketnine.space/gitlab.com/tslocum/cview/#hdr-Concurrency)

View File

@ -354,97 +354,109 @@ func (a *Application) Run() error {
}
}()
handle := func(event interface{}) {
a.RLock()
p := a.focus
inputCapture := a.inputCapture
screen := a.screen
a.RUnlock()
switch event := event.(type) {
case *tcell.EventKey:
// Intercept keys.
if inputCapture != nil {
event = inputCapture(event)
if event == nil {
a.draw()
return // Don't forward event.
}
}
// Ctrl-C closes the application.
if event.Key() == tcell.KeyCtrlC {
a.Stop()
return
}
// Pass other key events to the currently focused primitive.
if p != nil {
if handler := p.InputHandler(); handler != nil {
handler(event, func(p Primitive) {
a.SetFocus(p)
})
a.draw()
}
}
case *tcell.EventResize:
// Throttle resize events.
if time.Since(a.lastResize) < resizeEventThrottle {
// Stop timer
if a.throttleResize != nil && !a.throttleResize.Stop() {
select {
case <-a.throttleResize.C:
default:
}
}
event := event // Capture
// Start timer
a.throttleResize = time.AfterFunc(resizeEventThrottle, func() {
a.events <- event
})
return
}
a.lastResize = time.Now()
if screen == nil {
return
}
screen.Clear()
a.width, a.height = event.Size()
// Call afterResize handler if there is one.
if a.afterResize != nil {
a.afterResize(a.width, a.height)
}
a.draw()
case *tcell.EventMouse:
consumed, isMouseDownAction := a.fireMouseActions(event)
if consumed {
a.draw()
}
a.lastMouseButtons = event.Buttons()
if isMouseDownAction {
a.mouseDownX, a.mouseDownY = event.Position()
}
}
}
// Start event loop.
EventLoop:
for {
// Handle events before executing updates
select {
case event := <-a.events:
if event == nil {
break EventLoop
}
handle(event)
continue
default:
}
a.RLock()
p := a.focus
inputCapture := a.inputCapture
screen := a.screen
a.RUnlock()
switch event := event.(type) {
case *tcell.EventKey:
// Intercept keys.
if inputCapture != nil {
event = inputCapture(event)
if event == nil {
a.draw()
continue // Don't forward event.
}
}
// Ctrl-C closes the application.
if event.Key() == tcell.KeyCtrlC {
a.Stop()
continue
}
// Pass other key events to the currently focused primitive.
if p != nil {
if handler := p.InputHandler(); handler != nil {
handler(event, func(p Primitive) {
a.SetFocus(p)
})
a.draw()
}
}
case *tcell.EventResize:
// Throttle resize events.
if time.Since(a.lastResize) < resizeEventThrottle {
// Stop timer
if a.throttleResize != nil && !a.throttleResize.Stop() {
select {
case <-a.throttleResize.C:
default:
}
}
event := event // Capture
// Start timer
a.throttleResize = time.AfterFunc(resizeEventThrottle, func() {
a.events <- event
})
continue
}
a.lastResize = time.Now()
if screen == nil {
continue
}
screen.Clear()
a.width, a.height = event.Size()
// Call afterResize handler if there is one.
if a.afterResize != nil {
a.afterResize(a.width, a.height)
}
a.draw()
case *tcell.EventMouse:
consumed, isMouseDownAction := a.fireMouseActions(event)
if consumed {
a.draw()
}
a.lastMouseButtons = event.Buttons()
if isMouseDownAction {
a.mouseDownX, a.mouseDownY = event.Position()
}
select {
case event := <-a.events:
if event == nil {
break EventLoop
}
// If we have updates, now is the time to execute them.
case updater := <-a.updates:
updater()
handle(event)
case update := <-a.updates:
update()
}
}