Skip to content

Commit

Permalink
Merge branch 'release/v2.5.x'
Browse files Browse the repository at this point in the history
  • Loading branch information
andydotxyz committed Feb 1, 2025
2 parents 4a875d9 + 0574526 commit 2360626
Show file tree
Hide file tree
Showing 72 changed files with 482 additions and 162 deletions.
22 changes: 21 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@
This file lists the main changes with each version of the Fyne toolkit.
More detailed release notes can be found on the [releases page](https://github.com/fyne-io/fyne/releases).

## 2.5.4 - 1 February 2025

### Changed

* Added Tamil translation

### Fixed

* Checkbox not responding to click because it is too "large"? (#5331)
* Fix progressbar not showing label until first refresh
* FyneApp.toml causes BadLength error (#5272)
* Test suite: failure with locale/language different from 'en' (#5362)
* fix GridWrap crash when resizing to same size without creating renderer
* Submenus not working on mobile (#5398)
* Subtle scrolling bug in List when the last two items are of different size (#5281)
* File picker does not ignore case (#5113)
* Tab "OnSelected" doesn't appear to allow focussing tab content (#5454)
* Documentation fixes


## 2.5.3 - 15 December 2024

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

Expand Down
2 changes: 2 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ type App interface {
Lifecycle() Lifecycle

// Metadata returns the application metadata that was set at compile time.
// The items of metadata are available after "fyne package" or when running "go run"
// Building with "go build" may cause this to be unavailable.
//
// Since: 2.2
Metadata() AppMetadata
Expand Down
2 changes: 1 addition & 1 deletion app/meta_development.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func checkLocalMetadata() {
if data.Details.Icon != "" {
res, err := fyne.LoadResourceFromPath(data.Details.Icon)
if err == nil {
meta.Icon = res
meta.Icon = metadata.ScaleIcon(res, 512)
}
}

Expand Down
6 changes: 5 additions & 1 deletion cmd/fyne/internal/commands/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,10 +340,12 @@ func createMetadataInitFile(srcdir string, app *appData) (func(), error) {
if app.icon != "" {
res, err := fyne.LoadResourceFromPath(app.icon)
if err != nil {
fyne.LogError("Unable to load medadata icon file "+app.icon, err)
fyne.LogError("Unable to load metadata icon file "+app.icon, err)
return func() { os.Remove(metadataInitFilePath) }, err
}

res = metadata.ScaleIcon(res, 512)

// The return type of fyne.LoadResourceFromPath is always a *fyne.StaticResource.
app.ResGoString = res.(*fyne.StaticResource).GoString()
}
Expand Down Expand Up @@ -442,6 +444,8 @@ func normaliseVersion(str string) string {

if pos := strings.Index(str, "-0.20"); pos != -1 {
str = str[:pos] + "-dev"
} else if pos = strings.Index(str, "-rc"); pos != -1 {
str = str[:pos] + "-dev"
}
return version.Normalize(str)
}
1 change: 1 addition & 0 deletions cmd/fyne/internal/commands/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,5 @@ func Test_NormaliseVersion(t *testing.T) {
assert.Equal(t, "2.3.0.0", normaliseVersion("v2.3"))
assert.Equal(t, "2.4.0.0", normaliseVersion("v2.4.0"))
assert.Equal(t, "2.3.6.0-dev", normaliseVersion("v2.3.6-0.20230711180435-d4b95e1cb1eb"))
assert.Equal(t, "2.4.1.0-dev", normaliseVersion("v2.4.1-rc7.0.20230711180435-d4b95e1cb1eb"))
}
2 changes: 1 addition & 1 deletion cmd/fyne/internal/mobile/binres/binres.go
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,7 @@ func poolTrim(s string) string {
}

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

Expand Down
2 changes: 1 addition & 1 deletion cmd/fyne/internal/mobile/binres/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ type Package struct {
specs []*TypeSpec
}

// UnmarshalBinary creates a pakage from binary data
// UnmarshalBinary creates a package from binary data
func (pkg *Package) UnmarshalBinary(bin []byte) error {
if err := (&pkg.chunkHeader).UnmarshalBinary(bin); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion cmd/fyne/internal/templates/data/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# If PREFIX isn't provided, we check for $(DESTDIR)/usr/local and use that if it exists.
# Otherwice we fall back to using /usr.
# Otherwise we fall back to using /usr.

LOCAL != test -d $(DESTDIR)/usr/local && echo -n "/local" || echo -n ""
LOCAL ?= $(shell test -d $(DESTDIR)/usr/local && echo "/local" || echo "")
Expand Down
2 changes: 1 addition & 1 deletion cmd/fyne/internal/templates/data/webgl-debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ function makeLostContextSimulatingCanvas(canvas) {

canvas.loseContextInNCalls = function(numCalls) {
if (contextLost_) {
throw "You can not ask a lost contet to be lost";
throw "You can not ask a lost content to be lost";
}
numCallsToLoseContext_ = numCalls_ + numCalls;
};
Expand Down
2 changes: 1 addition & 1 deletion cmd/fyne/internal/util/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func EnsureSubDir(parent, name string) string {
if _, err := os.Stat(path); os.IsNotExist(err) {
err := os.Mkdir(path, os.ModePerm)
if err != nil {
fyne.LogError("Failed to create dirrectory", err)
fyne.LogError("Failed to create directory", err)
}
}
return path
Expand Down
2 changes: 1 addition & 1 deletion cmd/fyne_demo/tutorials/dialog.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func colorPicked(c color.Color, w fyne.Window) {
rectangle := canvas.NewRectangle(c)
size := 2 * theme.IconInlineSize()
rectangle.SetMinSize(fyne.NewSize(size, size))
dialog.ShowCustom("Color Picked", "Ok", rectangle, w)
dialog.ShowCustom("Color Picked", "OK", rectangle, w)
}

// dialogScreen loads demos of the dialogs we support
Expand Down
2 changes: 0 additions & 2 deletions container/apptabs.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,11 @@ func (t *AppTabs) RemoveIndex(index int) {
// Select sets the specified TabItem to be selected and its content visible.
func (t *AppTabs) Select(item *TabItem) {
selectItem(t, item)
t.Refresh()
}

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

// SelectTab sets the specified TabItem to be selected and its content visible.
Expand Down
14 changes: 14 additions & 0 deletions container/apptabs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,20 @@ func TestAppTabs_Select(t *testing.T) {
assert.Equal(t, tab2, tabs.Selected())
}

func TestAppTabs_SelectFocus(t *testing.T) {
tab1 := &TabItem{Text: "Test1", Content: widget.NewEntry()}
tab2 := &TabItem{Text: "Test2", Content: widget.NewEntry()}
tabs := NewAppTabs(tab1, tab2)
w := test.NewTempWindow(t, tabs)

tabs.OnSelected = func(t *TabItem) {
w.Canvas().Focus(t.Content.(*widget.Entry))
}

tabs.Select(tab2)
assert.Equal(t, tab2.Content, w.Canvas().Focused())
}

func TestAppTabs_SelectIndex(t *testing.T) {
tabs := NewAppTabs(&TabItem{Text: "Test1", Content: widget.NewLabel("Test1")},
&TabItem{Text: "Test2", Content: widget.NewLabel("Test2")})
Expand Down
1 change: 0 additions & 1 deletion container/doctabs.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ func (t *DocTabs) Select(item *TabItem) {
// SelectIndex sets the TabItem at the specific index to be selected and its content visible.
func (t *DocTabs) SelectIndex(index int) {
selectIndex(t, index)
t.Refresh()
}

// Selected returns the currently selected TabItem.
Expand Down
1 change: 1 addition & 0 deletions container/tabs.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ func selectIndex(t baseTabs, index int) {

t.setTransitioning(true)
t.setSelected(index)
t.Refresh()

if f := t.onSelected(); f != nil {
// Notification of selected
Expand Down
2 changes: 1 addition & 1 deletion dialog/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func (d *dialog) setButtons(buttons fyne.CanvasObject) {
d.win.Refresh()
}

// The method .create() needs to be called before the dialog cna be shown.
// The method .create() needs to be called before the dialog can be shown.
func newDialog(title, message string, icon fyne.Resource, callback func(bool), parent fyne.Window) *dialog {
d := &dialog{content: newCenterWrappedLabel(message), title: title, icon: icon, parent: parent}
d.callback = callback
Expand Down
2 changes: 1 addition & 1 deletion dialog/confirm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func TestConfirmDialog_Resize(t *testing.T) {
expectedWidth = 600 //since win width only 600
assert.Equal(t, expectedWidth, theDialog.win.Size().Width) //max, also work
assert.Equal(t, expectedWidth, theDialog.win.Content.Size().Width+theme.Padding()*2)
expectedHeight = 400 //since win heigh only 400
expectedHeight = 400 //since win height only 400
assert.Equal(t, expectedHeight, theDialog.win.Size().Height) //max, also work
assert.Equal(t, expectedHeight, theDialog.win.Content.Size().Height+theme.Padding()*2)

Expand Down
4 changes: 2 additions & 2 deletions dialog/custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ func NewCustomWithoutButtons(title string, content fyne.CanvasObject, parent fyn
}

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

// ShowCustomWithoutButtons shows a dialog, wihout buttons, over the specified application
// ShowCustomWithoutButtons shows a dialog, without buttons, over the specified application
// using custom content.
// The MinSize() of the CanvasObject passed will be used to set the size of the window.
//
Expand Down
2 changes: 1 addition & 1 deletion dialog/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (i *EntryDialog) SetOnClosed(callback func()) {
func NewEntryDialog(title, message string, onConfirm func(string), parent fyne.Window) *EntryDialog {
i := &EntryDialog{entry: widget.NewEntry()}
items := []*widget.FormItem{widget.NewFormItem(message, i.entry)}
i.FormDialog = NewForm(title, lang.L("Ok"), lang.L("Cancel"), items, func(ok bool) {
i.FormDialog = NewForm(title, lang.L("OK"), lang.L("Cancel"), items, func(ok bool) {
// User has confirmed and entered an input
if ok && onConfirm != nil {
onConfirm(i.entry.Text)
Expand Down
17 changes: 17 additions & 0 deletions dialog/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"sync"

Expand Down Expand Up @@ -417,6 +418,18 @@ func (f *fileDialog) refreshDir(dir fyne.ListableURI) {
}
}

toSort := icons
if parent != nil {
toSort = icons[1:]
}
sort.Slice(toSort, func(i, j int) bool {
if parent != nil { // avoiding the parent in [0]
i++
j++
}

return strings.ToLower(icons[i].Name()) < strings.ToLower(icons[j].Name())
})
f.dataLock.Lock()
f.data = icons
f.dataLock.Unlock()
Expand Down Expand Up @@ -911,6 +924,10 @@ func getFavoriteOrder() []string {
}

func hasAppFiles(a fyne.App) bool {
if a.UniqueID() == "testApp" {
return false
}

return len(a.Storage().List()) > 0
}

Expand Down
57 changes: 50 additions & 7 deletions dialog/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"testing"

intWidget "fyne.io/fyne/v2/internal/widget"
"fyne.io/fyne/v2/lang"
"github.com/stretchr/testify/assert"

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

Expand Down Expand Up @@ -183,7 +184,7 @@ func TestShowFileOpen(t *testing.T) {
ui := popup.Content.(*fyne.Container)
//header
title := ui.Objects[1].(*fyne.Container).Objects[1].(*widget.Label)
assert.Equal(t, "Open File", title.Text)
assert.Equal(t, lang.L("Open")+" "+lang.L("File"), title.Text)
//optionsbuttons
createNewFolderButton := ui.Objects[1].(*fyne.Container).Objects[0].(*fyne.Container).Objects[0].(*widget.Button)
assert.Equal(t, "", createNewFolderButton.Text)
Expand Down Expand Up @@ -219,7 +220,7 @@ func TestShowFileOpen(t *testing.T) {
assert.Greater(t, len(objects), 0)

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

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

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

func TestShowFileSave(t *testing.T) {
Expand Down Expand Up @@ -335,7 +336,7 @@ func TestShowFileSave(t *testing.T) {
assert.Greater(t, len(objects), 0)

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

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

func TestFileSort(t *testing.T) {
testDataPath, _ := filepath.Abs("testdata")
testData := storage.NewFileURI(testDataPath)
dir, err := storage.ListerForURI(testData)
if err != nil {
t.Error("Failed to open testdata dir", err)
}

win := test.NewTempWindow(t, widget.NewLabel("Content"))
d := NewFileOpen(func(file fyne.URIReadCloser, err error) {
}, win)
d.SetLocation(dir)
d.Show()

popup := win.Canvas().Overlays().Top().(*widget.PopUp)
defer win.Canvas().Overlays().Remove(popup)
assert.NotNil(t, popup)

ui := popup.Content.(*fyne.Container)

files := ui.Objects[0].(*container.Split).Trailing.(*fyne.Container).Objects[1].(*container.Scroll).Content.(*fyne.Container).Objects[0].(*widget.GridWrap)
objects := test.TempWidgetRenderer(t, files).Objects()[0].(*container.Scroll).Content.(*fyne.Container).Objects
assert.NotEmpty(t, objects)

binPos := -1
capitalPos := -1
for i, icon := range objects {
item := test.TempWidgetRenderer(t, icon.(fyne.Widget)).Objects()[1].(*fileDialogItem)
switch item.name {
case "bin":
binPos = i
case "Capitalised":
capitalPos = i
}
}

assert.NotEqual(t, -1, binPos, "bin file not found")
assert.NotEqual(t, -1, capitalPos, "Capitalised.txt file not found")
assert.Less(t, binPos, capitalPos)
}

func TestView(t *testing.T) {
win := test.NewTempWindow(t, widget.NewLabel("Content"))

Expand Down Expand Up @@ -633,6 +675,7 @@ func TestViewPreferences(t *testing.T) {
}

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

dlg := NewFileOpen(func(reader fyne.URIReadCloser, err error) {
Expand Down
Loading

0 comments on commit 2360626

Please sign in to comment.