Queue UI output and status updates
This commit is contained in:
committed by
Brandon McGinty
parent
b10e08477c
commit
12761f29ca
@@ -63,9 +63,14 @@ func (b *Barnard) GetInputStatus() string {
|
|||||||
|
|
||||||
func (b *Barnard) UpdateInputStatus(status string) {
|
func (b *Barnard) UpdateInputStatus(status string) {
|
||||||
status = truncateInputStatus(status)
|
status = truncateInputStatus(status)
|
||||||
|
if b.Ui == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.Ui.Post(func() {
|
||||||
b.UiInputStatus.Text = status
|
b.UiInputStatus.Text = status
|
||||||
b.RebuildUserChannelTreePreservingSelection()
|
b.RebuildUserChannelTreePreservingSelection()
|
||||||
b.Ui.Refresh()
|
b.Ui.Refresh()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// truncateInputStatus limits terminal cells without splitting UTF-8 runes.
|
// truncateInputStatus limits terminal cells without splitting UTF-8 runes.
|
||||||
@@ -79,7 +84,11 @@ func truncateInputStatus(status string) string {
|
|||||||
|
|
||||||
func (b *Barnard) AddOutputLine(line string) {
|
func (b *Barnard) AddOutputLine(line string) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
b.UiOutput.AddLine(fmt.Sprintf("%s [%02d:%02d:%02d]", line, now.Hour(), now.Minute(), now.Second()))
|
formatted := fmt.Sprintf("%s [%02d:%02d:%02d]", line, now.Hour(), now.Minute(), now.Second())
|
||||||
|
if b.Ui == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.Ui.Post(func() { b.UiOutput.AddLine(formatted) })
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Barnard) AddOutputMessage(sender *gumble.User, message string) {
|
func (b *Barnard) AddOutputMessage(sender *gumble.User, message string) {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ type Ui struct {
|
|||||||
|
|
||||||
close chan struct{}
|
close chan struct{}
|
||||||
closeOnce sync.Once
|
closeOnce sync.Once
|
||||||
|
events chan func()
|
||||||
manager UiManager
|
manager UiManager
|
||||||
|
|
||||||
drawCount int32
|
drawCount int32
|
||||||
@@ -42,6 +43,7 @@ type uiElement struct {
|
|||||||
func New(manager UiManager) *Ui {
|
func New(manager UiManager) *Ui {
|
||||||
ui := &Ui{
|
ui := &Ui{
|
||||||
close: make(chan struct{}),
|
close: make(chan struct{}),
|
||||||
|
events: make(chan func(), 256),
|
||||||
elements: make(map[string]*uiElement),
|
elements: make(map[string]*uiElement),
|
||||||
manager: manager,
|
manager: manager,
|
||||||
keyListeners: make(map[Key][]KeyListener),
|
keyListeners: make(map[Key][]KeyListener),
|
||||||
@@ -55,6 +57,22 @@ func (ui *Ui) Close() {
|
|||||||
ui.closeOnce.Do(func() { close(ui.close) })
|
ui.closeOnce.Do(func() { close(ui.close) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Post schedules UI work on Run's owning goroutine. It is deliberately
|
||||||
|
// bounded: network callbacks must not block behind slow terminal rendering.
|
||||||
|
func (ui *Ui) Post(fn func()) bool {
|
||||||
|
if fn == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ui.close:
|
||||||
|
return false
|
||||||
|
case ui.events <- fn:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (ui *Ui) Refresh() {
|
func (ui *Ui) Refresh() {
|
||||||
if termbox.IsInit {
|
if termbox.IsInit {
|
||||||
ui.beginDraw()
|
ui.beginDraw()
|
||||||
@@ -137,6 +155,8 @@ func (ui *Ui) Run(cmds chan string) error {
|
|||||||
select {
|
select {
|
||||||
case <-ui.close:
|
case <-ui.close:
|
||||||
return nil
|
return nil
|
||||||
|
case fn := <-ui.events:
|
||||||
|
fn()
|
||||||
case cmd, ok := <-cmds:
|
case cmd, ok := <-cmds:
|
||||||
if !ok {
|
if !ok {
|
||||||
cmds = nil
|
cmds = nil
|
||||||
|
|||||||
@@ -4,6 +4,24 @@ import "testing"
|
|||||||
|
|
||||||
// Regression: Close sent to a bounded channel and could block or enqueue
|
// Regression: Close sent to a bounded channel and could block or enqueue
|
||||||
// duplicate shutdowns when called more than once.
|
// duplicate shutdowns when called more than once.
|
||||||
|
// Regression: network callbacks modified terminal state directly. Post gives
|
||||||
|
// them a bounded handoff to the UI-owning Run goroutine instead of blocking.
|
||||||
|
func TestPostIsBoundedAndRejectsClosedUI(t *testing.T) {
|
||||||
|
ui := New(nil)
|
||||||
|
for i := 0; i < cap(ui.events); i++ {
|
||||||
|
if !ui.Post(func() {}) {
|
||||||
|
t.Fatal("queue filled too early")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ui.Post(func() {}) {
|
||||||
|
t.Fatal("Post accepted work past queue capacity")
|
||||||
|
}
|
||||||
|
ui.Close()
|
||||||
|
if ui.Post(func() {}) {
|
||||||
|
t.Fatal("Post accepted work after close")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCloseIsNonblockingAndIdempotent(t *testing.T) {
|
func TestCloseIsNonblockingAndIdempotent(t *testing.T) {
|
||||||
ui := New(nil)
|
ui := New(nil)
|
||||||
ui.Close()
|
ui.Close()
|
||||||
|
|||||||
Reference in New Issue
Block a user