Improve performance of spinner in Synchronized events

Instead of redrawing the two views on each tick, only redraw the spinner
This commit is contained in:
Antoine Gaudreau Simard 2026-05-05 18:39:07 -04:00 committed by Stefan Haller
parent 5dcc93e8cc
commit b7edcbad3a
3 changed files with 17 additions and 27 deletions

View file

@ -64,7 +64,7 @@ func TestFlushContentOnly_SkipsUntaintedViews(t *testing.T) {
assert.False(t, main.IsTainted(), "main view should not be tainted (was not modified)")
// flushContentOnly should succeed and clear status tainted flag
assert.NoError(t, g.flushContentOnly())
assert.NoError(t, g.flushContentOnly(g.views))
assert.False(t, status.IsTainted(), "status view should not be tainted after flushContentOnly")
assert.False(t, main.IsTainted(), "main view should not be tainted after flushContentOnly")
@ -75,7 +75,7 @@ func TestFlushContentOnly_WritesCorrectContent(t *testing.T) {
status, _ := setupViews(t, g)
status.SetContent("Fetching |")
assert.NoError(t, g.flushContentOnly())
assert.NoError(t, g.flushContentOnly(g.views))
assert.Equal(t, "Fetching |", status.Buffer())
}

View file

@ -779,7 +779,7 @@ func (g *Gui) processEvent() error {
contentOnly = contentOnly && remainingContentOnly
if contentOnly {
return g.flushContentOnly()
return g.flushContentOnly(g.views)
}
return g.flush()
}
@ -1167,32 +1167,11 @@ func (g *Gui) flush() error {
return nil
}
func (g *Gui) ForceLayoutAndRedraw() error {
return g.flush()
}
// force redrawing one or more views outside of the normal main loop. Useful during longer
// operations that block the main thread, to update a spinner in a status view.
func (g *Gui) ForceRedrawViews(views ...*View) error {
for _, m := range g.managers {
if err := m.Layout(g); err != nil {
return err
}
}
for _, v := range views {
v.draw()
}
Screen.Show()
return nil
}
// Redraws only tainted views and skips the layout pass.
// tcell's cell-level dirty tracking ensures only
// actually-changed cells are emitted to the terminal.
func (g *Gui) flushContentOnly() error {
for _, v := range g.views {
func (g *Gui) flushContentOnly(views []*View) error {
for _, v := range views {
if !v.tainted {
continue
}
@ -1205,6 +1184,17 @@ func (g *Gui) flushContentOnly() error {
return nil
}
func (g *Gui) ForceLayoutAndRedraw() error {
return g.flush()
}
// Redraws only tainted views outside of the normal main
// loop, without a layout pass. Useful during longer operations that block the
// main thread, e.g. to update a spinner in a status view.
func (g *Gui) ForceFlushViewsContentOnly(views []*View) error {
return g.flushContentOnly(views)
}
// draw manages the cursor and calls the draw function of a view.
func (g *Gui) draw(v *View) error {
if g.suspended {

View file

@ -136,7 +136,7 @@ func (self *AppStatusHelper) renderAppStatusSync(stop chan struct{}) {
self.c.Views().AppStatus, self.c.Views().Options, self.c.Views().Information,
self.c.Views().StatusSpacer1, self.c.Views().StatusSpacer2,
}
_ = self.c.GocuiGui().ForceRedrawViews(bottomLineViews...)
_ = self.c.GocuiGui().ForceFlushViewsContentOnly(bottomLineViews)
case <-stop:
break outer
}