Skip to content

Commit

Permalink
update EnsureMinSize to walk complete trees to ensure CanvasForObject…
Browse files Browse the repository at this point in the history
… updates for invisible objects
  • Loading branch information
dweymouth committed Feb 23, 2025
1 parent 4dda1d6 commit 6dbb1a4
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 28 deletions.
1 change: 1 addition & 0 deletions internal/cache/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ func ResetThemeCaches() {

// destroyExpiredCanvases deletes objects from the canvases cache.
func destroyExpiredCanvases(now time.Time) {
log.Printf("pre-clean canvases size was %d", canvases.Len())
canvases.Range(func(obj fyne.CanvasObject, cinfo *canvasInfo) bool {
if cinfo.isExpired(now) {
canvases.Delete(obj)
Expand Down
21 changes: 16 additions & 5 deletions internal/cache/canvases.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,24 @@ func GetCanvasForObject(obj fyne.CanvasObject) fyne.Canvas {
// SetCanvasForObject sets the canvas for the specified object.
// The passed function will be called if the item was not previously attached to this canvas
func SetCanvasForObject(obj fyne.CanvasObject, c fyne.Canvas, setup func()) {
cinfo := &canvasInfo{canvas: c}
cinfo.setAlive()

old, found := canvases.LoadOrStore(obj, cinfo)
if (!found || old.canvas != c) && setup != nil {
old, found := canvases.Load(obj)
needSetup := false
if found {
old.setAlive()
if old.canvas != c {
old.canvas = c
needSetup = setup != nil
}
} else {
needSetup = setup != nil
cinfo := &canvasInfo{canvas: c}
cinfo.setAlive()
canvases.Store(obj, cinfo)
}
if needSetup {
setup()
}

if canvases.Len() > 2*canvasCacheLastCleanSize {
shouldCleanCanvases = true
}
Expand Down
2 changes: 1 addition & 1 deletion internal/driver/common/canvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func (c *Canvas) EnsureMinSize() bool {
}
}
}
c.WalkTrees(nil, ensureMinSize)
c.WalkCompleteTrees(nil, ensureMinSize)

shouldResize := windowNeedsMinSizeUpdate && (csize.Width < min.Width || csize.Height < min.Height)
if shouldResize {
Expand Down
16 changes: 5 additions & 11 deletions internal/driver/glfw/canvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ func (c *glCanvas) overlayChanged() {
// markAlive walks the object trees for this canvas and marks
// all cache entries for visible objects alive.
// It should be called on inactive windows when a clean is requested.
func (c *glCanvas) markAlive() {
func (c *glCanvas) markAlive(visibleOnly bool) {
mark := func(node *common.RenderCacheNode, pos fyne.Position) {
obj := node.Obj()
_ = cache.GetCanvasForObject(obj)
Expand All @@ -262,17 +262,11 @@ func (c *glCanvas) markAlive() {
}
}

c.WalkTrees(mark, nil)
}

// markObjectsAlive marks the CanvasForObject cache as alive for
// the canvas's full contents, including invisible widgets.
// It should be called for all windows when a clean is requested.
func (c *glCanvas) markObjectsAlive() {
mark := func(node *common.RenderCacheNode, pos fyne.Position) {
_ = cache.GetCanvasForObject(node.Obj())
if visibleOnly {
c.WalkTrees(mark, nil)
} else {
c.WalkCompleteTrees(mark, nil)
}
c.WalkCompleteTrees(mark, nil)
}

func (c *glCanvas) paint(size fyne.Size) {
Expand Down
17 changes: 6 additions & 11 deletions internal/driver/glfw/loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,6 @@ func (d *gLDriver) drawSingleFrame() {
continue
}

// Perform a complete walk of the canvas, including invisible,
// to mark all contained CanvasObjects as alive
// We do not need to do this if the clean tasks do not
// include the canvases (CanvasForObject) clean
if shouldCleanCanvases {
w.canvas.markObjectsAlive()
}

canvas := w.canvas

// CheckDirtyAndClear must be checked after visibility,
Expand All @@ -83,7 +75,7 @@ func (d *gLDriver) drawSingleFrame() {
// Do the clear if and only if the window is visible.
if !w.visible || !canvas.CheckDirtyAndClear() {
if shouldClean {
d.cleanInactiveWindowTextures(w)
d.cleanInactiveWindowTextures(w, !shouldCleanCanvases /*walkVisibleOnly*/)
}
continue
}
Expand Down Expand Up @@ -191,11 +183,11 @@ func (d *gLDriver) destroyWindow(w *window, index int) {
}
}

func (d *gLDriver) cleanInactiveWindowTextures(w *window) {
func (d *gLDriver) cleanInactiveWindowTextures(w *window, walkVisibleOnly bool) {
w.RunWithContext(func() {
// Walk trees of inactive window and mark its visible contents
// as alive in all caches, so they are not cleaned.
w.canvas.markAlive()
w.canvas.markAlive(walkVisibleOnly)
var texFree func(fyne.CanvasObject)
if w.canvas.Painter() != nil {
texFree = w.canvas.Painter().Free
Expand Down Expand Up @@ -224,6 +216,9 @@ func (d *gLDriver) repaintWindow(w *window, cleanTextures bool) bool {
}

if cleanTextures {
// the object tree walks in EnsureMinSize and canvas.paint
// will have ensured all alive objects are marked in the caches
// No need to call canvas.markAlive
var texFree func(fyne.CanvasObject)
if canvas.Painter() != nil {
texFree = canvas.Painter().Free
Expand Down

0 comments on commit 6dbb1a4

Please sign in to comment.