Skip to content

Commit

Permalink
Merge pull request #5392 from andydotxyz/feature/invoke
Browse files Browse the repository at this point in the history
Add a fyne.Do method for developers
  • Loading branch information
andydotxyz authored Jan 11, 2025
2 parents 2355f9d + 82e2aa5 commit 9d4dce9
Show file tree
Hide file tree
Showing 20 changed files with 50 additions and 45 deletions.
2 changes: 1 addition & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (a *fyneApp) NewWindow(title string) fyne.Window {
}

func (a *fyneApp) Run() {
go a.lifecycle.RunEventQueue(a.driver.CallFromGoroutine)
go a.lifecycle.RunEventQueue(a.driver.DoFromGoroutine)
a.driver.Run()
}

Expand Down
2 changes: 1 addition & 1 deletion app/preferences.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (p *preferences) resetSavedRecently() {
time.Sleep(time.Millisecond * 100) // writes are not always atomic. 10ms worked, 100 is safer.

// For test reasons we need to use current app not what we were initialised with as they can differ
fyne.CurrentApp().Driver().CallFromGoroutine(func() {
fyne.Do(func() {
p.prefLock.Lock()
p.savedRecently = false
changedDuringSaving := p.changedDuringSaving
Expand Down
2 changes: 1 addition & 1 deletion app/preferences_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ func (p *preferences) watch() {
return
}

fyne.CurrentApp().Driver().CallFromGoroutine(p.load)
fyne.Do(p.load)
})
}
2 changes: 1 addition & 1 deletion app/settings_desktop.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func watchFile(path string, callback func()) *fsnotify.Watcher {

watchFileAddTarget(watcher, path)
} else {
callback()
fyne.Do(callback)
}
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/fyne_demo/tutorials/advanced.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func advancedScreen(win fyne.Window) fyne.CanvasObject {

go func(canvas fyne.Canvas) {
for range time.NewTicker(time.Second).C {
fyne.CurrentApp().Driver().CallFromGoroutine(func() {
fyne.Do(func() {
scale.SetText(scaleToString(canvas))
tex.SetText(textureScaleToString(canvas))
})
Expand Down
2 changes: 1 addition & 1 deletion cmd/fyne_demo/tutorials/canvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func canvasScreen(_ fyne.Window) fyne.CanvasObject {
gradient := canvas.NewHorizontalGradient(color.NRGBA{0x80, 0, 0, 0xff}, color.NRGBA{0, 0x80, 0, 0xff})
go func() {
for range time.NewTicker(time.Second).C {
fyne.CurrentApp().Driver().CallFromGoroutine(func() {
fyne.Do(func() {
gradient.Angle += 45
if gradient.Angle >= 360 {
gradient.Angle -= 360
Expand Down
2 changes: 1 addition & 1 deletion cmd/fyne_demo/tutorials/welcome.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func welcomeScreen(_ fyne.Window) fyne.CanvasObject {
fyne.CurrentApp().Settings().AddChangeListener(listen)
go func() {
for range listen {
fyne.CurrentApp().Driver().CallFromGoroutine(func() {
fyne.Do(func() {
bgColor = withAlpha(theme.Color(theme.ColorNameBackground), 0xe0)
bg.FillColor = bgColor
bg.Refresh()
Expand Down
4 changes: 2 additions & 2 deletions cmd/fyne_demo/tutorials/widget.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func makeActivityTab(win fyne.Window) fyne.CanvasObject {
a2.Show()

time.AfterFunc(10*time.Second, func() {
fyne.CurrentApp().Driver().CallFromGoroutine(func() {
fyne.Do(func() {
a1.Stop()
a1.Hide()
a2.Stop()
Expand Down Expand Up @@ -90,7 +90,7 @@ func makeActivityTab(win fyne.Window) fyne.CanvasObject {
d.Show()

time.AfterFunc(5*time.Second, func() {
fyne.CurrentApp().Driver().CallFromGoroutine(func() {
fyne.Do(func() {
a3.Stop()
d.Hide()
})
Expand Down
2 changes: 1 addition & 1 deletion cmd/fyne_demo/tutorials/window.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func windowScreen(_ fyne.Window) fyne.CanvasObject {
w.Show()

time.AfterFunc(3*time.Second, func() {
fyne.CurrentApp().Driver().CallFromGoroutine(w.Close)
fyne.Do(w.Close)
})
}))
}
Expand Down
2 changes: 1 addition & 1 deletion data/binding/queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ func queueItem(f func()) {
return
}

fyne.CurrentApp().Driver().CallFromGoroutine(f)
fyne.Do(f)
}
12 changes: 9 additions & 3 deletions driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import "time"
// Driver defines an abstract concept of a Fyne render driver.
// Any implementation must provide at least these methods.
type Driver interface {
// CreateWindow creates a new UI Window.
// CreateWindow creates a new UI Window for a certain implementation.
// Developers should use [App.NewWindow].
CreateWindow(string) Window
// AllWindows returns a slice containing all app windows.
AllWindows() []Window
Expand All @@ -29,8 +30,10 @@ type Driver interface {
Quit()

// StartAnimation registers a new animation with this driver and requests it be started.
// Developers should use the [Animation.Start] function.
StartAnimation(*Animation)
// StopAnimation stops an animation and unregisters from this driver.
// Developers should use the [Animation.Stop] function.
StopAnimation(*Animation)

// DoubleTapDelay returns the maximum duration where a second tap after a first one
Expand All @@ -44,8 +47,11 @@ type Driver interface {
// Since: 2.5
SetDisableScreenBlanking(bool)

// CallFromGoroutine provides a way to queue a function that is running on a goroutine back to the main thread.
// DoFromGoroutine provides a way to queue a function that is running on a goroutine back to
// the central thread for Fyne updates.
// The driver provides the implementation normally accessed through [fyne.Do].
// This is required when background tasks want to execute code safely in the graphical context.
//
// Since: 2.6
CallFromGoroutine(func())
DoFromGoroutine(func())
}
14 changes: 9 additions & 5 deletions internal/driver/glfw/canvas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,16 +411,20 @@ func TestGlCanvas_ResizeWithOtherOverlay(t *testing.T) {
w.Canvas().Overlays().Add(over)
ensureCanvasSize(t, w, fyne.NewSize(69, 36))
// TODO: address #707; overlays should always be canvas size
over.Resize(w.Canvas().Size())
size := w.Canvas().Size()
runOnMain(func() {
over.Resize(size)
})

size := fyne.NewSize(200, 100)
size = fyne.NewSize(200, 100)
assert.NotEqual(t, size, content.Size())
assert.NotEqual(t, size, over.Size())

w.Resize(size)
ensureCanvasSize(t, w, size)
assert.Equal(t, size, content.Size(), "canvas content is resized")
assert.Equal(t, size, over.Size(), "canvas overlay is resized")
runOnMain(func() {
assert.Equal(t, size, content.Size(), "canvas content is resized")
assert.Equal(t, size, over.Size(), "canvas overlay is resized")
})
}

func TestGlCanvas_ResizeWithOverlays(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion internal/driver/glfw/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func toOSIcon(icon []byte) ([]byte, error) {
return buf.Bytes(), nil
}

func (d *gLDriver) CallFromGoroutine(f func()) {
func (d *gLDriver) DoFromGoroutine(f func()) {
runOnMain(f)
}

Expand Down
13 changes: 0 additions & 13 deletions internal/driver/glfw/window_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"net/url"
"os"
"runtime"
"sync/atomic"
"testing"
"time"

Expand Down Expand Up @@ -1215,14 +1214,11 @@ func TestWindow_TappedAndDoubleTapped(t *testing.T) {
w := createWindow("Test")
waitSingleTapped := make(chan struct{}, 1)
waitDoubleTapped := make(chan struct{}, 1)
tapped := atomic.Int32{}
but := newDoubleTappableButton()
but.OnTapped = func() {
tapped.Store(1)
waitSingleTapped <- struct{}{}
}
but.onDoubleTap = func() {
tapped.Store(2)
waitDoubleTapped <- struct{}{}
}
w.SetContent(but)
Expand All @@ -1233,24 +1229,15 @@ func TestWindow_TappedAndDoubleTapped(t *testing.T) {
w.mouseClicked(w.viewport, glfw.MouseButton1, glfw.Press, 0)
w.mouseClicked(w.viewport, glfw.MouseButton1, glfw.Release, 0)
})

<-waitSingleTapped
time.Sleep(500 * time.Millisecond)

assert.Equal(t, int32(1), tapped.Load(), "Single tap should have fired")
tapped.Store(0)

runOnMain(func() {
w.mouseClicked(w.viewport, glfw.MouseButton1, glfw.Press, 0)
w.mouseClicked(w.viewport, glfw.MouseButton1, glfw.Release, 0)
w.mouseClicked(w.viewport, glfw.MouseButton1, glfw.Press, 0)
w.mouseClicked(w.viewport, glfw.MouseButton1, glfw.Release, 0)
})

<-waitDoubleTapped
time.Sleep(500 * time.Millisecond)

assert.Equal(t, int32(2), tapped.Load(), "Double tap should have fired")
}

func TestWindow_MouseEventContainsModifierKeys(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion internal/driver/mobile/canvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ func (c *canvas) waitForDoubleTap(co fyne.CanvasObject, ev *fyne.PointEvent, tap
defer cancel()

<-ctx.Done()
fyne.CurrentApp().Driver().CallFromGoroutine(func() {
fyne.CurrentApp().Driver().DoFromGoroutine(func() {
c.touchCancelLock.Lock()
touchCount := c.touchTapCount
touchLast := c.touchLastTapped
Expand Down
8 changes: 4 additions & 4 deletions internal/driver/mobile/canvas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func Test_canvas_Focusable(t *testing.T) {
content.Resize(fyne.NewSize(25, 25))

pos := fyne.NewPos(10, 10)
d.CallFromGoroutine(func() {
d.DoFromGoroutine(func() {
c.tapDown(pos, 0)
c.tapUp(pos, 0, func(wid fyne.Tappable, ev *fyne.PointEvent) {
wid.Tapped(ev)
Expand All @@ -149,7 +149,7 @@ func Test_canvas_Focusable(t *testing.T) {
assert.Equal(t, 0, content.unfocusedTimes)
})

d.CallFromGoroutine(func() {
d.DoFromGoroutine(func() {
c.tapDown(pos, 1)
c.tapUp(pos, 1, func(wid fyne.Tappable, ev *fyne.PointEvent) {
wid.Tapped(ev)
Expand All @@ -160,7 +160,7 @@ func Test_canvas_Focusable(t *testing.T) {
assert.Equal(t, 0, content.unfocusedTimes)
})

d.CallFromGoroutine(func() {
d.DoFromGoroutine(func() {
c.Focus(content)
assert.Equal(t, 1, content.focusedTimes)
assert.Equal(t, 0, content.unfocusedTimes)
Expand Down Expand Up @@ -530,7 +530,7 @@ func waitAndCheck(msWait time.Duration, fn func()) {
defer common.DonePool.Put(waitForCheck)

time.Sleep(msWait * time.Millisecond)
d.CallFromGoroutine(func() {
d.DoFromGoroutine(func() {
fn()

waitForCheck <- struct{}{}
Expand Down
8 changes: 3 additions & 5 deletions internal/driver/mobile/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func init() {
runtime.LockOSThread()
}

func (d *driver) CallFromGoroutine(fn func()) {
func (d *driver) DoFromGoroutine(fn func()) {
done := common.DonePool.Get()
defer common.DonePool.Put(done)

Expand Down Expand Up @@ -433,15 +433,13 @@ func (d *driver) tapUpCanvas(w *window, x, y float32, tapID touch.Sequence) {
ev.Dragged.DY *= tapMoveDecay
}

d.CallFromGoroutine(func() {
d.DoFromGoroutine(func() {
wid.Dragged(ev)
})
time.Sleep(time.Millisecond * 16)
}

d.CallFromGoroutine(func() {
wid.DragEnd()
})
d.DoFromGoroutine(wid.DragEnd)
}()
})
}
Expand Down
2 changes: 1 addition & 1 deletion test/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ func (s *testSettings) apply() {
listener <- s
}

s.app.driver.CallFromGoroutine(func() {
s.app.driver.DoFromGoroutine(func() {
s.app.propertyLock.Lock()
painter.ClearFontCache()
cache.ResetThemeCaches()
Expand Down
2 changes: 1 addition & 1 deletion test/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func NewDriverWithPainter(painter SoftwarePainter) fyne.Driver {
return &driver{painter: painter}
}

func (d *driver) CallFromGoroutine(f func()) {
func (d *driver) DoFromGoroutine(f func()) {
f() // Tests all run on a single (but potentially different per-test) thread
}

Expand Down
10 changes: 10 additions & 0 deletions thread.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package fyne

// Do is used to execute a specified function in the main Fyne runtime context.
// This is required when a background process wishes to adjust graphical elements of a running app.
// Developers should use this only from within goroutines they have created.
//
// Since: 2.6
func Do(fn func()) {
CurrentApp().Driver().DoFromGoroutine(fn)
}

0 comments on commit 9d4dce9

Please sign in to comment.