Skip to content

Commit b6bf4d6

Browse files
authored
Merge branch 'develop' into settings-callback
2 parents 37d8213 + e553897 commit b6bf4d6

25 files changed

+779
-238
lines changed

app/preferences.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func (p *preferences) resetSavedRecently() {
5151
time.Sleep(time.Millisecond * 100) // writes are not always atomic. 10ms worked, 100 is safer.
5252

5353
// For test reasons we need to use current app not what we were initialised with as they can differ
54-
fyne.Do(func() {
54+
fyne.DoAndWait(func() {
5555
p.prefLock.Lock()
5656
p.savedRecently = false
5757
changedDuringSaving := p.changedDuringSaving

data/binding/queue.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ func queueItem(f func()) {
1111
return
1212
}
1313

14-
fyne.Do(f)
14+
fyne.DoAndWait(f)
1515
}

dialog/file.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"os"
77
"path/filepath"
88
"runtime"
9+
"sort"
910
"strings"
1011

1112
"fyne.io/fyne/v2"
@@ -422,6 +423,18 @@ func (f *fileDialog) refreshDir(dir fyne.ListableURI) {
422423
}
423424
}
424425

426+
toSort := icons
427+
if parent != nil {
428+
toSort = icons[1:]
429+
}
430+
sort.Slice(toSort, func(i, j int) bool {
431+
if parent != nil { // avoiding the parent in [0]
432+
i++
433+
j++
434+
}
435+
436+
return strings.ToLower(icons[i].Name()) < strings.ToLower(icons[j].Name())
437+
})
425438
f.data = icons
426439

427440
f.files.Refresh()

dialog/file_test.go

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ func TestHiddenFiles(t *testing.T) {
276276
t.Error("Failed to open testdata dir", err)
277277
}
278278

279-
// git does not preserve windows hidden flag so we have to set it.
279+
// git does not preserve windows hidden flag, so we have to set it.
280280
// just an empty function for non windows builds
281281
if err := hideFile(filepath.Join(testDataPath, ".hidden")); err != nil {
282282
t.Error("Failed to hide .hidden", err)
@@ -326,7 +326,7 @@ func TestHiddenFiles(t *testing.T) {
326326
target = item
327327
}
328328
}
329-
assert.NotNil(t, target, "Failed,.hidden not found in testdata")
329+
assert.NotNil(t, target, "Failed, .hidden not found in testdata")
330330
}
331331

332332
func TestShowFileSave(t *testing.T) {
@@ -471,6 +471,47 @@ func TestFileFilters(t *testing.T) {
471471
assert.Equal(t, 11, count)
472472
}
473473

474+
func TestFileSort(t *testing.T) {
475+
testDataPath, _ := filepath.Abs("testdata")
476+
testData := storage.NewFileURI(testDataPath)
477+
dir, err := storage.ListerForURI(testData)
478+
if err != nil {
479+
t.Error("Failed to open testdata dir", err)
480+
}
481+
482+
win := test.NewTempWindow(t, widget.NewLabel("Content"))
483+
d := NewFileOpen(func(file fyne.URIReadCloser, err error) {
484+
}, win)
485+
d.SetLocation(dir)
486+
d.Show()
487+
488+
popup := win.Canvas().Overlays().Top().(*widget.PopUp)
489+
defer win.Canvas().Overlays().Remove(popup)
490+
assert.NotNil(t, popup)
491+
492+
ui := popup.Content.(*fyne.Container)
493+
494+
files := ui.Objects[0].(*container.Split).Trailing.(*fyne.Container).Objects[1].(*container.Scroll).Content.(*fyne.Container).Objects[0].(*widget.GridWrap)
495+
objects := test.TempWidgetRenderer(t, files).Objects()[0].(*container.Scroll).Content.(*fyne.Container).Objects
496+
assert.NotEmpty(t, objects)
497+
498+
binPos := -1
499+
capitalPos := -1
500+
for i, icon := range objects {
501+
item := test.TempWidgetRenderer(t, icon.(fyne.Widget)).Objects()[1].(*fileDialogItem)
502+
switch item.name {
503+
case "bin":
504+
binPos = i
505+
case "Capitalised":
506+
capitalPos = i
507+
}
508+
}
509+
510+
assert.NotEqual(t, -1, binPos, "bin file not found")
511+
assert.NotEqual(t, -1, capitalPos, "Capitalised.txt file not found")
512+
assert.Less(t, binPos, capitalPos)
513+
}
514+
474515
func TestView(t *testing.T) {
475516
win := test.NewTempWindow(t, widget.NewLabel("Content"))
476517

dialog/file_xdg_flatpak.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,14 @@ func fileOpenOSOverride(d *FileDialog) bool {
8383
if folder {
8484
go func() {
8585
folder, err := openFolder(windowHandle, options)
86-
fyne.Do(func() {
86+
fyne.DoAndWait(func() {
8787
folderCallback(folder, err)
8888
})
8989
}()
9090
} else {
9191
go func() {
9292
file, err := openFile(windowHandle, options)
93-
fyne.Do(func() {
93+
fyne.DoAndWait(func() {
9494
fileCallback(file, err)
9595
})
9696
}()
@@ -113,7 +113,7 @@ func fileSaveOSOverride(d *FileDialog) bool {
113113

114114
go func() {
115115
file, err := saveFile(windowHandle, options)
116-
fyne.Do(func() {
116+
fyne.DoAndWait(func() {
117117
callback(file, err)
118118
})
119119
}()

dialog/testdata/Capitalised.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
adsfasdfasdfasdfdsfasdfasdfafdafdasf

driver.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ type Driver interface {
4747
// Since: 2.5
4848
SetDisableScreenBlanking(bool)
4949

50-
// DoFromGoroutine provides a way to queue a function that is running on a goroutine back to
51-
// the central thread for Fyne updates.
50+
// DoFromGoroutine provides a way to queue a function `fn` that is running on a goroutine back to
51+
// the central thread for Fyne updates, waiting for it to return if `wait` is true.
5252
// The driver provides the implementation normally accessed through [fyne.Do].
5353
// This is required when background tasks want to execute code safely in the graphical context.
5454
//
5555
// Since: 2.6
56-
DoFromGoroutine(func())
56+
DoFromGoroutine(fn func(), wait bool)
5757
}

internal/app/lifecycle.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,9 @@ func (l *Lifecycle) QueueEvent(fn func()) {
106106

107107
// RunEventQueue runs the event queue. This should called inside a go routine.
108108
// This function blocks.
109-
func (l *Lifecycle) RunEventQueue(run func(func())) {
109+
func (l *Lifecycle) RunEventQueue(run func(func(), bool)) {
110110
for fn := range l.eventQueue.Out() {
111-
run(fn)
111+
run(fn, true)
112112
}
113113
}
114114

internal/app/lifecycle_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func TestLifecycle(t *testing.T) {
1717

1818
var entered, exited, start, stop, hookedStop, called bool
1919
life.InitEventQueue()
20-
go life.RunEventQueue(func(fn func()) {
20+
go life.RunEventQueue(func(fn func(), _ bool) {
2121
fn()
2222
})
2323
life.QueueEvent(func() { called = true })

internal/async/goroutine.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ func IsMainGoroutine() bool {
3030
//
3131
// This will be removed later and should never be public
3232
func EnsureNotMain(fn func()) {
33-
if build.MigratedToFyneDo() || !build.HasHints || !IsMainGoroutine() {
33+
if build.MigratedToFyneDo() || !IsMainGoroutine() {
3434
fn()
3535
return
3636
}
3737

38-
log.Println("*** Error in Fyne call thread, fyne.Do called from main goroutine ***")
38+
log.Println("*** Error in Fyne call thread, fyne.Do[AndWait] called from main goroutine ***")
3939

4040
logStackTop(2)
4141
go fn()
@@ -47,15 +47,15 @@ func EnsureNotMain(fn func()) {
4747
//
4848
// This will be removed later and should never be public
4949
func EnsureMain(fn func()) {
50-
if build.MigratedToFyneDo() || !build.HasHints || IsMainGoroutine() {
50+
if build.MigratedToFyneDo() || IsMainGoroutine() {
5151
fn()
5252
return
5353
}
5454

55-
log.Println("*** Error in Fyne call thread, this should have been called in fyne.Do ***")
55+
log.Println("*** Error in Fyne call thread, this should have been called in fyne.Do[AndWait] ***")
5656

5757
logStackTop(1)
58-
fyne.Do(fn)
58+
fyne.DoAndWait(fn)
5959
}
6060

6161
func logStackTop(skip int) {

internal/driver/glfw/driver.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ func toOSIcon(icon []byte) ([]byte, error) {
5656
return buf.Bytes(), nil
5757
}
5858

59-
func (d *gLDriver) DoFromGoroutine(f func()) {
59+
func (d *gLDriver) DoFromGoroutine(f func(), wait bool) {
6060
async.EnsureNotMain(func() {
61-
runOnMain(f)
61+
runOnMainWithWait(f, wait)
6262
})
6363
}
6464

internal/driver/glfw/loop.go

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"fyne.io/fyne/v2"
1010
"fyne.io/fyne/v2/internal/app"
11+
"fyne.io/fyne/v2/internal/async"
1112
"fyne.io/fyne/v2/internal/cache"
1213
"fyne.io/fyne/v2/internal/driver/common"
1314
"fyne.io/fyne/v2/internal/painter"
@@ -20,7 +21,7 @@ type funcData struct {
2021
}
2122

2223
// channel for queuing functions on the main thread
23-
var funcQueue = make(chan funcData)
24+
var funcQueue = async.NewUnboundedChan[funcData]()
2425
var running atomic.Bool
2526
var initOnce = &sync.Once{}
2627

@@ -31,19 +32,27 @@ func init() {
3132

3233
// force a function f to run on the main thread
3334
func runOnMain(f func()) {
35+
runOnMainWithWait(f, true)
36+
}
37+
38+
// force a function f to run on the main thread and specify if we should wait for it to return
39+
func runOnMainWithWait(f func(), wait bool) {
3440
// If we are on main just execute - otherwise add it to the main queue and wait.
3541
// The "running" variable is normally false when we are on the main thread.
3642
if !running.Load() {
3743
f()
3844
return
3945
}
4046

41-
done := common.DonePool.Get()
42-
defer common.DonePool.Put(done)
43-
44-
funcQueue <- funcData{f: f, done: done}
47+
if wait {
48+
done := common.DonePool.Get()
49+
defer common.DonePool.Put(done)
4550

46-
<-done
51+
funcQueue.In() <- funcData{f: f, done: done}
52+
<-done
53+
} else {
54+
funcQueue.In() <- funcData{f: f}
55+
}
4756
}
4857

4958
// Preallocate to avoid allocations on every drawSingleFrame.
@@ -110,9 +119,11 @@ func (d *gLDriver) runGL() {
110119
l.QueueEvent(f)
111120
}
112121
return
113-
case f := <-funcQueue:
122+
case f := <-funcQueue.Out():
114123
f.f()
115-
f.done <- struct{}{}
124+
if f.done != nil {
125+
f.done <- struct{}{}
126+
}
116127
case <-eventTick.C:
117128
d.pollEvents()
118129
for i := 0; i < len(d.windows); i++ {

internal/driver/glfw/window.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -867,11 +867,7 @@ func (w *window) Context() any {
867867

868868
func (w *window) runOnMainWhenCreated(fn func()) {
869869
if w.view() != nil {
870-
if async.IsMainGoroutine() {
871-
fn()
872-
return
873-
}
874-
fyne.Do(fn)
870+
async.EnsureMain(fn)
875871
return
876872
}
877873

internal/driver/glfw/window_desktop.go

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -114,23 +114,25 @@ type window struct {
114114
}
115115

116116
func (w *window) SetFullScreen(full bool) {
117-
w.fullScreen = full
118-
if !w.visible {
119-
return
120-
}
117+
w.runOnMainWhenCreated(func() {
118+
w.fullScreen = full
119+
if !w.visible {
120+
return
121+
}
121122

122-
monitor := w.getMonitorForWindow()
123-
mode := monitor.GetVideoMode()
123+
monitor := w.getMonitorForWindow()
124+
mode := monitor.GetVideoMode()
124125

125-
if full {
126-
w.viewport.SetMonitor(monitor, 0, 0, mode.Width, mode.Height, mode.RefreshRate)
127-
} else {
128-
if w.width == 0 && w.height == 0 { // if we were fullscreen on creation...
129-
s := w.canvas.Size().Max(w.canvas.MinSize())
130-
w.width, w.height = w.screenSize(s)
126+
if full {
127+
w.viewport.SetMonitor(monitor, 0, 0, mode.Width, mode.Height, mode.RefreshRate)
128+
} else {
129+
if w.width == 0 && w.height == 0 { // if we were fullscreen on creation...
130+
s := w.canvas.Size().Max(w.canvas.MinSize())
131+
w.width, w.height = w.screenSize(s)
132+
}
133+
w.viewport.SetMonitor(nil, w.xpos, w.ypos, w.width, w.height, 0)
131134
}
132-
w.viewport.SetMonitor(nil, w.xpos, w.ypos, w.width, w.height, 0)
133-
}
135+
})
134136
}
135137

136138
func (w *window) CenterOnScreen() {

internal/driver/mobile/canvas.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ func (c *canvas) waitForDoubleTap(co fyne.CanvasObject, ev *fyne.PointEvent, tap
407407
c.touchCancelFunc = nil
408408
c.touchLastTapped = nil
409409
c.touchCancelLock.Unlock()
410-
})
410+
}, true)
411411
}
412412

413413
func (c *canvas) windowHeadIsDisplacing() bool {

internal/driver/mobile/canvas_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ func Test_canvas_Focusable(t *testing.T) {
142142
c.tapUp(pos, 0, func(wid fyne.Tappable, ev *fyne.PointEvent) {
143143
wid.Tapped(ev)
144144
}, nil, nil, nil)
145-
})
145+
}, true)
146146

147147
waitAndCheck(tapDoubleDelay/time.Millisecond+150, func() {
148148
assert.Equal(t, 1, content.focusedTimes)
@@ -154,7 +154,7 @@ func Test_canvas_Focusable(t *testing.T) {
154154
c.tapUp(pos, 1, func(wid fyne.Tappable, ev *fyne.PointEvent) {
155155
wid.Tapped(ev)
156156
}, nil, nil, nil)
157-
})
157+
}, true)
158158
waitAndCheck(tapDoubleDelay/time.Millisecond+150, func() {
159159
assert.Equal(t, 1, content.focusedTimes)
160160
assert.Equal(t, 0, content.unfocusedTimes)
@@ -177,7 +177,7 @@ func Test_canvas_Focusable(t *testing.T) {
177177
c.tapDown(fyne.NewPos(10, 10), 2)
178178
assert.Equal(t, 1, content.focusedTimes)
179179
assert.Equal(t, 1, content.unfocusedTimes)
180-
})
180+
}, true)
181181
}
182182

183183
func Test_canvas_InteractiveArea(t *testing.T) {
@@ -534,7 +534,7 @@ func waitAndCheck(msWait time.Duration, fn func()) {
534534
fn()
535535

536536
waitForCheck <- struct{}{}
537-
})
537+
}, true)
538538
}()
539539
<-waitForCheck
540540
}

0 commit comments

Comments
 (0)