Skip to content

Commit

Permalink
Improve rendering performance with many send/receives
Browse files Browse the repository at this point in the history
Only refresh the item in the list corresponding to the specific transfer.

Fixes #99
  • Loading branch information
Jacalz committed Jan 24, 2024
1 parent 58217d4 commit 951e27f
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 11 deletions.
37 changes: 31 additions & 6 deletions internal/transport/bridge/recv.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package bridge

import (
"path/filepath"
"sync/atomic"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
Expand All @@ -22,23 +23,25 @@ type RecvItem struct {
Max int64
Status func() string

list *widget.List
// Allow the list to only refresh a single object.
refresh func(int)
index int
}

func (r *RecvItem) update(delta, total int64) {
r.Value += delta
r.Max = total
r.list.Refresh()
r.refresh(r.index)
}

func (r *RecvItem) done() {
r.Value = r.Max
r.list.Refresh()
r.refresh(r.index)
}

func (r *RecvItem) failed() {
r.Status = func() string { return "Failed" }
r.list.Refresh()
r.refresh(r.index)
}

// RecvData is a list of progress bars that track send progress.
Expand All @@ -47,7 +50,9 @@ type RecvData struct {
Window fyne.Window

items []*RecvItem
list *widget.List

deleting atomic.Bool
list *widget.List
}

// Length returns the length of the data.
Expand Down Expand Up @@ -86,14 +91,26 @@ func (d *RecvData) OnSelected(i int) {

removeLabel := &widget.Label{Text: "This item has completed the transfer and can be removed."}
removeButton := &widget.Button{Icon: theme.DeleteIcon(), Importance: widget.DangerImportance, Text: "Remove", OnTapped: func() {
// Make sure that no updates happen while we modify the slice.
d.deleting.Store(true)

if i < len(d.items)-1 {
copy(d.items[i:], d.items[i+1:])
}

d.items[len(d.items)-1] = nil // Allow the GC to reclaim memory.
d.items = d.items[:len(d.items)-1]

// Update the moved items to have the correct index.
for j := i; j < len(d.items); j++ {
d.items[j].index = j
}

// Refresh the whole list.
d.list.Refresh()

// Allow individual objects to be refreshed again.
d.deleting.Store(false)
}}

removeCard := &widget.Card{Content: container.NewVBox(removeLabel, removeButton)}
Expand All @@ -109,7 +126,7 @@ func (d *RecvData) OnSelected(i int) {

// NewRecv creates a new send item and adds it to the items.
func (d *RecvData) NewRecv(code string) *RecvItem {
item := &RecvItem{Name: "Waiting for filename...", Code: code, Max: 1, list: d.list}
item := &RecvItem{Name: "Waiting for filename...", Code: code, Max: 1, refresh: d.refresh, index: len(d.items)}
d.items = append(d.items, item)
return item
}
Expand Down Expand Up @@ -143,6 +160,14 @@ func (d *RecvData) NewReceive(code string) {
}(code)
}

func (d *RecvData) refresh(index int) {
if d.deleting.Load() {
return // Don't update if we are deleting.
}

d.list.RefreshItem(index)
}

// NewRecvList greates a list of progress bars.
func NewRecvList(data *RecvData) *widget.List {
list := &widget.List{
Expand Down
35 changes: 30 additions & 5 deletions internal/transport/bridge/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package bridge

import (
"path/filepath"
"sync/atomic"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
Expand All @@ -25,18 +26,20 @@ type SendItem struct {
Max int64
Status func() string

list *widget.List
// Allow the list to only refresh a single object.
refresh func(int)
index int
}

func (s *SendItem) update(sent, total int64) {
s.Value = sent
s.Max = total
s.list.Refresh()
s.refresh(s.index)
}

func (s *SendItem) failed() {
s.Status = func() string { return "Failed" }
s.list.Refresh()
s.refresh(s.index)
}

// SendData is a list of progress bars that track send progress.
Expand All @@ -46,7 +49,9 @@ type SendData struct {
Canvas fyne.Canvas

items []*SendItem
list *widget.List

deleting atomic.Bool
list *widget.List
}

// Length returns the length of the data.
Expand Down Expand Up @@ -113,14 +118,26 @@ func (d *SendData) OnSelected(i int) {

removeLabel := &widget.Label{Text: "This item can be removed.\nThe transfer has completed."}
removeButton := &widget.Button{Icon: theme.DeleteIcon(), Importance: widget.DangerImportance, Text: "Remove", OnTapped: func() {
// Make sure that no updates happen while we modify the slice.
d.deleting.Store(true)

if i < len(d.items)-1 {
copy(d.items[i:], d.items[i+1:])
}

d.items[len(d.items)-1] = nil // Allow the GC to reclaim memory.
d.items = d.items[:len(d.items)-1]

// Update the moved items to have the correct index.
for j := i; j < len(d.items); j++ {
d.items[j].index = j
}

// Refresh the whole list.
d.list.Refresh()

// Allow individual objects to be refreshed again.
d.deleting.Store(false)
}}

// Only allow failed or completed items to be removed.
Expand All @@ -143,7 +160,7 @@ func (d *SendData) OnSelected(i int) {

// NewSend adds data about a new send to the list and then returns the item.
func (d *SendData) NewSend(uri fyne.URI) *SendItem {
item := &SendItem{Code: "Waiting for code...", URI: uri, list: d.list, Max: 1}
item := &SendItem{Code: "Waiting for code...", URI: uri, Max: 1, refresh: d.refresh, index: len(d.items)}
d.items = append(d.items, item)
return item
}
Expand Down Expand Up @@ -327,6 +344,14 @@ func (d *SendData) getCustomCode() string {
return <-code
}

func (d *SendData) refresh(index int) {
if d.deleting.Load() {
return // Don't update if we are deleting.
}

d.list.RefreshItem(index)
}

// NewSendList greates a list of progress bars.
func NewSendList(data *SendData) *widget.List {
list := &widget.List{
Expand Down

0 comments on commit 951e27f

Please sign in to comment.