Skip to content

Commit

Permalink
Add a universal CanvasObject.Refresh() that does the right thing.
Browse files Browse the repository at this point in the history
Use this to finally remove access to the widget renderers
  • Loading branch information
andydotxyz committed Nov 19, 2019
1 parent 9423a11 commit f32bf9c
Show file tree
Hide file tree
Showing 21 changed files with 150 additions and 51 deletions.
3 changes: 0 additions & 3 deletions canvas/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ package canvas // import "fyne.io/fyne/canvas"

import "fyne.io/fyne"

// Declare conformity with CanvasObject interface
var _ fyne.CanvasObject = (*baseObject)(nil)

type baseObject struct {
size fyne.Size // The current size of the Rectangle
position fyne.Position // The current position of the Rectangle
Expand Down
7 changes: 6 additions & 1 deletion canvas/circle.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,18 @@ func (l *Circle) Visible() bool {
func (l *Circle) Show() {
l.Hidden = false

Refresh(l)
l.Refresh()
}

// Hide will set this circle to not be visible
func (l *Circle) Hide() {
l.Hidden = true

l.Refresh()
}

// Refresh causes this object to be redrawn in it's current state
func (l *Circle) Refresh() {
Refresh(l)
}

Expand Down
10 changes: 10 additions & 0 deletions canvas/gradient.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ func (g *LinearGradient) Generate(w, h int) image.Image {
return computeGradient(generator, w, h, g.StartColor, g.EndColor)
}

// Refresh causes this object to be redrawn in it's current state
func (g *LinearGradient) Refresh() {
Refresh(g)
}

// RadialGradient defines a Gradient travelling radially from a center point outward.
type RadialGradient struct {
baseObject
Expand Down Expand Up @@ -106,6 +111,11 @@ func (g *RadialGradient) Generate(w, h int) image.Image {
return computeGradient(generator, w, h, g.StartColor, g.EndColor)
}

// Refresh causes this object to be redrawn in it's current state
func (g *RadialGradient) Refresh() {
Refresh(g)
}

func calculatePixel(d float64, startColor, endColor color.Color) *color.RGBA64 {
// fetch RGBA values
aR, aG, aB, aA := startColor.RGBA()
Expand Down
5 changes: 5 additions & 0 deletions canvas/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ func (i *Image) Alpha() float64 {
return 1.0 - i.Translucency
}

// Refresh causes this object to be redrawn in it's current state
func (i *Image) Refresh() {
Refresh(i)
}

// NewImageFromFile creates a new image from a local file.
// Images returned from this method will scale to fit the canvas object.
// The method for scaling can be set using the Fill field.
Expand Down
7 changes: 6 additions & 1 deletion canvas/line.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,18 @@ func (l *Line) Visible() bool {
func (l *Line) Show() {
l.Hidden = false

Refresh(l)
l.Refresh()
}

// Hide will set this line to not be visible
func (l *Line) Hide() {
l.Hidden = true

l.Refresh()
}

// Refresh causes this object to be redrawn in it's current state
func (l *Line) Refresh() {
Refresh(l)
}

Expand Down
5 changes: 5 additions & 0 deletions canvas/raster.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ func (r *Raster) Alpha() float64 {
return 1.0 - r.Translucency
}

// Refresh causes this object to be redrawn in it's current state
func (r *Raster) Refresh() {
Refresh(r)
}

// NewRaster returns a new Image instance that is rendered dynamically using
// the specified generate function.
// Images returned from this method should draw dynamically to fill the width
Expand Down
5 changes: 5 additions & 0 deletions canvas/rectangle.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ type Rectangle struct {
StrokeWidth float32 // The stroke width of the rectangle
}

// Refresh causes this object to be redrawn in it's current state
func (r *Rectangle) Refresh() {
Refresh(r)
}

// NewRectangle returns a new Rectangle instance
func NewRectangle(color color.Color) *Rectangle {
return &Rectangle{
Expand Down
5 changes: 5 additions & 0 deletions canvas/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ func (t *Text) MinSize() fyne.Size {
return fyne.CurrentApp().Driver().RenderedTextSize(t.Text, t.TextSize, t.TextStyle)
}

// Refresh causes this object to be redrawn in it's current state
func (t *Text) Refresh() {
Refresh(t)
}

// NewText returns a new Text implementation
func NewText(text string, color color.Color) *Text {
return &Text{
Expand Down
2 changes: 2 additions & 0 deletions canvasobject.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type CanvasObject interface {
Visible() bool
Show()
Hide()

Refresh()
}

// Themeable indicates that the associated CanvasObject responds to theme
Expand Down
5 changes: 5 additions & 0 deletions container.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ func (c *Container) AddObject(o CanvasObject) {
c.layout()
}

// Refresh causes this object to be redrawn in it's current state
func (c *Container) Refresh() {
c.layout()
}

// NewContainer returns a new Container instance holding the specified CanvasObjects.
func NewContainer(objects ...CanvasObject) *Container {
ret := &Container{
Expand Down
3 changes: 3 additions & 0 deletions container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,6 @@ func (d *dummyObject) Show() {
func (d *dummyObject) Hide() {
d.hidden = true
}

func (d *dummyObject) Refresh() {
}
49 changes: 49 additions & 0 deletions internal/cache/widget.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package cache

import (
"sync"

"fyne.io/fyne"
)

var renderers sync.Map

type isBaseWidget interface {
ExtendBaseWidget(fyne.Widget)
super() fyne.Widget
}

// Renderer looks up the render implementation for a widget
func Renderer(wid fyne.Widget) fyne.WidgetRenderer {
if wid == nil {
return nil
}

if wd, ok := wid.(isBaseWidget); ok {
if wd.super() != nil {
wid = wd.super()
}
}
renderer, ok := renderers.Load(wid)
if !ok {
renderer = wid.CreateRenderer()
renderers.Store(wid, renderer)
}

return renderer.(fyne.WidgetRenderer)
}

// DestroyRenderer frees a render implementation for a widget.
// This is typically for internal use only.
func DestroyRenderer(wid fyne.Widget) {
Renderer(wid).Destroy()

renderers.Delete(wid)
}

// IsRendered returns true of the widget currently has a renderer.
// One will be created the first time a widget is shown but may be removed after it is hidden.
func IsRendered(wid fyne.Widget) bool {
_, found := renderers.Load(wid)
return found
}
3 changes: 2 additions & 1 deletion internal/driver/glfw/window.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"fyne.io/fyne"
"fyne.io/fyne/driver/desktop"
"fyne.io/fyne/internal"
"fyne.io/fyne/internal/cache"
"fyne.io/fyne/internal/driver"
"fyne.io/fyne/internal/painter/gl"
"fyne.io/fyne/widget"
Expand Down Expand Up @@ -407,7 +408,7 @@ func (w *window) closed(viewport *glfw.Window) {
w.canvas.walkTrees(nil, func(node *renderCacheNode) {
switch co := node.obj.(type) {
case fyne.Widget:
widget.DestroyRenderer(co)
cache.DestroyRenderer(co)
}
})

Expand Down
3 changes: 2 additions & 1 deletion internal/driver/gomobile/window.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gomobile

import (
"fyne.io/fyne"
"fyne.io/fyne/internal/cache"
"fyne.io/fyne/layout"
"fyne.io/fyne/theme"
"fyne.io/fyne/widget"
Expand Down Expand Up @@ -143,7 +144,7 @@ func (w *window) Close() {
w.canvas.walkTree(nil, func(obj, _ fyne.CanvasObject) {
switch co := obj.(type) {
case fyne.Widget:
widget.DestroyRenderer(co)
cache.DestroyRenderer(co)
}
})

Expand Down
4 changes: 4 additions & 0 deletions layout/spacer.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ func (s *Spacer) Hide() {
s.hidden = true
}

// Refresh does nothing for a spacer but is part of the CanvasObject definition
func (s *Spacer) Refresh() {
}

// NewSpacer returns a spacer object which can fill vertical and horizontal
// space. This is primarily used with a box layout.
func NewSpacer() fyne.CanvasObject {
Expand Down
3 changes: 3 additions & 0 deletions widget/box.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ func (b *boxRenderer) Objects() []fyne.CanvasObject {

func (b *boxRenderer) Refresh() {
b.objects = b.box.Children
for _, child := range b.objects {
child.Refresh()
}
b.Layout(b.box.Size())

canvas.Refresh(b.box)
Expand Down
5 changes: 2 additions & 3 deletions widget/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -1059,22 +1059,21 @@ func NewEntry() *Entry {
e := &Entry{}
e.ExtendBaseWidget(e)
e.registerShortcut()
// Refresh(e)
return e
}

// NewMultiLineEntry creates a new entry that allows multiple lines
func NewMultiLineEntry() *Entry {
e := &Entry{MultiLine: true}
e.registerShortcut()
Refresh(e)
e.ExtendBaseWidget(e)
return e
}

// NewPasswordEntry creates a new entry password widget
func NewPasswordEntry() *Entry {
e := &Entry{Password: true}
e.registerShortcut()
Refresh(e)
e.ExtendBaseWidget(e)
return e
}
3 changes: 1 addition & 2 deletions widget/icon.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ type Icon struct {
// SetResource updates the resource rendered in this icon widget
func (i *Icon) SetResource(res fyne.Resource) {
i.Resource = res

Refresh(i)
i.refresh(i)
}

// MinSize returns the size that this widget should not shrink below
Expand Down
10 changes: 5 additions & 5 deletions widget/progressbarinfinite.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"fyne.io/fyne"
"fyne.io/fyne/canvas"
"fyne.io/fyne/internal/cache"
"fyne.io/fyne/theme"
)

Expand Down Expand Up @@ -148,22 +149,21 @@ func (p *ProgressBarInfinite) Hide() {

// Start the infinite progress bar background thread to update it continuously
func (p *ProgressBarInfinite) Start() {
Renderer(p).(*infProgressRenderer).start()
cache.Renderer(p).(*infProgressRenderer).start()
}

// Stop the infinite progress goroutine and sets value to the Max
func (p *ProgressBarInfinite) Stop() {
Renderer(p).(*infProgressRenderer).stop()
cache.Renderer(p).(*infProgressRenderer).stop()
}

// Running returns the current state of the infinite progress animation
func (p *ProgressBarInfinite) Running() bool {
renderer, ok := renderers.Load(p)
if !ok {
if !cache.IsRendered(p) {
return false
}

return renderer.(*infProgressRenderer).running.Load().(bool)
return cache.Renderer(p).(*infProgressRenderer).running.Load().(bool)
}

// MinSize returns the size that this widget should not shrink below
Expand Down
9 changes: 4 additions & 5 deletions widget/progressbarinfinite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

"fyne.io/fyne"
"fyne.io/fyne/internal/cache"
"github.com/stretchr/testify/assert"
)

Expand All @@ -16,17 +17,15 @@ func TestProgressBarInfinite_Creation(t *testing.T) {

func TestProgressBarInfinite_Destroy(t *testing.T) {
bar := NewProgressBarInfinite()
_, found := renderers.Load(bar)
assert.True(t, found)
assert.True(t, cache.IsRendered(bar))
assert.True(t, bar.Running())

// check that it stopped
DestroyRenderer(bar)
cache.DestroyRenderer(bar)
assert.False(t, bar.Running())

// and that the cache was removed
_, found = renderers.Load(bar)
assert.False(t, found)
assert.False(t, cache.IsRendered(bar))
}

func TestProgressBarInfinite_Reshown(t *testing.T) {
Expand Down
Loading

0 comments on commit f32bf9c

Please sign in to comment.