Skip to content

Commit 2360626

Browse files
committed
Merge branch 'release/v2.5.x'
2 parents 4a875d9 + 0574526 commit 2360626

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+482
-162
lines changed

CHANGELOG.md

+21-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,26 @@
33
This file lists the main changes with each version of the Fyne toolkit.
44
More detailed release notes can be found on the [releases page](https://github.com/fyne-io/fyne/releases).
55

6+
## 2.5.4 - 1 February 2025
7+
8+
### Changed
9+
10+
* Added Tamil translation
11+
12+
### Fixed
13+
14+
* Checkbox not responding to click because it is too "large"? (#5331)
15+
* Fix progressbar not showing label until first refresh
16+
* FyneApp.toml causes BadLength error (#5272)
17+
* Test suite: failure with locale/language different from 'en' (#5362)
18+
* fix GridWrap crash when resizing to same size without creating renderer
19+
* Submenus not working on mobile (#5398)
20+
* Subtle scrolling bug in List when the last two items are of different size (#5281)
21+
* File picker does not ignore case (#5113)
22+
* Tab "OnSelected" doesn't appear to allow focussing tab content (#5454)
23+
* Documentation fixes
24+
25+
626
## 2.5.3 - 15 December 2024
727

828
### Changed
@@ -1128,7 +1148,7 @@ The import path is now `fyne.io/fyne/v2` when you are ready to make the update.
11281148
* Creating a windows inside onClose handler causes Fyne to panic (#1106)
11291149
* Backspace in entry after SetText("") can crash (#1096)
11301150
* Empty main menu causes panic (#1073)
1131-
* Installing using `fyne install` on Linux now works on distrubutions that don't use `/usr/local`
1151+
* Installing using `fyne install` on Linux now works on distributions that don't use `/usr/local`
11321152
* Fix recommendations from staticcheck
11331153
* Unable to overwrite file when using dialog.ShowFileSave (#1168)
11341154

app.go

+2
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ type App interface {
6464
Lifecycle() Lifecycle
6565

6666
// Metadata returns the application metadata that was set at compile time.
67+
// The items of metadata are available after "fyne package" or when running "go run"
68+
// Building with "go build" may cause this to be unavailable.
6769
//
6870
// Since: 2.2
6971
Metadata() AppMetadata

app/meta_development.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func checkLocalMetadata() {
3737
if data.Details.Icon != "" {
3838
res, err := fyne.LoadResourceFromPath(data.Details.Icon)
3939
if err == nil {
40-
meta.Icon = res
40+
meta.Icon = metadata.ScaleIcon(res, 512)
4141
}
4242
}
4343

cmd/fyne/internal/commands/build.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -340,10 +340,12 @@ func createMetadataInitFile(srcdir string, app *appData) (func(), error) {
340340
if app.icon != "" {
341341
res, err := fyne.LoadResourceFromPath(app.icon)
342342
if err != nil {
343-
fyne.LogError("Unable to load medadata icon file "+app.icon, err)
343+
fyne.LogError("Unable to load metadata icon file "+app.icon, err)
344344
return func() { os.Remove(metadataInitFilePath) }, err
345345
}
346346

347+
res = metadata.ScaleIcon(res, 512)
348+
347349
// The return type of fyne.LoadResourceFromPath is always a *fyne.StaticResource.
348350
app.ResGoString = res.(*fyne.StaticResource).GoString()
349351
}
@@ -442,6 +444,8 @@ func normaliseVersion(str string) string {
442444

443445
if pos := strings.Index(str, "-0.20"); pos != -1 {
444446
str = str[:pos] + "-dev"
447+
} else if pos = strings.Index(str, "-rc"); pos != -1 {
448+
str = str[:pos] + "-dev"
445449
}
446450
return version.Normalize(str)
447451
}

cmd/fyne/internal/commands/build_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,5 @@ func Test_NormaliseVersion(t *testing.T) {
170170
assert.Equal(t, "2.3.0.0", normaliseVersion("v2.3"))
171171
assert.Equal(t, "2.4.0.0", normaliseVersion("v2.4.0"))
172172
assert.Equal(t, "2.3.6.0-dev", normaliseVersion("v2.3.6-0.20230711180435-d4b95e1cb1eb"))
173+
assert.Equal(t, "2.4.1.0-dev", normaliseVersion("v2.4.1-rc7.0.20230711180435-d4b95e1cb1eb"))
173174
}

cmd/fyne/internal/mobile/binres/binres.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,7 @@ func poolTrim(s string) string {
992992
}
993993

994994
// byNamespace sorts attributes based on string pool position of namespace.
995-
// Given that "android" always preceds "" in the pool, this results in the
995+
// Given that "android" always precedes "" in the pool, this results in the
996996
// correct ordering of attributes.
997997
type byNamespace []*Attribute
998998

cmd/fyne/internal/mobile/binres/table.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ type Package struct {
254254
specs []*TypeSpec
255255
}
256256

257-
// UnmarshalBinary creates a pakage from binary data
257+
// UnmarshalBinary creates a package from binary data
258258
func (pkg *Package) UnmarshalBinary(bin []byte) error {
259259
if err := (&pkg.chunkHeader).UnmarshalBinary(bin); err != nil {
260260
return err

cmd/fyne/internal/templates/data/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# If PREFIX isn't provided, we check for $(DESTDIR)/usr/local and use that if it exists.
2-
# Otherwice we fall back to using /usr.
2+
# Otherwise we fall back to using /usr.
33

44
LOCAL != test -d $(DESTDIR)/usr/local && echo -n "/local" || echo -n ""
55
LOCAL ?= $(shell test -d $(DESTDIR)/usr/local && echo "/local" || echo "")

cmd/fyne/internal/templates/data/webgl-debug.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,7 @@ function makeLostContextSimulatingCanvas(canvas) {
752752

753753
canvas.loseContextInNCalls = function(numCalls) {
754754
if (contextLost_) {
755-
throw "You can not ask a lost contet to be lost";
755+
throw "You can not ask a lost content to be lost";
756756
}
757757
numCallsToLoseContext_ = numCalls_ + numCalls;
758758
};

cmd/fyne/internal/util/file.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func EnsureSubDir(parent, name string) string {
3333
if _, err := os.Stat(path); os.IsNotExist(err) {
3434
err := os.Mkdir(path, os.ModePerm)
3535
if err != nil {
36-
fyne.LogError("Failed to create dirrectory", err)
36+
fyne.LogError("Failed to create directory", err)
3737
}
3838
}
3939
return path

cmd/fyne_demo/tutorials/dialog.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func colorPicked(c color.Color, w fyne.Window) {
2626
rectangle := canvas.NewRectangle(c)
2727
size := 2 * theme.IconInlineSize()
2828
rectangle.SetMinSize(fyne.NewSize(size, size))
29-
dialog.ShowCustom("Color Picked", "Ok", rectangle, w)
29+
dialog.ShowCustom("Color Picked", "OK", rectangle, w)
3030
}
3131

3232
// dialogScreen loads demos of the dialogs we support

container/apptabs.go

-2
Original file line numberDiff line numberDiff line change
@@ -161,13 +161,11 @@ func (t *AppTabs) RemoveIndex(index int) {
161161
// Select sets the specified TabItem to be selected and its content visible.
162162
func (t *AppTabs) Select(item *TabItem) {
163163
selectItem(t, item)
164-
t.Refresh()
165164
}
166165

167166
// SelectIndex sets the TabItem at the specific index to be selected and its content visible.
168167
func (t *AppTabs) SelectIndex(index int) {
169168
selectIndex(t, index)
170-
t.Refresh()
171169
}
172170

173171
// SelectTab sets the specified TabItem to be selected and its content visible.

container/apptabs_test.go

+14
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,20 @@ func TestAppTabs_Select(t *testing.T) {
9797
assert.Equal(t, tab2, tabs.Selected())
9898
}
9999

100+
func TestAppTabs_SelectFocus(t *testing.T) {
101+
tab1 := &TabItem{Text: "Test1", Content: widget.NewEntry()}
102+
tab2 := &TabItem{Text: "Test2", Content: widget.NewEntry()}
103+
tabs := NewAppTabs(tab1, tab2)
104+
w := test.NewTempWindow(t, tabs)
105+
106+
tabs.OnSelected = func(t *TabItem) {
107+
w.Canvas().Focus(t.Content.(*widget.Entry))
108+
}
109+
110+
tabs.Select(tab2)
111+
assert.Equal(t, tab2.Content, w.Canvas().Focused())
112+
}
113+
100114
func TestAppTabs_SelectIndex(t *testing.T) {
101115
tabs := NewAppTabs(&TabItem{Text: "Test1", Content: widget.NewLabel("Test1")},
102116
&TabItem{Text: "Test2", Content: widget.NewLabel("Test2")})

container/doctabs.go

-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ func (t *DocTabs) Select(item *TabItem) {
152152
// SelectIndex sets the TabItem at the specific index to be selected and its content visible.
153153
func (t *DocTabs) SelectIndex(index int) {
154154
selectIndex(t, index)
155-
t.Refresh()
156155
}
157156

158157
// Selected returns the currently selected TabItem.

container/tabs.go

+1
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ func selectIndex(t baseTabs, index int) {
195195

196196
t.setTransitioning(true)
197197
t.setSelected(index)
198+
t.Refresh()
198199

199200
if f := t.onSelected(); f != nil {
200201
// Notification of selected

dialog/base.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ func (d *dialog) setButtons(buttons fyne.CanvasObject) {
140140
d.win.Refresh()
141141
}
142142

143-
// The method .create() needs to be called before the dialog cna be shown.
143+
// The method .create() needs to be called before the dialog can be shown.
144144
func newDialog(title, message string, icon fyne.Resource, callback func(bool), parent fyne.Window) *dialog {
145145
d := &dialog{content: newCenterWrappedLabel(message), title: title, icon: icon, parent: parent}
146146
d.callback = callback

dialog/confirm_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func TestConfirmDialog_Resize(t *testing.T) {
9393
expectedWidth = 600 //since win width only 600
9494
assert.Equal(t, expectedWidth, theDialog.win.Size().Width) //max, also work
9595
assert.Equal(t, expectedWidth, theDialog.win.Content.Size().Width+theme.Padding()*2)
96-
expectedHeight = 400 //since win heigh only 400
96+
expectedHeight = 400 //since win height only 400
9797
assert.Equal(t, expectedHeight, theDialog.win.Size().Height) //max, also work
9898
assert.Equal(t, expectedHeight, theDialog.win.Content.Size().Height+theme.Padding()*2)
9999

dialog/custom.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,15 @@ func NewCustomWithoutButtons(title string, content fyne.CanvasObject, parent fyn
4747
}
4848

4949
// SetButtons sets the row of buttons at the bottom of the dialog.
50-
// Passing an empy slice will result in a dialog with no buttons.
50+
// Passing an empty slice will result in a dialog with no buttons.
5151
//
5252
// Since: 2.4
5353
func (d *CustomDialog) SetButtons(buttons []fyne.CanvasObject) {
5454
d.dismiss = nil // New button row invalidates possible dismiss button.
5555
d.setButtons(container.NewGridWithRows(1, buttons...))
5656
}
5757

58-
// ShowCustomWithoutButtons shows a dialog, wihout buttons, over the specified application
58+
// ShowCustomWithoutButtons shows a dialog, without buttons, over the specified application
5959
// using custom content.
6060
// The MinSize() of the CanvasObject passed will be used to set the size of the window.
6161
//

dialog/entry.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func (i *EntryDialog) SetOnClosed(callback func()) {
5050
func NewEntryDialog(title, message string, onConfirm func(string), parent fyne.Window) *EntryDialog {
5151
i := &EntryDialog{entry: widget.NewEntry()}
5252
items := []*widget.FormItem{widget.NewFormItem(message, i.entry)}
53-
i.FormDialog = NewForm(title, lang.L("Ok"), lang.L("Cancel"), items, func(ok bool) {
53+
i.FormDialog = NewForm(title, lang.L("OK"), lang.L("Cancel"), items, func(ok bool) {
5454
// User has confirmed and entered an input
5555
if ok && onConfirm != nil {
5656
onConfirm(i.entry.Text)

dialog/file.go

+17
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
"sync"
1112

@@ -417,6 +418,18 @@ func (f *fileDialog) refreshDir(dir fyne.ListableURI) {
417418
}
418419
}
419420

421+
toSort := icons
422+
if parent != nil {
423+
toSort = icons[1:]
424+
}
425+
sort.Slice(toSort, func(i, j int) bool {
426+
if parent != nil { // avoiding the parent in [0]
427+
i++
428+
j++
429+
}
430+
431+
return strings.ToLower(icons[i].Name()) < strings.ToLower(icons[j].Name())
432+
})
420433
f.dataLock.Lock()
421434
f.data = icons
422435
f.dataLock.Unlock()
@@ -911,6 +924,10 @@ func getFavoriteOrder() []string {
911924
}
912925

913926
func hasAppFiles(a fyne.App) bool {
927+
if a.UniqueID() == "testApp" {
928+
return false
929+
}
930+
914931
return len(a.Storage().List()) > 0
915932
}
916933

dialog/file_test.go

+50-7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"testing"
99

1010
intWidget "fyne.io/fyne/v2/internal/widget"
11+
"fyne.io/fyne/v2/lang"
1112
"github.com/stretchr/testify/assert"
1213

1314
"fyne.io/fyne/v2"
@@ -97,7 +98,7 @@ func TestEffectiveStartingDir(t *testing.T) {
9798
// make sure we fail over if the specified directory does not exist
9899
dialog.startingLocation, err = storage.ListerForURI(storage.NewFileURI("/some/file/that/does/not/exist"))
99100
if err == nil {
100-
t.Errorf("Should have failed to create lister for nonexistant file")
101+
t.Errorf("Should have failed to create lister for nonexistent file")
101102
}
102103
res = dialog.effectiveStartingDir()
103104
expect = home
@@ -146,7 +147,7 @@ func TestFileDialogResize(t *testing.T) {
146147
expectedWidth = 600 //since win width only 600
147148
assert.Equal(t, expectedWidth, file.dialog.win.Size().Width) //max, also work
148149
assert.Equal(t, expectedWidth, file.dialog.win.Content.Size().Width+theme.Padding()*2)
149-
expectedHeight = 400 //since win heigh only 400
150+
expectedHeight = 400 //since win height only 400
150151
assert.Equal(t, expectedHeight, file.dialog.win.Size().Height) //max, also work
151152
assert.Equal(t, expectedHeight, file.dialog.win.Content.Size().Height+theme.Padding()*2)
152153

@@ -183,7 +184,7 @@ func TestShowFileOpen(t *testing.T) {
183184
ui := popup.Content.(*fyne.Container)
184185
//header
185186
title := ui.Objects[1].(*fyne.Container).Objects[1].(*widget.Label)
186-
assert.Equal(t, "Open File", title.Text)
187+
assert.Equal(t, lang.L("Open")+" "+lang.L("File"), title.Text)
187188
//optionsbuttons
188189
createNewFolderButton := ui.Objects[1].(*fyne.Container).Objects[0].(*fyne.Container).Objects[0].(*widget.Button)
189190
assert.Equal(t, "", createNewFolderButton.Text)
@@ -219,7 +220,7 @@ func TestShowFileOpen(t *testing.T) {
219220
assert.Greater(t, len(objects), 0)
220221

221222
fileName := test.TempWidgetRenderer(t, objects[0].(fyne.Widget)).Objects()[1].(*fileDialogItem).name
222-
assert.Equal(t, "(Parent)", fileName)
223+
assert.Equal(t, lang.L("(Parent)"), fileName)
223224
assert.True(t, open.Disabled())
224225

225226
var target *fileDialogItem
@@ -255,7 +256,7 @@ func TestHiddenFiles(t *testing.T) {
255256
t.Error("Failed to open testdata dir", err)
256257
}
257258

258-
// git does not preserve windows hidden flag so we have to set it.
259+
// git does not preserve windows hidden flag, so we have to set it.
259260
// just an empty function for non windows builds
260261
if err := hideFile(filepath.Join(testDataPath, ".hidden")); err != nil {
261262
t.Error("Failed to hide .hidden", err)
@@ -305,7 +306,7 @@ func TestHiddenFiles(t *testing.T) {
305306
target = item
306307
}
307308
}
308-
assert.NotNil(t, target, "Failed,.hidden not found in testdata")
309+
assert.NotNil(t, target, "Failed, .hidden not found in testdata")
309310
}
310311

311312
func TestShowFileSave(t *testing.T) {
@@ -335,7 +336,7 @@ func TestShowFileSave(t *testing.T) {
335336
assert.Greater(t, len(objects), 0)
336337

337338
item := test.TempWidgetRenderer(t, objects[0].(fyne.Widget)).Objects()[1].(*fileDialogItem)
338-
assert.Equal(t, "(Parent)", item.name)
339+
assert.Equal(t, lang.L("(Parent)"), item.name)
339340
assert.True(t, save.Disabled())
340341

341342
abs, _ := filepath.Abs("./testdata/")
@@ -450,6 +451,47 @@ func TestFileFilters(t *testing.T) {
450451
assert.Equal(t, 9, count)
451452
}
452453

454+
func TestFileSort(t *testing.T) {
455+
testDataPath, _ := filepath.Abs("testdata")
456+
testData := storage.NewFileURI(testDataPath)
457+
dir, err := storage.ListerForURI(testData)
458+
if err != nil {
459+
t.Error("Failed to open testdata dir", err)
460+
}
461+
462+
win := test.NewTempWindow(t, widget.NewLabel("Content"))
463+
d := NewFileOpen(func(file fyne.URIReadCloser, err error) {
464+
}, win)
465+
d.SetLocation(dir)
466+
d.Show()
467+
468+
popup := win.Canvas().Overlays().Top().(*widget.PopUp)
469+
defer win.Canvas().Overlays().Remove(popup)
470+
assert.NotNil(t, popup)
471+
472+
ui := popup.Content.(*fyne.Container)
473+
474+
files := ui.Objects[0].(*container.Split).Trailing.(*fyne.Container).Objects[1].(*container.Scroll).Content.(*fyne.Container).Objects[0].(*widget.GridWrap)
475+
objects := test.TempWidgetRenderer(t, files).Objects()[0].(*container.Scroll).Content.(*fyne.Container).Objects
476+
assert.NotEmpty(t, objects)
477+
478+
binPos := -1
479+
capitalPos := -1
480+
for i, icon := range objects {
481+
item := test.TempWidgetRenderer(t, icon.(fyne.Widget)).Objects()[1].(*fileDialogItem)
482+
switch item.name {
483+
case "bin":
484+
binPos = i
485+
case "Capitalised":
486+
capitalPos = i
487+
}
488+
}
489+
490+
assert.NotEqual(t, -1, binPos, "bin file not found")
491+
assert.NotEqual(t, -1, capitalPos, "Capitalised.txt file not found")
492+
assert.Less(t, binPos, capitalPos)
493+
}
494+
453495
func TestView(t *testing.T) {
454496
win := test.NewTempWindow(t, widget.NewLabel("Content"))
455497

@@ -633,6 +675,7 @@ func TestViewPreferences(t *testing.T) {
633675
}
634676

635677
func TestFileFavorites(t *testing.T) {
678+
_ = test.NewApp()
636679
win := test.NewTempWindow(t, widget.NewLabel("Content"))
637680

638681
dlg := NewFileOpen(func(reader fyne.URIReadCloser, err error) {

0 commit comments

Comments
 (0)