From 8777e7fafb5254f843c13e58d56cc139ee4e8497 Mon Sep 17 00:00:00 2001 From: Bruce Thompson Date: Fri, 18 Oct 2024 05:17:00 -0600 Subject: [PATCH 01/74] Fix for https://github.com/fyne-io/fyne/issues/5200 --- dialog/file.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dialog/file.go b/dialog/file.go index d858143d2a..b0441067c5 100644 --- a/dialog/file.go +++ b/dialog/file.go @@ -616,7 +616,7 @@ func (f *FileDialog) effectiveStartingDir() fyne.ListableURI { return f.startingLocation } } - + return f.startingLocation } // Try app storage From 37121a3a99079440f353d41331e05cf6d08d55da Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 26 Nov 2024 11:32:41 +0000 Subject: [PATCH 02/74] Use bugfix in glfw-js --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a9c5a089c9..1643bf56d9 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/fredbi/uri v1.1.0 github.com/fsnotify/fsnotify v1.7.0 github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe - github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a + github.com/fyne-io/glfw-js v0.0.0-20241126112943-313d8a0fe1d0 github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a diff --git a/go.sum b/go.sum index 617cc24c83..3aa61ab33e 100644 --- a/go.sum +++ b/go.sum @@ -85,8 +85,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe h1:A/wiwvQ0CAjPkuJytaD+SsXkPU0asQ+guQEIg1BJGX4= github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe/go.mod h1:d4clgH0/GrRwWjRzJJQXxT/h1TyuNSfF/X64zb/3Ggg= -github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a h1:ybgRdYvAHTn93HW79bLiBiJwVL4jVeyGQRZMgImoeWs= -github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a/go.mod h1:gsGA2dotD4v0SR6PmPCYvS9JuOeMwAtmfvDE7mbYXMY= +github.com/fyne-io/glfw-js v0.0.0-20241126112943-313d8a0fe1d0 h1:/1YRWFv9bAWkoo3SuxpFfzpXH0D/bQnTjNXyF4ih7Os= +github.com/fyne-io/glfw-js v0.0.0-20241126112943-313d8a0fe1d0/go.mod h1:gsGA2dotD4v0SR6PmPCYvS9JuOeMwAtmfvDE7mbYXMY= github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 h1:hnLq+55b7Zh7/2IRzWCpiTcAvjv/P8ERF+N7+xXbZhk= github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2/go.mod h1:eO7W361vmlPOrykIg+Rsh1SZ3tQBaOsfzZhsIOb/Lm0= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= From 51017d6716845ba40335a3b8f8da73e4aee3c1f4 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 15 Oct 2024 15:46:41 +0100 Subject: [PATCH 03/74] Add optional Source metadata to the FyneApp.toml Use this to write an extension of the .desktop file for Linux. I am not sure if we can extend other package formats with this information also? --- cmd/fyne/internal/commands/package-unix.go | 8 +++++++ .../internal/commands/package-unix_test.go | 23 +++++++++++++++++++ cmd/fyne/internal/commands/package.go | 3 +++ cmd/fyne/internal/templates/data/app.desktop | 8 ++++++- cmd/fyne_demo/FyneApp.toml | 4 ++++ internal/metadata/data.go | 5 ++++ internal/metadata/load_test.go | 4 ++++ internal/metadata/testdata/FyneApp.toml | 4 ++++ 8 files changed, 58 insertions(+), 1 deletion(-) diff --git a/cmd/fyne/internal/commands/package-unix.go b/cmd/fyne/internal/commands/package-unix.go index 8e10d5790e..1aede64e70 100644 --- a/cmd/fyne/internal/commands/package-unix.go +++ b/cmd/fyne/internal/commands/package-unix.go @@ -21,6 +21,8 @@ type unixData struct { Comment string Keywords string ExecParams string + + SourceRepo, SourceDir string } func (p *Packager) packageUNIX() error { @@ -74,6 +76,12 @@ func (p *Packager) packageUNIX() error { Categories: formatDesktopFileList(linuxBSD.Categories), ExecParams: linuxBSD.ExecParams, } + + if p.sourceMetadata != nil { + tplData.SourceRepo = p.sourceMetadata.Repo + tplData.SourceDir = p.sourceMetadata.Dir + } + err = templates.DesktopFileUNIX.Execute(deskFile, tplData) if err != nil { return fmt.Errorf("failed to write desktop entry string: %w", err) diff --git a/cmd/fyne/internal/commands/package-unix_test.go b/cmd/fyne/internal/commands/package-unix_test.go index 8b78fa5bb9..67800dd54d 100644 --- a/cmd/fyne/internal/commands/package-unix_test.go +++ b/cmd/fyne/internal/commands/package-unix_test.go @@ -1,8 +1,11 @@ package commands import ( + "bytes" + "strings" "testing" + "fyne.io/fyne/v2/cmd/fyne/internal/templates" "github.com/stretchr/testify/assert" ) @@ -11,3 +14,23 @@ func TestFormatDesktopFileList(t *testing.T) { assert.Equal(t, "One;", formatDesktopFileList([]string{"One"})) assert.Equal(t, "One;Two;", formatDesktopFileList([]string{"One", "Two"})) } + +func TestDesktopFileSource(t *testing.T) { + tplData := unixData{ + Name: "Testing", + } + buf := &bytes.Buffer{} + + err := templates.DesktopFileUNIX.Execute(buf, tplData) + assert.Nil(t, err) + assert.False(t, strings.Contains(buf.String(), "[X-Fyne")) + + tplData.SourceRepo = "https://example.com" + tplData.SourceDir = "cmd/name" + + err = templates.DesktopFileUNIX.Execute(buf, tplData) + assert.Nil(t, err) + assert.True(t, strings.Contains(buf.String(), "[X-Fyne")) + assert.True(t, strings.Contains(buf.String(), "Repo=https://example.com")) + assert.True(t, strings.Contains(buf.String(), "Dir=cmd/name")) +} diff --git a/cmd/fyne/internal/commands/package.go b/cmd/fyne/internal/commands/package.go index 552755b6f3..367bd02e7e 100644 --- a/cmd/fyne/internal/commands/package.go +++ b/cmd/fyne/internal/commands/package.go @@ -137,6 +137,7 @@ type Packager struct { customMetadata keyValueFlag linuxAndBSDMetadata *metadata.LinuxAndBSD + sourceMetadata *metadata.AppSource } // NewPackager returns a command that can handle the packaging a GUI apps built using Fyne from local source code. @@ -342,6 +343,8 @@ func (p *Packager) validate() (err error) { } p.appData.mergeMetadata(data) + p.sourceMetadata = data.Source + p.linuxAndBSDMetadata = data.LinuxAndBSD } diff --git a/cmd/fyne/internal/templates/data/app.desktop b/cmd/fyne/internal/templates/data/app.desktop index a8ae72d8a7..cfba84c65b 100644 --- a/cmd/fyne/internal/templates/data/app.desktop +++ b/cmd/fyne/internal/templates/data/app.desktop @@ -9,4 +9,10 @@ Icon={{.Name}} Comment={{.Comment}}{{end}} {{- if ne .Categories ""}} Categories={{.Categories}}{{end}} -{{if ne .Keywords ""}}Keywords={{.Keywords}}{{else}}Keywords=fyne;{{end}} \ No newline at end of file +{{- if ne .Keywords ""}}Keywords={{.Keywords}}{{else}}Keywords=fyne;{{end}} + +{{- if or (ne .SourceRepo "") (ne .SourceDir "")}} +[X-Fyne Source] +Repo={{.SourceRepo}} +Dir={{.SourceDir}} +{{end}} \ No newline at end of file diff --git a/cmd/fyne_demo/FyneApp.toml b/cmd/fyne_demo/FyneApp.toml index eb3cd500c0..672030555b 100644 --- a/cmd/fyne_demo/FyneApp.toml +++ b/cmd/fyne_demo/FyneApp.toml @@ -5,6 +5,10 @@ Version = "2.4.0" Build = 11 +[Source] + Repo = "https://github.com/fyne-io/fyne" + Dir = "cmd/fyne_demo" + [Development] HelperText = "This binary was built with debug symbols" diff --git a/internal/metadata/data.go b/internal/metadata/data.go index 6517c025f0..53f95b7e4b 100644 --- a/internal/metadata/data.go +++ b/internal/metadata/data.go @@ -6,6 +6,7 @@ type FyneApp struct { Details AppDetails Development map[string]string `toml:",omitempty"` Release map[string]string `toml:",omitempty"` + Source *AppSource `toml:",omitempty"` LinuxAndBSD *LinuxAndBSD `toml:",omitempty"` } @@ -17,6 +18,10 @@ type AppDetails struct { Build int `toml:",omitempty"` } +type AppSource struct { + Repo, Dir string `toml:",omitempty"` +} + // LinuxAndBSD describes specific metadata for desktop files on Linux and BSD. type LinuxAndBSD struct { GenericName string `toml:",omitempty"` diff --git a/internal/metadata/load_test.go b/internal/metadata/load_test.go index ce1fdb03e0..d3e8b6d10d 100644 --- a/internal/metadata/load_test.go +++ b/internal/metadata/load_test.go @@ -24,4 +24,8 @@ func TestLoadAppMetadata(t *testing.T) { assert.Equal(t, data.Development["Test"], "Value2") assert.Equal(t, data.Development["InDevelopmentOnly"], "Value4") assert.NotContains(t, data.Development, "InReleaseOnly") + + assert.NotNil(t, data.Source) + assert.Equal(t, data.Source.Repo, "https://github.com/fyne-io/fyne") + assert.Equal(t, data.Source.Dir, "internal/metadata/testdata") } diff --git a/internal/metadata/testdata/FyneApp.toml b/internal/metadata/testdata/FyneApp.toml index db23dbd72e..8c1833b37c 100644 --- a/internal/metadata/testdata/FyneApp.toml +++ b/internal/metadata/testdata/FyneApp.toml @@ -7,6 +7,10 @@ Icon = "https://conf.fyne.io/assets/img/fyne.png" Version = "1.0.0" Build = 1 +[Source] +Repo = "https://github.com/fyne-io/fyne" +Dir = "internal/metadata/testdata" + [Release] Test = "Value1" InReleaseOnly = "Value3" From 4f1c32a64f9352149f96e2cb8f0aa70c251a5949 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 15 Oct 2024 15:56:56 +0100 Subject: [PATCH 04/74] oops, missed bundle file --- cmd/fyne/internal/templates/bundled.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/fyne/internal/templates/bundled.go b/cmd/fyne/internal/templates/bundled.go index 023e0a317a..e2bed532b2 100644 --- a/cmd/fyne/internal/templates/bundled.go +++ b/cmd/fyne/internal/templates/bundled.go @@ -23,7 +23,7 @@ var resourceMakefile = &fyne.StaticResource{ var resourceAppDesktop = &fyne.StaticResource{ StaticName: "app.desktop", StaticContent: []byte( - "[Desktop Entry]\nType=Application\nName={{.Name}}\n{{- if ne .GenericName \"\"}}\nGenericName={{.GenericName}}{{end}}\nExec={{.Exec}} {{- .ExecParams}}\nIcon={{.Name}}\n{{- if ne .Comment \"\"}}\nComment={{.Comment}}{{end}}\n{{- if ne .Categories \"\"}}\nCategories={{.Categories}}{{end}}\n{{if ne .Keywords \"\"}}Keywords={{.Keywords}}{{else}}Keywords=fyne;{{end}}"), + "[Desktop Entry]\nType=Application\nName={{.Name}}\n{{- if ne .GenericName \"\"}}\nGenericName={{.GenericName}}{{end}}\nExec={{.Exec}} {{- .ExecParams}}\nIcon={{.Name}}\n{{- if ne .Comment \"\"}}\nComment={{.Comment}}{{end}}\n{{- if ne .Categories \"\"}}\nCategories={{.Categories}}{{end}}\n{{- if ne .Keywords \"\"}}Keywords={{.Keywords}}{{else}}Keywords=fyne;{{end}}\n\n{{- if or (ne .SourceRepo \"\") (ne .SourceDir \"\")}}\n[X-Fyne Source]\nRepo={{.SourceRepo}}\nDir={{.SourceDir}}\n{{end}}"), } var resourceAppManifest = &fyne.StaticResource{ StaticName: "app.manifest", From 830a0784aa912b218ff27260b3e9a42ff4c14d45 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sat, 12 Oct 2024 11:05:28 +0100 Subject: [PATCH 05/74] Move text cache index to the text details not object reference --- internal/cache/text.go | 15 +++++++++---- internal/cache/texture_common.go | 36 ++++++++++++++++++++++++++++++ internal/cache/texture_desktop.go | 4 +++- internal/cache/texture_gomobile.go | 4 +++- internal/cache/texture_goxjs.go | 6 +++-- internal/painter/gl/texture.go | 24 ++++++++++++++++++++ 6 files changed, 81 insertions(+), 8 deletions(-) diff --git a/internal/cache/text.go b/internal/cache/text.go index 0cbf754e9f..84381fafac 100644 --- a/internal/cache/text.go +++ b/internal/cache/text.go @@ -1,6 +1,7 @@ package cache import ( + "image/color" "sync" "time" @@ -19,10 +20,16 @@ type fontMetric struct { } type fontSizeEntry struct { - text string - size float32 - style fyne.TextStyle - custom string + Text string + Size float32 + Style fyne.TextStyle + Source string +} + +type FontCacheEntry struct { + fontSizeEntry + + Color color.Color } // GetFontMetrics looks up a calculated size and baseline required for the specified text parameters. diff --git a/internal/cache/texture_common.go b/internal/cache/texture_common.go index abfd54f644..40af0f48ad 100644 --- a/internal/cache/texture_common.go +++ b/internal/cache/texture_common.go @@ -13,8 +13,17 @@ func DeleteTexture(obj fyne.CanvasObject) { textures.Delete(obj) } +// GetTextTexture gets cached texture for a text run. +func GetTextTexture(ent FontCacheEntry) (TextureType, bool) { + return load(ent) +} + // GetTexture gets cached texture. func GetTexture(obj fyne.CanvasObject) (TextureType, bool) { + return load(obj) +} + +func load(obj any) (TextureType, bool) { t, ok := textures.Load(obj) if t == nil || !ok { return NoTexture, false @@ -31,6 +40,17 @@ func GetTexture(obj fyne.CanvasObject) (TextureType, bool) { func RangeExpiredTexturesFor(canvas fyne.Canvas, f func(fyne.CanvasObject)) { now := timeNow() textures.Range(func(key, value any) bool { + if _, ok := key.(FontCacheEntry); ok { + tinfo := value.(*textureInfo) + + // just free text directly when that string/style combo is done + if tinfo.isExpired(now) && tinfo.canvas == canvas { + textures.Delete(key) + tinfo.textFree() + } + + return true + } obj, tinfo := key.(fyne.CanvasObject), value.(*textureInfo) if tinfo.isExpired(now) && tinfo.canvas == canvas { f(obj) @@ -45,6 +65,10 @@ func RangeExpiredTexturesFor(canvas fyne.Canvas, f func(fyne.CanvasObject)) { // gl context to ensure textures are deleted from gl. func RangeTexturesFor(canvas fyne.Canvas, f func(fyne.CanvasObject)) { textures.Range(func(key, value any) bool { + if _, ok := key.(FontCacheEntry); ok { + return true // TODO what? + } + obj, tinfo := key.(fyne.CanvasObject), value.(*textureInfo) if tinfo.canvas == canvas { f(obj) @@ -53,9 +77,21 @@ func RangeTexturesFor(canvas fyne.Canvas, f func(fyne.CanvasObject)) { }) } +// SetTextTexture sets cached texture for a text run. +func SetTextTexture(ent FontCacheEntry, texture TextureType, canvas fyne.Canvas, free func()) { + store(ent, texture, canvas, free) +} + // SetTexture sets cached texture. func SetTexture(obj fyne.CanvasObject, texture TextureType, canvas fyne.Canvas) { + store(obj, texture, canvas, nil) +} + +func store(obj any, texture TextureType, canvas fyne.Canvas, free func()) { texInfo := &textureInfo{texture: texture} + if free != nil { + texInfo.textFree = free + } texInfo.canvas = canvas texInfo.setAlive() textures.Store(obj, texInfo) diff --git a/internal/cache/texture_desktop.go b/internal/cache/texture_desktop.go index 26062eb7e5..9627731d54 100644 --- a/internal/cache/texture_desktop.go +++ b/internal/cache/texture_desktop.go @@ -10,7 +10,9 @@ var NoTexture = TextureType(0) type textureInfo struct { textureCacheBase - texture TextureType + + texture TextureType + textFree func() } // IsValid will return true if the passed texture is potentially a texture diff --git a/internal/cache/texture_gomobile.go b/internal/cache/texture_gomobile.go index 4b0d89cfd7..14ea8cc74e 100644 --- a/internal/cache/texture_gomobile.go +++ b/internal/cache/texture_gomobile.go @@ -11,7 +11,9 @@ var NoTexture = gl.Texture{0} type textureInfo struct { textureCacheBase - texture TextureType + + texture TextureType + textFree func() } // IsValid will return true if the passed texture is potentially a texture diff --git a/internal/cache/texture_goxjs.go b/internal/cache/texture_goxjs.go index 17669ea596..246f9021b6 100644 --- a/internal/cache/texture_goxjs.go +++ b/internal/cache/texture_goxjs.go @@ -2,7 +2,7 @@ package cache -import gl "github.com/fyne-io/gl-js" +import "github.com/fyne-io/gl-js" // TextureType represents an uploaded GL texture type TextureType = gl.Texture @@ -11,7 +11,9 @@ var NoTexture = gl.NoTexture type textureInfo struct { textureCacheBase - texture TextureType + + texture TextureType + textFree func() } // IsValid will return true if the passed texture is potentially a texture diff --git a/internal/painter/gl/texture.go b/internal/painter/gl/texture.go index 207ee6cd12..79dedf704b 100644 --- a/internal/painter/gl/texture.go +++ b/internal/painter/gl/texture.go @@ -33,6 +33,30 @@ func (p *painter) freeTexture(obj fyne.CanvasObject) { } func (p *painter) getTexture(object fyne.CanvasObject, creator func(canvasObject fyne.CanvasObject) Texture) (Texture, error) { + if t, ok := object.(*canvas.Text); ok { + custom := "" + if t.FontSource != nil { + custom = t.FontSource.Name() + } + ent := cache.FontCacheEntry{Color: t.Color} + ent.Text = t.Text + ent.Size = t.TextSize + ent.Style = t.TextStyle + ent.Source = custom + + texture, ok := cache.GetTextTexture(ent) + + if !ok { + tex := creator(object) + texture = cache.TextureType(tex) + cache.SetTextTexture(ent, texture, p.canvas, func() { + p.ctx.DeleteTexture(tex) + }) + } + + return Texture(texture), nil + } + texture, ok := cache.GetTexture(object) if !ok { From c649326681741582eb3a41f0d39442e4e4fdc113 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sun, 13 Oct 2024 15:15:46 +0100 Subject: [PATCH 06/74] Fix todo with a doc update --- internal/cache/texture_common.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/cache/texture_common.go b/internal/cache/texture_common.go index 40af0f48ad..be0dce90b1 100644 --- a/internal/cache/texture_common.go +++ b/internal/cache/texture_common.go @@ -60,13 +60,14 @@ func RangeExpiredTexturesFor(canvas fyne.Canvas, f func(fyne.CanvasObject)) { } // RangeTexturesFor range over the textures for the specified canvas. +// It will not return the texture for a `canvas.Text` as their render lifecycle is handled separately. // // Note: If this is used to free textures, then it should be called inside a current // gl context to ensure textures are deleted from gl. func RangeTexturesFor(canvas fyne.Canvas, f func(fyne.CanvasObject)) { textures.Range(func(key, value any) bool { if _, ok := key.(FontCacheEntry); ok { - return true // TODO what? + return true // do nothing, text cache lives outside the scope of an object } obj, tinfo := key.(fyne.CanvasObject), value.(*textureInfo) From d5cbce122a3545de79bc8c65609b396159103d39 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 15 Oct 2024 16:39:55 +0100 Subject: [PATCH 07/74] Remove dead code --- app/preferences.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/preferences.go b/app/preferences.go index 138fd30eb4..24ef00e0b1 100644 --- a/app/preferences.go +++ b/app/preferences.go @@ -183,12 +183,7 @@ func convertLists(values map[string]any) { floats[i] = item.(float64) } values[k] = floats - case int: - ints := make([]int, len(items)) - for i, item := range items { - ints[i] = item.(int) - } - values[k] = ints + //case int: // json has no int! case string: strings := make([]string, len(items)) for i, item := range items { From 11b791a44c5fa85277156b6959ddbccbbd660d3a Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 15 Oct 2024 16:40:10 +0100 Subject: [PATCH 08/74] Check for empty slices and empty preference --- app/preferences_test.go | 14 ++++++++++++++ app/testdata/preferences.json | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/preferences_test.go b/app/preferences_test.go index f30df7021f..101d59e7bf 100644 --- a/app/preferences_test.go +++ b/app/preferences_test.go @@ -97,4 +97,18 @@ func TestPreferences_Load(t *testing.T) { assert.Equal(t, []float64{1.1, 2.2, 3.3}, p.FloatList("keyFloatList")) assert.Equal(t, true, p.Bool("keyBool")) assert.Equal(t, []bool{true, false, true}, p.BoolList("keyBoolList")) + assert.Equal(t, 0, len(p.StringList("keyEmptyList"))) +} + +func TestPreferences_EmptyLoad(t *testing.T) { + p := newPreferences(&fyneApp{uniqueID: ""}) + + count := 0 + p.ReadValues(func(v map[string]any) { + for range v { + count++ + } + }) + + assert.Zero(t, count) } diff --git a/app/testdata/preferences.json b/app/testdata/preferences.json index 8aa684b567..b98974770b 100644 --- a/app/testdata/preferences.json +++ b/app/testdata/preferences.json @@ -6,5 +6,6 @@ "keyFloat": 3.5, "keyFloatList": [1.1, 2.2, 3.3], "keyBool": true, - "keyBoolList": [true, false, true] + "keyBoolList": [true, false, true], + "keyEmptyList": [] } \ No newline at end of file From e47860f91089d6309f88fee835c439f7c712bc4d Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 15 Oct 2024 16:48:23 +0100 Subject: [PATCH 09/74] Add basic icon cache test --- app/icon_cache_file_test.go | 29 +++++++++++++++++++++++++++++ app/testdata/fyne.png | Bin 0 -> 19560 bytes 2 files changed, 29 insertions(+) create mode 100644 app/icon_cache_file_test.go create mode 100644 app/testdata/fyne.png diff --git a/app/icon_cache_file_test.go b/app/icon_cache_file_test.go new file mode 100644 index 0000000000..a285500cbd --- /dev/null +++ b/app/icon_cache_file_test.go @@ -0,0 +1,29 @@ +package app + +import ( + _ "embed" + "os" + "testing" + + "fyne.io/fyne/v2" + "github.com/stretchr/testify/assert" +) + +//go:embed testdata/fyne.png +var iconData []byte + +func TestCachedIcon_PATH(t *testing.T) { + a := &fyneApp{uniqueID: "icontest"} + assert.Equal(t, "", a.cachedIconPath()) + + a.SetIcon(fyne.NewStaticResource("dummy", iconData)) + path := a.cachedIconPath() + assert.NotEqual(t, "", path) + + info, err := os.Stat(path) + assert.Nil(t, err) + assert.Equal(t, "icon.png", info.Name()) + + err = os.Remove(path) + assert.Nil(t, err) +} diff --git a/app/testdata/fyne.png b/app/testdata/fyne.png new file mode 100644 index 0000000000000000000000000000000000000000..066216d81b91d7171ad4e462a4462f1bd41e957b GIT binary patch literal 19560 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 z|F#E*1ONa40D}I%c7+W9000000000000000000000A>LNkXYGol|W|z0000 Date: Tue, 15 Oct 2024 16:54:51 +0100 Subject: [PATCH 10/74] Testing locale parse --- lang/locale_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 lang/locale_test.go diff --git a/lang/locale_test.go b/lang/locale_test.go new file mode 100644 index 0000000000..71d134506d --- /dev/null +++ b/lang/locale_test.go @@ -0,0 +1,20 @@ +package lang + +import ( + "testing" + + "github.com/jeandeaual/go-locale" + "github.com/stretchr/testify/assert" +) + +func TestSystemLocale(t *testing.T) { + info, err := locale.GetLocale() + if err != nil { + // something not testable + t.Log("Unable to run locale test because", err) + return + } + + loc := SystemLocale() + assert.Equal(t, info[:2], loc.String()[:2]) +} From 7bf5ab6a337007b8803db76faf9d9f409d584f63 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 15 Oct 2024 17:01:56 +0100 Subject: [PATCH 11/74] Add a basic playground test --- tools/playground/playground.go | 12 +++++++++-- tools/playground/playground_test.go | 31 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 tools/playground/playground_test.go diff --git a/tools/playground/playground.go b/tools/playground/playground.go index 8dfb852dda..04481d8471 100644 --- a/tools/playground/playground.go +++ b/tools/playground/playground.go @@ -14,15 +14,23 @@ import ( "fyne.io/fyne/v2/theme" ) -func imageToPlayground(img image.Image) { +func encodeImage(img image.Image) (string, error) { var buf bytes.Buffer err := png.Encode(&buf, img) + if err != nil { + return "", err + } + + return base64.StdEncoding.EncodeToString(buf.Bytes()), nil +} + +func imageToPlayground(img image.Image) { + enc, err := encodeImage(img) if err != nil { fyne.LogError("Failed to encode image", err) return } - enc := base64.StdEncoding.EncodeToString(buf.Bytes()) fmt.Println("IMAGE:" + enc) } diff --git a/tools/playground/playground_test.go b/tools/playground/playground_test.go new file mode 100644 index 0000000000..dab78a0ab2 --- /dev/null +++ b/tools/playground/playground_test.go @@ -0,0 +1,31 @@ +package playground + +import ( + "encoding/base64" + "image/color" + "testing" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/driver/software" + "fyne.io/fyne/v2/internal/test" + "fyne.io/fyne/v2/theme" + + "github.com/stretchr/testify/assert" +) + +func TestRender(t *testing.T) { + obj := canvas.NewRectangle(color.Black) + obj.SetMinSize(fyne.NewSquareSize(10)) + + img := software.Render(obj, test.DarkTheme(theme.DefaultTheme())) + assert.NotNil(t, img) + + enc, err := encodeImage(img) + assert.Nil(t, err) + assert.Equal(t, "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAFElEQVR4nGJiwAtGpbECQAAAAP//DogAFaNSFa8AAAAASUVORK5CYII=", enc) + + bytes, err := base64.StdEncoding.DecodeString(enc) + assert.Nil(t, err) + assert.Equal(t, "PNG", string(bytes)[1:4]) +} From f9d4d6583421ac7f5e680c762a2c5970c7882629 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 15 Oct 2024 17:06:59 +0100 Subject: [PATCH 12/74] Don't bomb simulated mobile locale tests --- lang/locale_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lang/locale_test.go b/lang/locale_test.go index 71d134506d..1cdd1682fe 100644 --- a/lang/locale_test.go +++ b/lang/locale_test.go @@ -15,6 +15,10 @@ func TestSystemLocale(t *testing.T) { return } + if len(info) == 0 { + info = "en_US" + } + loc := SystemLocale() assert.Equal(t, info[:2], loc.String()[:2]) } From 661cd9fb435f61a49885caf70ede705e65378dd1 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 15 Oct 2024 17:10:32 +0100 Subject: [PATCH 13/74] Use a real locale when it is simulated --- lang/locale.go | 2 +- lang/locale_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/locale.go b/lang/locale.go index 77f396fe72..43610d46db 100644 --- a/lang/locale.go +++ b/lang/locale.go @@ -14,7 +14,7 @@ func SystemLocale() fyne.Locale { if err != nil { fyne.LogError("Failed to look up user locale", err) } - if loc == "" { + if len(loc) < 2 { loc = "en" } diff --git a/lang/locale_test.go b/lang/locale_test.go index 1cdd1682fe..c9c42f7f31 100644 --- a/lang/locale_test.go +++ b/lang/locale_test.go @@ -15,7 +15,7 @@ func TestSystemLocale(t *testing.T) { return } - if len(info) == 0 { + if len(info) < 2 { info = "en_US" } From 3fbdadd835c0ac3d30fdf48666a5180ea34af343 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 16 Oct 2024 10:42:44 +0100 Subject: [PATCH 14/74] Correct "nilness" if empty resource --- app/app.go | 3 +++ app/app_test.go | 2 +- app/icon_cache_file_test.go | 9 +++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/app.go b/app/app.go index 6c44d95141..b1f1ca20b8 100644 --- a/app/app.go +++ b/app/app.go @@ -39,6 +39,9 @@ func (a *fyneApp) Icon() fyne.Resource { return a.icon } + if a.Metadata().Icon == nil || len(a.Metadata().Icon.Content()) == 0 { + return nil + } return a.Metadata().Icon } diff --git a/app/app_test.go b/app/app_test.go index ce67ca2cca..0291657bb6 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -36,7 +36,7 @@ func TestFyneApp_UniqueID(t *testing.T) { func TestFyneApp_SetIcon(t *testing.T) { app := NewWithID("io.fyne.test") - metaIcon := &fyne.StaticResource{StaticName: "Metadata"} + metaIcon := &fyne.StaticResource{StaticName: "Metadata", StaticContent: []byte("?PNG...")} SetMetadata(fyne.AppMetadata{ Icon: metaIcon, }) diff --git a/app/icon_cache_file_test.go b/app/icon_cache_file_test.go index a285500cbd..3a810a0eb7 100644 --- a/app/icon_cache_file_test.go +++ b/app/icon_cache_file_test.go @@ -13,17 +13,22 @@ import ( var iconData []byte func TestCachedIcon_PATH(t *testing.T) { + SetMetadata(fyne.AppMetadata{}) a := &fyneApp{uniqueID: "icontest"} assert.Equal(t, "", a.cachedIconPath()) a.SetIcon(fyne.NewStaticResource("dummy", iconData)) path := a.cachedIconPath() - assert.NotEqual(t, "", path) + if path == "" { + t.Error("cache path not constructed") + return + } else { + defer os.Remove(path) + } info, err := os.Stat(path) assert.Nil(t, err) assert.Equal(t, "icon.png", info.Name()) - err = os.Remove(path) assert.Nil(t, err) } From ec99b16f57f4be215c13a3f615ecf777fee2197a Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 16 Oct 2024 10:42:57 +0100 Subject: [PATCH 15/74] Correct broken test case --- app/preferences_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/app/preferences_test.go b/app/preferences_test.go index 101d59e7bf..2a7e733cb0 100644 --- a/app/preferences_test.go +++ b/app/preferences_test.go @@ -43,6 +43,7 @@ func TestPreferences_Save(t *testing.T) { val["keyFloatList"] = []float64{1.1, 2.2, 3.3} val["keyBool"] = true val["keyBoolList"] = []bool{true, false, true} + val["keyEmptyList"] = []string{} }) path := p.storagePath() From eec147a71aa692641ec65ee42a251b3cffea70e3 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 16 Oct 2024 20:29:26 +0100 Subject: [PATCH 16/74] Fix whitespace and possible newline bug --- cmd/fyne/internal/templates/bundled.go | 2 +- cmd/fyne/internal/templates/data/app.desktop | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/fyne/internal/templates/bundled.go b/cmd/fyne/internal/templates/bundled.go index e2bed532b2..4da6ab8eac 100644 --- a/cmd/fyne/internal/templates/bundled.go +++ b/cmd/fyne/internal/templates/bundled.go @@ -23,7 +23,7 @@ var resourceMakefile = &fyne.StaticResource{ var resourceAppDesktop = &fyne.StaticResource{ StaticName: "app.desktop", StaticContent: []byte( - "[Desktop Entry]\nType=Application\nName={{.Name}}\n{{- if ne .GenericName \"\"}}\nGenericName={{.GenericName}}{{end}}\nExec={{.Exec}} {{- .ExecParams}}\nIcon={{.Name}}\n{{- if ne .Comment \"\"}}\nComment={{.Comment}}{{end}}\n{{- if ne .Categories \"\"}}\nCategories={{.Categories}}{{end}}\n{{- if ne .Keywords \"\"}}Keywords={{.Keywords}}{{else}}Keywords=fyne;{{end}}\n\n{{- if or (ne .SourceRepo \"\") (ne .SourceDir \"\")}}\n[X-Fyne Source]\nRepo={{.SourceRepo}}\nDir={{.SourceDir}}\n{{end}}"), + "[Desktop Entry]\nType=Application\nName={{.Name}}\n{{- if ne .GenericName \"\"}}\nGenericName={{.GenericName}}{{end}}\nExec={{.Exec}} {{- .ExecParams}}\nIcon={{.Name}}\n{{- if ne .Comment \"\"}}\nComment={{.Comment}}{{end}}\n{{- if ne .Categories \"\"}}\nCategories={{.Categories}}{{end}}\nKeywords={{if ne .Keywords \"\"}}{{.Keywords}}{{else}}fyne;{{end}}\n\n{{if or (ne .SourceRepo \"\") (ne .SourceDir \"\") -}}\n[X-Fyne Source]\nRepo={{.SourceRepo}}\nDir={{.SourceDir}}\n\n{{end -}}\n"), } var resourceAppManifest = &fyne.StaticResource{ StaticName: "app.manifest", diff --git a/cmd/fyne/internal/templates/data/app.desktop b/cmd/fyne/internal/templates/data/app.desktop index cfba84c65b..0ee461ba07 100644 --- a/cmd/fyne/internal/templates/data/app.desktop +++ b/cmd/fyne/internal/templates/data/app.desktop @@ -9,10 +9,11 @@ Icon={{.Name}} Comment={{.Comment}}{{end}} {{- if ne .Categories ""}} Categories={{.Categories}}{{end}} -{{- if ne .Keywords ""}}Keywords={{.Keywords}}{{else}}Keywords=fyne;{{end}} +Keywords={{if ne .Keywords ""}}{{.Keywords}}{{else}}fyne;{{end}} -{{- if or (ne .SourceRepo "") (ne .SourceDir "")}} +{{if or (ne .SourceRepo "") (ne .SourceDir "") -}} [X-Fyne Source] Repo={{.SourceRepo}} Dir={{.SourceDir}} -{{end}} \ No newline at end of file + +{{end -}} From 877c4c106e2bc6909b2b281853282772d3c8e35b Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sat, 12 Oct 2024 12:23:50 +0100 Subject: [PATCH 17/74] Continue scrolling after finger leaves the screen on mobile --- internal/driver/mobile/canvas.go | 11 ++++++++--- internal/driver/mobile/driver.go | 32 +++++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/internal/driver/mobile/canvas.go b/internal/driver/mobile/canvas.go index 5c8c6a5db1..081153eea4 100644 --- a/internal/driver/mobile/canvas.go +++ b/internal/driver/mobile/canvas.go @@ -25,6 +25,7 @@ type canvas struct { initialized bool lastTapDown map[int]time.Time lastTapDownPos map[int]fyne.Position + lastTapDelta map[int]fyne.Delta menu fyne.CanvasObject padded bool scale float32 @@ -54,6 +55,7 @@ func newCanvas(dev fyne.Device) fyne.Canvas { device: d, lastTapDown: make(map[int]time.Time), lastTapDownPos: make(map[int]fyne.Position), + lastTapDelta: make(map[int]fyne.Delta), padded: true, scale: dev.SystemScaleForWindow(nil), // we don't need a window parameter on mobile, touched: make(map[int]mobile.Touchable), @@ -258,6 +260,8 @@ func (c *canvas) tapMove(pos fyne.Position, tapID int, return } c.lastTapDownPos[tapID] = pos + offset := fyne.Delta{DX: deltaX, DY: deltaY} + c.lastTapDelta[tapID] = offset co, objPos, _ := c.findObjectAtPositionMatching(pos, func(object fyne.CanvasObject) bool { if _, ok := object.(fyne.Draggable); ok { @@ -292,7 +296,7 @@ func (c *canvas) tapMove(pos fyne.Position, tapID int, ev := &fyne.DragEvent{} draggedObjDelta := c.dragStart.Subtract(c.dragging.(fyne.CanvasObject).Position()) ev.Position = pos.Subtract(c.dragOffset).Add(draggedObjDelta) - ev.Dragged = fyne.Delta{DX: deltaX, DY: deltaY} + ev.Dragged = offset dragCallback(c.dragging, ev) } @@ -301,10 +305,11 @@ func (c *canvas) tapUp(pos fyne.Position, tapID int, tapCallback func(fyne.Tappable, *fyne.PointEvent), tapAltCallback func(fyne.SecondaryTappable, *fyne.PointEvent), doubleTapCallback func(fyne.DoubleTappable, *fyne.PointEvent), - dragCallback func(fyne.Draggable)) { + dragCallback func(fyne.Draggable, *fyne.DragEvent)) { if c.dragging != nil { - dragCallback(c.dragging) + previousDelta := c.lastTapDelta[tapID] + dragCallback(c.dragging, &fyne.DragEvent{Dragged: previousDelta}) c.dragging = nil return diff --git a/internal/driver/mobile/driver.go b/internal/driver/mobile/driver.go index 41675ee581..2966abaf04 100644 --- a/internal/driver/mobile/driver.go +++ b/internal/driver/mobile/driver.go @@ -1,6 +1,7 @@ package mobile import ( + "math" "runtime" "strconv" "sync/atomic" @@ -30,9 +31,11 @@ import ( ) const ( - tapMoveThreshold = 4.0 // how far can we move before it is a drag - tapSecondaryDelay = 300 * time.Millisecond // how long before secondary tap - tapDoubleDelay = 500 * time.Millisecond // max duration between taps for a DoubleTap event + tapMoveDecay = 0.85 // how much should the scroll continue decay on each frame? + tapMoveEndThreshold = 2.0 // at what offset will we stop decaying? + tapMoveThreshold = 4.0 // how far can we move before it is a drag + tapSecondaryDelay = 300 * time.Millisecond // how long before secondary tap + tapDoubleDelay = 500 * time.Millisecond // max duration between taps for a DoubleTap event ) // Configuration is the system information about the current device @@ -395,8 +398,27 @@ func (d *driver) tapUpCanvas(w *window, x, y float32, tapID touch.Sequence) { w.QueueEvent(func() { wid.TappedSecondary(ev) }) }, func(wid fyne.DoubleTappable, ev *fyne.PointEvent) { w.QueueEvent(func() { wid.DoubleTapped(ev) }) - }, func(wid fyne.Draggable) { - w.QueueEvent(wid.DragEnd) + }, func(wid fyne.Draggable, ev *fyne.DragEvent) { + if math.Abs(float64(ev.Dragged.DX)) <= tapMoveEndThreshold && math.Abs(float64(ev.Dragged.DY)) <= tapMoveEndThreshold { + w.QueueEvent(wid.DragEnd) + return + } + + go func() { + for math.Abs(float64(ev.Dragged.DX)) > tapMoveEndThreshold || math.Abs(float64(ev.Dragged.DY)) > tapMoveEndThreshold { + if math.Abs(float64(ev.Dragged.DX)) > 0 { + ev.Dragged.DX *= tapMoveDecay + } + if math.Abs(float64(ev.Dragged.DY)) > 0 { + ev.Dragged.DY *= tapMoveDecay + } + + w.QueueEvent(func() { wid.Dragged(ev) }) + time.Sleep(time.Millisecond * 12) + } + + w.QueueEvent(wid.DragEnd) + }() }) } From 8a343184b80a2d7a5f52da84884730ac44190800 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sat, 12 Oct 2024 12:24:25 +0100 Subject: [PATCH 18/74] Also reduce mobile offset - some fingers were missing the target --- internal/driver/mobile/device_android.go | 2 +- internal/driver/mobile/device_ios.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/driver/mobile/device_android.go b/internal/driver/mobile/device_android.go index 6952e22eea..44c0398cc9 100644 --- a/internal/driver/mobile/device_android.go +++ b/internal/driver/mobile/device_android.go @@ -4,7 +4,7 @@ package mobile import "fyne.io/fyne/v2" -const tapYOffset = -12.0 // to compensate for how we hold our fingers on the device +const tapYOffset = -8.0 // to compensate for how we hold our fingers on the device func (*device) SystemScaleForWindow(_ fyne.Window) float32 { if currentDPI >= 600 { diff --git a/internal/driver/mobile/device_ios.go b/internal/driver/mobile/device_ios.go index a69bcbf164..284b6c04ec 100644 --- a/internal/driver/mobile/device_ios.go +++ b/internal/driver/mobile/device_ios.go @@ -4,7 +4,7 @@ package mobile import "fyne.io/fyne/v2" -const tapYOffset = -12.0 // to compensate for how we hold our fingers on the device +const tapYOffset = -8.0 // to compensate for how we hold our fingers on the device func (*device) SystemScaleForWindow(_ fyne.Window) float32 { if currentDPI >= 450 { From f78cde97078e7fcf114beddc5f18b7080313d4ea Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sat, 12 Oct 2024 12:29:20 +0100 Subject: [PATCH 19/74] Oops, fix mobile test compilation --- internal/driver/mobile/canvas_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/driver/mobile/canvas_test.go b/internal/driver/mobile/canvas_test.go index 1e0d771dd4..f566dc2636 100644 --- a/internal/driver/mobile/canvas_test.go +++ b/internal/driver/mobile/canvas_test.go @@ -249,7 +249,7 @@ func Test_canvas_Tappable(t *testing.T) { c.tapUp(fyne.NewPos(15, 15), 0, func(wid fyne.Tappable, ev *fyne.PointEvent) { }, func(wid fyne.SecondaryTappable, ev *fyne.PointEvent) { }, func(wid fyne.DoubleTappable, ev *fyne.PointEvent) { - }, func(wid fyne.Draggable) { + }, func(wid fyne.Draggable, ev *fyne.DragEvent) { }) assert.True(t, content.up) @@ -286,7 +286,7 @@ func Test_canvas_Tapped(t *testing.T) { wid.TappedSecondary(ev) }, func(wid fyne.DoubleTappable, ev *fyne.PointEvent) { wid.DoubleTapped(ev) - }, func(wid fyne.Draggable) { + }, func(wid fyne.Draggable, ev *fyne.DragEvent) { }) assert.True(t, tapped, "tap primary") @@ -340,7 +340,7 @@ func Test_canvas_TappedMulti(t *testing.T) { }, func(wid fyne.SecondaryTappable, ev *fyne.PointEvent) { }, func(wid fyne.DoubleTappable, ev *fyne.PointEvent) { wid.DoubleTapped(ev) - }, func(wid fyne.Draggable) { + }, func(wid fyne.Draggable, ev *fyne.DragEvent) { }) assert.False(t, buttonTap, "button should not be tapped") @@ -369,7 +369,7 @@ func Test_canvas_TappedSecondary(t *testing.T) { wid.TappedSecondary(ev) }, func(wid fyne.DoubleTappable, ev *fyne.PointEvent) { wid.DoubleTapped(ev) - }, func(wid fyne.Draggable) { + }, func(wid fyne.Draggable, ev *fyne.DragEvent) { }) assert.False(t, obj.tap, "don't tap primary") @@ -464,7 +464,7 @@ func simulateTap(c *canvas) { }, func(wid fyne.SecondaryTappable, ev *fyne.PointEvent) { }, func(wid fyne.DoubleTappable, ev *fyne.PointEvent) { wid.DoubleTapped(ev) - }, func(wid fyne.Draggable) { + }, func(wid fyne.Draggable, ev *fyne.DragEvent) { }) } From 58d3f84a518eca84dd7428509f0338651fd11d0d Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sat, 12 Oct 2024 16:42:51 +0100 Subject: [PATCH 20/74] Adjust decay curve from user feedback --- internal/driver/mobile/driver.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/driver/mobile/driver.go b/internal/driver/mobile/driver.go index 2966abaf04..d0ac74921f 100644 --- a/internal/driver/mobile/driver.go +++ b/internal/driver/mobile/driver.go @@ -31,7 +31,7 @@ import ( ) const ( - tapMoveDecay = 0.85 // how much should the scroll continue decay on each frame? + tapMoveDecay = 0.92 // how much should the scroll continue decay on each frame? tapMoveEndThreshold = 2.0 // at what offset will we stop decaying? tapMoveThreshold = 4.0 // how far can we move before it is a drag tapSecondaryDelay = 300 * time.Millisecond // how long before secondary tap @@ -414,7 +414,7 @@ func (d *driver) tapUpCanvas(w *window, x, y float32, tapID touch.Sequence) { } w.QueueEvent(func() { wid.Dragged(ev) }) - time.Sleep(time.Millisecond * 12) + time.Sleep(time.Millisecond * 16) } w.QueueEvent(wid.DragEnd) From 7c849e40b05178fdf41b0eca3078f0f36c5663a4 Mon Sep 17 00:00:00 2001 From: Daniel Riera Date: Mon, 21 Oct 2024 10:50:30 +0200 Subject: [PATCH 21/74] Create base.es.json Add Spanish translation --- lang/translations/base.es.json | 47 ++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 lang/translations/base.es.json diff --git a/lang/translations/base.es.json b/lang/translations/base.es.json new file mode 100644 index 0000000000..587f444e53 --- /dev/null +++ b/lang/translations/base.es.json @@ -0,0 +1,47 @@ +{ + "Advanced": "Avanzado", + "Cancel": "Cancelar", + "Confirm": "Confirmar", + "Copy": "Copiar", + "Create Folder": "Crear Carpeta", + "Cut": "Cortar", + "Enter filename": "Introduzca nombres del archivo", + "Error": "Error", + "Favourites": "Favoritos", + "File": "Archivo", + "Folder": "Carpeta", + "New Folder": "Nueva Carpeta", + "No": "No", + "OK": "OK", + "Open": "Abrir", + "Paste": "Pegar", + "Quit": "Salir", + "Redo": "Rehacer", + "Save": "Guardar", + "Select all": "Seleccionar todo", + "Show Hidden Files": "Mostrar archivos ocultos", + "Undo": "Deshacer", + "Yes": "Si", + + "file.name": { + "other": "Nombre" + }, + "file.parent": { + "other": "Padre" + }, + + "monday": "Lunes", + "monday.short": "Lun", + "tuesday": "Martes", + "tuesday.short": "Mar", + "wednesday": "Miércoles", + "wednesday.short": "Mie", + "thursday": "Jueves", + "thursday.short": "Jue", + "friday": "Viernes", + "friday.short": "Vie", + "saturday": "Sábado", + "saturday.short": "Sab", + "sunday": "Domingo", + "sunday.short": "Dom" +} From 93f3948a19bb90b69fa10903570549821ac35a13 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 6 Nov 2024 17:50:59 +0000 Subject: [PATCH 22/74] Remove extra spaces --- storage/uri_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/storage/uri_test.go b/storage/uri_test.go index 97146aec70..b439f9501e 100644 --- a/storage/uri_test.go +++ b/storage/uri_test.go @@ -15,7 +15,6 @@ import ( ) func TestURIAuthority(t *testing.T) { - // from IETF RFC 3986 s := "foo://example.com:8042/over/there?name=ferret#nose" u, err := storage.ParseURI(s) @@ -226,7 +225,6 @@ func TestExists(t *testing.T) { fooParent, err := storage.Parent(foo) assert.Nil(t, err) assert.Equal(t, fooExpectedParent.String(), fooParent.String()) - } func TestWriteAndDelete(t *testing.T) { @@ -310,7 +308,6 @@ func TestWriteAndDelete(t *testing.T) { bazExists, err := storage.Exists(baz) assert.False(t, bazExists) assert.Nil(t, err) - } func TestCanWrite(t *testing.T) { From e934254460f53b19b305c40b8869844001e3b804 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 6 Nov 2024 17:51:16 +0000 Subject: [PATCH 23/74] Ensure our file path tries to be absolute so the URI is valid --- storage/uri.go | 7 +++++++ storage/uri_test.go | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/storage/uri.go b/storage/uri.go index 5a8974a34e..c64e4dbea1 100644 --- a/storage/uri.go +++ b/storage/uri.go @@ -1,12 +1,19 @@ package storage import ( + "path/filepath" + "fyne.io/fyne/v2" "fyne.io/fyne/v2/storage/repository" ) // NewFileURI creates a new URI from the given file path. func NewFileURI(path string) fyne.URI { + absolute, err := filepath.Abs(path) + if err == nil { + path = absolute + } + return repository.NewFileURI(path) } diff --git a/storage/uri_test.go b/storage/uri_test.go index b439f9501e..9fe2d9bd6f 100644 --- a/storage/uri_test.go +++ b/storage/uri_test.go @@ -2,6 +2,7 @@ package storage_test import ( "io" + "os" "runtime" "testing" @@ -227,6 +228,27 @@ func TestExists(t *testing.T) { assert.Equal(t, fooExpectedParent.String(), fooParent.String()) } +func TestFileAbs(t *testing.T) { + pwd, err := os.Getwd() + if err != nil { + t.Error("Could not get working directory") + defer os.Chdir(pwd) + } + + home, err := os.UserHomeDir() + if err != nil { + t.Error("Could not get user home directory") + } + + os.Chdir(home) + + abs := storage.NewFileURI(home) + rel := storage.NewFileURI(".") + + assert.Equal(t, abs.Path(), rel.Path()) + assert.Equal(t, abs.String(), rel.String()) +} + func TestWriteAndDelete(t *testing.T) { // set up our repository - it's OK if we already registered it m := intRepo.NewInMemoryRepository("uritest") From 6a31c44c6ff0a11f3e985d2f5e01ab8bcbd13ddd Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 6 Nov 2024 17:56:16 +0000 Subject: [PATCH 24/74] Improve documentation --- uri.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/uri.go b/uri.go index a4228306d2..0991cb62dc 100644 --- a/uri.go +++ b/uri.go @@ -23,9 +23,10 @@ type URIWriteCloser interface { // URI represents the identifier of a resource on a target system. This // resource may be a file or another data source such as an app or file sharing -// system. +// system. The URI represents an absolute location of a resource, it is up to any +// parse or constructor implementations to ensure that relative resources are made absolute. // -// In general, it is expected that URI implementations follow IETF RFC3896. +// In general, it is expected that URI implementations follow IETF RFC3986. // Implementations are highly recommended to utilize [net/url] to implement URI // parsing methods, especially [net/url/url.Scheme], [net/url/url.Authority], // [net/url/url.Path], [net/url/url.Query], and [net/url/url.Fragment]. From a85ecf172e5bded55a621eef9e43594261260ad3 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 6 Nov 2024 18:04:10 +0000 Subject: [PATCH 25/74] Make assumptions about different platform Abs paths are so we don't trip up when the URI is used on a different system --- storage/uri.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/storage/uri.go b/storage/uri.go index c64e4dbea1..3cf8ed4521 100644 --- a/storage/uri.go +++ b/storage/uri.go @@ -9,9 +9,16 @@ import ( // NewFileURI creates a new URI from the given file path. func NewFileURI(path string) fyne.URI { - absolute, err := filepath.Abs(path) - if err == nil { - path = absolute + assumeAbs := false + if len(path) >= 2 { + assumeAbs = path[1] == ':' || path[0] == '/' + } + + if !assumeAbs { + absolute, err := filepath.Abs(path) + if err == nil { + path = absolute + } } return repository.NewFileURI(path) From 642eaf649ff1c5416916168c61b6e457586cdf8e Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 6 Nov 2024 18:05:46 +0000 Subject: [PATCH 26/74] Another note in the docs --- storage/uri.go | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/uri.go b/storage/uri.go index 3cf8ed4521..63c9faed0a 100644 --- a/storage/uri.go +++ b/storage/uri.go @@ -8,6 +8,7 @@ import ( ) // NewFileURI creates a new URI from the given file path. +// Relative paths will be converted to absolute using filepath.Abs if required. func NewFileURI(path string) fyne.URI { assumeAbs := false if len(path) >= 2 { From 41086dfb95c955ed8dcbdee58d08c00ad7b85d46 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 6 Nov 2024 18:31:42 +0000 Subject: [PATCH 27/74] Fix support for the absolute path / --- storage/uri.go | 8 ++++++-- storage/uri_test.go | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/storage/uri.go b/storage/uri.go index 63c9faed0a..adab871855 100644 --- a/storage/uri.go +++ b/storage/uri.go @@ -11,8 +11,12 @@ import ( // Relative paths will be converted to absolute using filepath.Abs if required. func NewFileURI(path string) fyne.URI { assumeAbs := false - if len(path) >= 2 { - assumeAbs = path[1] == ':' || path[0] == '/' + if len(path) >= 1 { + if path[0] == '/' { + assumeAbs = true + } else if len(path) >= 2 { + assumeAbs = path[1] == ':' + } } if !assumeAbs { diff --git a/storage/uri_test.go b/storage/uri_test.go index 9fe2d9bd6f..40741c8f64 100644 --- a/storage/uri_test.go +++ b/storage/uri_test.go @@ -247,6 +247,8 @@ func TestFileAbs(t *testing.T) { assert.Equal(t, abs.Path(), rel.Path()) assert.Equal(t, abs.String(), rel.String()) + + assert.Equal(t, "file:///", storage.NewFileURI("/").String()) } func TestWriteAndDelete(t *testing.T) { From 1a201c4bbd813721859f8a0905fd46b74be70783 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 6 Nov 2024 18:51:17 +0000 Subject: [PATCH 28/74] Try a more platform-agnostic approach to getting parent string --- internal/repository/file.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/repository/file.go b/internal/repository/file.go index 45837cd4dc..4adf599ae7 100644 --- a/internal/repository/file.go +++ b/internal/repository/file.go @@ -176,9 +176,8 @@ func (r *FileRepository) Parent(u fyne.URI) (fyne.URI, error) { return nil, repository.ErrURIRoot } - parent := "" - // use the system native path resolution - parent = filepath.Dir(s) + child := filepath.Base(s) + parent := s[:len(s)-len(child)] if parent[len(parent)-1] != filepath.Separator { parent += "/" } From e924d6d90fd48085877b4b7dd2406fa691b21faa Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Thu, 7 Nov 2024 17:02:25 +0000 Subject: [PATCH 29/74] Fixing windows issues in the simplified parent finding --- internal/repository/file.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/repository/file.go b/internal/repository/file.go index 4adf599ae7..5d8898077f 100644 --- a/internal/repository/file.go +++ b/internal/repository/file.go @@ -171,14 +171,14 @@ func (r *FileRepository) Parent(u fyne.URI) (fyne.URI, error) { // trim the scheme s = strings.TrimPrefix(s, fileSchemePrefix) - // Completely empty URI with just a scheme - if s == "" { + // Completely empty URI or only root component + if s == "" || s == "/" || (len(s) == 2 && s[1] == ':') { return nil, repository.ErrURIRoot } child := filepath.Base(s) parent := s[:len(s)-len(child)] - if parent[len(parent)-1] != filepath.Separator { + if parent[len(parent)-1] != '/' { parent += "/" } From c93314d306b3760dcba868468d86edc1fe2663a3 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 19 Nov 2024 17:31:03 +0000 Subject: [PATCH 30/74] Document the reasons behind avoiding filesystem operations --- internal/repository/file.go | 4 ++-- storage/uri.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/repository/file.go b/internal/repository/file.go index 5d8898077f..8b6342a801 100644 --- a/internal/repository/file.go +++ b/internal/repository/file.go @@ -177,8 +177,8 @@ func (r *FileRepository) Parent(u fyne.URI) (fyne.URI, error) { } child := filepath.Base(s) - parent := s[:len(s)-len(child)] - if parent[len(parent)-1] != '/' { + parent := s[:len(s)-len(child)] // avoid filepath.Dir as it follows platform rules + if parent == "" || parent[len(parent)-1] != '/' { parent += "/" } diff --git a/storage/uri.go b/storage/uri.go index adab871855..36b2c08a7d 100644 --- a/storage/uri.go +++ b/storage/uri.go @@ -10,7 +10,7 @@ import ( // NewFileURI creates a new URI from the given file path. // Relative paths will be converted to absolute using filepath.Abs if required. func NewFileURI(path string) fyne.URI { - assumeAbs := false + assumeAbs := false // avoid filepath.IsAbs as it follows platform rules if len(path) >= 1 { if path[0] == '/' { assumeAbs = true From d4bbfc5d4daa6d8544c47fee2b52e72fa17266cf Mon Sep 17 00:00:00 2001 From: Jacalz Date: Sun, 10 Nov 2024 21:22:04 +0100 Subject: [PATCH 31/74] Only change variant when color scheme changes --- app/app_xdg.go | 7 ++++--- app/settings.go | 7 +++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/app_xdg.go b/app/app_xdg.go index e1835181fe..de821dfdf0 100644 --- a/app/app_xdg.go +++ b/app/app_xdg.go @@ -122,14 +122,15 @@ func rootConfigDir() string { func watchTheme(s *settings) { go func() { // Theme lookup hangs on some desktops. Update theme variant cache from within goroutine. - internalapp.CurrentVariant.Store(uint64(findFreedesktopColorScheme())) - s.setupTheme() + themeVariant := findFreedesktopColorScheme() + internalapp.CurrentVariant.Store(uint64(themeVariant)) + s.applyVariant(themeVariant) portalSettings.OnSignalSettingChanged(func(changed portalSettings.Changed) { if changed.Namespace == "org.freedesktop.appearance" && changed.Key == "color-scheme" { themeVariant := colorSchemeToThemeVariant(appearance.ColorScheme(changed.Value.(uint32))) internalapp.CurrentVariant.Store(uint64(themeVariant)) - s.setupTheme() + s.applyVariant(themeVariant) } }) }() diff --git a/app/settings.go b/app/settings.go index ca83c84e6d..fe92bc18c2 100644 --- a/app/settings.go +++ b/app/settings.go @@ -97,6 +97,13 @@ func (s *settings) applyTheme(theme fyne.Theme, variant fyne.ThemeVariant) { s.apply() } +func (s *settings) applyVariant(variant fyne.ThemeVariant) { + s.propertyLock.Lock() + defer s.propertyLock.Unlock() + s.variant = variant + s.apply() +} + func (s *settings) Scale() float32 { s.propertyLock.RLock() defer s.propertyLock.RUnlock() From 3d4a843a8b581ac0329a7b3d330238cfca799123 Mon Sep 17 00:00:00 2001 From: Jacalz Date: Sun, 10 Nov 2024 21:32:34 +0100 Subject: [PATCH 32/74] Use new namespace constant for appearance --- app/app_xdg.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/app_xdg.go b/app/app_xdg.go index de821dfdf0..71aeae4142 100644 --- a/app/app_xdg.go +++ b/app/app_xdg.go @@ -127,7 +127,7 @@ func watchTheme(s *settings) { s.applyVariant(themeVariant) portalSettings.OnSignalSettingChanged(func(changed portalSettings.Changed) { - if changed.Namespace == "org.freedesktop.appearance" && changed.Key == "color-scheme" { + if changed.Namespace == appearance.Namespace && changed.Key == "color-scheme" { themeVariant := colorSchemeToThemeVariant(appearance.ColorScheme(changed.Value.(uint32))) internalapp.CurrentVariant.Store(uint64(themeVariant)) s.applyVariant(themeVariant) diff --git a/go.mod b/go.mod index 1643bf56d9..8ec77bafb2 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/nicksnyder/go-i18n/v2 v2.4.0 - github.com/rymdport/portal v0.2.6 + github.com/rymdport/portal v0.3.0 github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef github.com/stretchr/testify v1.8.4 diff --git a/go.sum b/go.sum index 3aa61ab33e..cfe83b297d 100644 --- a/go.sum +++ b/go.sum @@ -263,8 +263,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/rymdport/portal v0.2.6 h1:HWmU3gORu7vWcpr7VSwUS2Xx1HtJXVcUuTqEZcMEsIg= -github.com/rymdport/portal v0.2.6/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4= +github.com/rymdport/portal v0.3.0 h1:QRHcwKwx3kY5JTQcsVhmhC3TGqGQb9LFghVNUy8AdB8= +github.com/rymdport/portal v0.3.0/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= From 057cc956752c6e9b6649b78699d3c214bc1669aa Mon Sep 17 00:00:00 2001 From: Simon Dassow Date: Fri, 18 Oct 2024 15:37:17 +0200 Subject: [PATCH 33/74] Use base (BSD) tar (flags and file extension) on OpenBSD to create package --- cmd/fyne/internal/commands/package-unix.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cmd/fyne/internal/commands/package-unix.go b/cmd/fyne/internal/commands/package-unix.go index 1aede64e70..520b27ccf8 100644 --- a/cmd/fyne/internal/commands/package-unix.go +++ b/cmd/fyne/internal/commands/package-unix.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "runtime" "strings" "fyne.io/fyne/v2/cmd/fyne/internal/templates" @@ -96,8 +97,14 @@ func (p *Packager) packageUNIX() error { return fmt.Errorf("failed to write Makefile string: %w", err) } + tarCmdArgs := []string{"-Jcf", filepath.Join(p.dir, p.Name+".tar.xz")} + if runtime.GOOS == "openbsd" { + tarCmdArgs = []string{"-zcf", filepath.Join(p.dir, p.Name+".tar.gz")} + } + tarCmdArgs = append(tarCmdArgs, "-C", filepath.Join(p.dir, tempDir), "usr", "Makefile") + var buf bytes.Buffer - tarCmd := execabs.Command("tar", "-Jcf", filepath.Join(p.dir, p.Name+".tar.xz"), "-C", filepath.Join(p.dir, tempDir), "usr", "Makefile") + tarCmd := execabs.Command("tar", tarCmdArgs...) tarCmd.Stderr = &buf if err = tarCmd.Run(); err != nil { return fmt.Errorf("failed to create archive with tar: %s - %w", buf.String(), err) From 40b317b8810efbd3bf2dcd21968f69a105438cee Mon Sep 17 00:00:00 2001 From: Simon Dassow Date: Tue, 29 Oct 2024 00:34:01 +0100 Subject: [PATCH 34/74] Check target os to change tar flags --- cmd/fyne/internal/commands/package-unix.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/fyne/internal/commands/package-unix.go b/cmd/fyne/internal/commands/package-unix.go index 520b27ccf8..1e5ef080df 100644 --- a/cmd/fyne/internal/commands/package-unix.go +++ b/cmd/fyne/internal/commands/package-unix.go @@ -5,7 +5,6 @@ import ( "fmt" "os" "path/filepath" - "runtime" "strings" "fyne.io/fyne/v2/cmd/fyne/internal/templates" @@ -98,7 +97,7 @@ func (p *Packager) packageUNIX() error { } tarCmdArgs := []string{"-Jcf", filepath.Join(p.dir, p.Name+".tar.xz")} - if runtime.GOOS == "openbsd" { + if p.os == "openbsd" { tarCmdArgs = []string{"-zcf", filepath.Join(p.dir, p.Name+".tar.gz")} } tarCmdArgs = append(tarCmdArgs, "-C", filepath.Join(p.dir, tempDir), "usr", "Makefile") From 2fff74969f707cdf15a574b85d76457f427cb3e5 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 19 Nov 2024 17:20:53 +0000 Subject: [PATCH 35/74] Log the failing locale for more help in the future Closes #5271 --- lang/locale.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/locale.go b/lang/locale.go index 43610d46db..93b4362329 100644 --- a/lang/locale.go +++ b/lang/locale.go @@ -20,7 +20,7 @@ func SystemLocale() fyne.Locale { tag, err := language.Parse(loc) if err != nil { - fyne.LogError("Error parsing user locale", err) + fyne.LogError("Error parsing user locale "+loc, err) } return localeFromTag(tag) } @@ -32,7 +32,7 @@ func closestSupportedLocale(locs []string) fyne.Locale { for i, loc := range locs { tag, err := language.Parse(loc) if err != nil { - fyne.LogError("Error parsing user locale", err) + fyne.LogError("Error parsing user locale "+loc, err) } tags[i] = tag } From 232a9f281de170375f558355852c74cf2fa7e88e Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 19 Nov 2024 21:56:17 +0000 Subject: [PATCH 36/74] Add missing arrow key mapping for mobile devices --- internal/driver/mobile/driver.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/driver/mobile/driver.go b/internal/driver/mobile/driver.go index d0ac74921f..636a026d86 100644 --- a/internal/driver/mobile/driver.go +++ b/internal/driver/mobile/driver.go @@ -434,6 +434,11 @@ var keyCodeMap = map[key.Code]fyne.KeyName{ key.CodeHome: fyne.KeyHome, key.CodeEnd: fyne.KeyEnd, + key.CodeLeftArrow: fyne.KeyLeft, + key.CodeRightArrow: fyne.KeyRight, + key.CodeUpArrow: fyne.KeyUp, + key.CodeDownArrow: fyne.KeyDown, + key.CodeF1: fyne.KeyF1, key.CodeF2: fyne.KeyF2, key.CodeF3: fyne.KeyF3, From 07244e6bba79572d5dda812e7b0450cdc388f699 Mon Sep 17 00:00:00 2001 From: Simon Dassow Date: Wed, 20 Nov 2024 17:25:43 +0100 Subject: [PATCH 37/74] Look up available languages in source directory to provide to macOS app language selection --- cmd/fyne/internal/commands/package-darwin.go | 38 ++++++++++++++++++- .../internal/commands/package-darwin_test.go | 27 +++++++++++++ cmd/fyne/internal/templates/bundled.go | 7 +++- cmd/fyne/internal/templates/data/Info.plist | 10 ++++- 4 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 cmd/fyne/internal/commands/package-darwin_test.go diff --git a/cmd/fyne/internal/commands/package-darwin.go b/cmd/fyne/internal/commands/package-darwin.go index 8f8d49ab67..c62fbadf28 100644 --- a/cmd/fyne/internal/commands/package-darwin.go +++ b/cmd/fyne/internal/commands/package-darwin.go @@ -1,11 +1,14 @@ package commands import ( + "errors" "fmt" "image" "image/color" + "io/fs" "os" "path/filepath" + "regexp" "strings" "fyne.io/fyne/v2/cmd/fyne/internal/templates" @@ -21,6 +24,7 @@ type darwinData struct { Version string Build int Category string + Languages []string } func (p *Packager) packageDarwin() (err error) { @@ -39,8 +43,13 @@ func (p *Packager) packageDarwin() (err error) { } }() + langs, err := findTranslationLanguages(p.srcDir) + if err != nil { + return fmt.Errorf("failed to find translation languages: %w", err) + } + tplData := darwinData{Name: p.Name, ExeName: exeName, AppID: p.AppID, Version: p.AppVersion, Build: p.AppBuild, - Category: strings.ToLower(p.category)} + Category: strings.ToLower(p.category), Languages: langs} if err := templates.InfoPlistDarwin.Execute(infoFile, tplData); err != nil { return fmt.Errorf("failed to write plist template: %w", err) } @@ -102,3 +111,30 @@ func processMacOSIcon(in image.Image) image.Image { return dc.Image() } + +func findTranslationLanguages(dir string) ([]string, error) { + langs := []string{} + re := regexp.MustCompile("(?:^|/)([a-z]{2}(?:[-][A-Z]{2})?)\\.json$") + err := filepath.Walk(dir, func(path string, fi fs.FileInfo, err error) error { + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil + } + return err + } + + if fi.IsDir() || !fi.Mode().IsRegular() || filepath.Ext(path) != ".json" { + return nil + } + + m := re.FindStringSubmatch(path) + if len(m) < 2 { + return nil + } + + langs = append(langs, m[1]) + + return nil + }) + return langs, err +} diff --git a/cmd/fyne/internal/commands/package-darwin_test.go b/cmd/fyne/internal/commands/package-darwin_test.go new file mode 100644 index 0000000000..1d1dc3f4f9 --- /dev/null +++ b/cmd/fyne/internal/commands/package-darwin_test.go @@ -0,0 +1,27 @@ +package commands + +import ( + "regexp" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFindTranslationLanguages(t *testing.T) { + dir := t.TempDir() + + f, err := os.Create(filepath.Join(dir, "en.json")) + if err != nil { + t.Fatal(err) + } + f.Write([]byte("")) + f.Close() + + langs, err := findTranslationLanguages(dir) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, 1, len(langs)) + assert.Equal(t, "en", langs[0]) +} diff --git a/cmd/fyne/internal/templates/bundled.go b/cmd/fyne/internal/templates/bundled.go index 4da6ab8eac..ed47d501c7 100644 --- a/cmd/fyne/internal/templates/bundled.go +++ b/cmd/fyne/internal/templates/bundled.go @@ -5,6 +5,11 @@ package templates import "fyne.io/fyne/v2" +var resourceInfoPlistSwp = &fyne.StaticResource{ + StaticName: ".Info.plist.swp", + StaticContent: []byte( + "b0VIM 9.0\x00\x00\x00\x00\x10\x00\x00s\f>g8ʽ\x03b\xcb\x00\x00simon\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00chaunax.errornet.nl\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00~simon/Code/fyne-dev/cmd/fyne/internal/templates/data/Info.plist\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00utf-8\r\x003210\x00\x00\x00\x00#\"! \x13\x12U\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tp\x01\x00\x7f\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00*\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ad\x00\x00\xba\n\x00\x00z\v\x00\x00\x00\x10\x00\x00*\x00\x00\x00\x00\x00\x00\x00\xd9\x0f\x00\x00i\x0f\x00\x00S\x0f\x00\x00L\x0f\x00\x003\x0f\x00\x00\x17\x0f\x00\x00\xf8\x0e\x00\x00\xd9\x0e\x00\x00\xba\x0e\x00\x00\x9d\x0e\x00\x00\x80\x0e\x00\x00d\x0e\x00\x00=\x0e\x00\x00\x1e\x0e\x00\x00\xf7\r\x00\x00\xee\r\x00\x00\xd4\r\x00\x00\xca\r\x00\x00\xae\r\x00\x00\x91\r\x00\x00m\r\x00\x00d\r\x00\x003\r\x00\x00*\r\x00\x00\x00\r\x00\x00\xea\f\x00\x00\xca\f\x00\x00\xb3\f\x00\x00\x9e\f\x00\x00|\f\x00\x00s\f\x00\x00Z\f\x00\x00A\f\x00\x005\f\x00\x00+\f\x00\x00 \f\x00\x00\xfa\v\x00\x00\xc6\v\x00\x00\xa3\v\x00\x00\x8b\v\x00\x00\x83\v\x00\x00z\v\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t10.11\x00\tLSMinimumSystemVersion\x00\tpublic.app-category.{{.Category}}\x00\tLSApplicationCategoryType\x00\t{{- end}}\x00\t\x00\t\t{{- end}}\x00\t\t{{.}}\x00\t\t{{- range .Languages}}\x00\t\x00\tCFBundleLocalizations\x00\t{{- if .Languages}}\x00\tAPPL\x00\tCFBundlePackageType\x00\t6.0\x00\tCFBundleInfoDictionaryVersion\x00\t\x00\tNSSupportsAutomaticGraphicsSwitching\x00\t\x00\tNSHighResolutionCapable\x00\t{{.Build}}\x00\tCFBundleVersion\x00\t\x00\t\tMacOSX\x00\t\x00\tCFBundleSupportedPlatforms\x00\t{{.Version}}\x00\tCFBundleShortVersionString\x00\ticon.icns\x00\tCFBundleIconFile\x00\t{{.AppID}}\x00\tCFBundleIdentifier\x00\t{{.ExeName}}\x00\tCFBundleExecutable\x00\t{{.Name}}\x00\tCFBundleName\x00\x00\x00\x00\x00"), +} var resourceAndroidManifestXml = &fyne.StaticResource{ StaticName: "AndroidManifest.xml", StaticContent: []byte( @@ -13,7 +18,7 @@ var resourceAndroidManifestXml = &fyne.StaticResource{ var resourceInfoPlist = &fyne.StaticResource{ StaticName: "Info.plist", StaticContent: []byte( - "\n\n\n\n\tCFBundleName\n\t{{.Name}}\n\tCFBundleExecutable\n\t{{.ExeName}}\n\tCFBundleIdentifier\n\t{{.AppID}}\n\tCFBundleIconFile\n\ticon.icns\n\tCFBundleShortVersionString\n\t{{.Version}}\n\tCFBundleSupportedPlatforms\n\t\n\t\tMacOSX\n\t\n\tCFBundleVersion\n\t{{.Build}}\n\tNSHighResolutionCapable\n\t\n\tNSSupportsAutomaticGraphicsSwitching\n\t\n\tCFBundleInfoDictionaryVersion\n\t6.0\n\tCFBundlePackageType\n\tAPPL\n\tLSApplicationCategoryType\n\tpublic.app-category.{{.Category}}\n\tLSMinimumSystemVersion\n\t10.11\n\n"), + "\n\n\n\n\tCFBundleName\n\t{{.Name}}\n\tCFBundleExecutable\n\t{{.ExeName}}\n\tCFBundleIdentifier\n\t{{.AppID}}\n\tCFBundleIconFile\n\ticon.icns\n\tCFBundleShortVersionString\n\t{{.Version}}\n\tCFBundleSupportedPlatforms\n\t\n\t\tMacOSX\n\t\n\tCFBundleVersion\n\t{{.Build}}\n\tNSHighResolutionCapable\n\t\n\tNSSupportsAutomaticGraphicsSwitching\n\t\n\tCFBundleInfoDictionaryVersion\n\t6.0\n\tCFBundlePackageType\n\tAPPL\n\t{{- if .Languages}}\n\tCFBundleLocalizations\n\t\n\t\t{{- range .Languages}}\n\t\t{{.}}\n\t\t{{- end}}\n\t\n\t{{- end}}\n\tLSApplicationCategoryType\n\tpublic.app-category.{{.Category}}\n\tLSMinimumSystemVersion\n\t10.11\n\n\n"), } var resourceMakefile = &fyne.StaticResource{ StaticName: "Makefile", diff --git a/cmd/fyne/internal/templates/data/Info.plist b/cmd/fyne/internal/templates/data/Info.plist index 9fb6a1953c..4e4a472080 100644 --- a/cmd/fyne/internal/templates/data/Info.plist +++ b/cmd/fyne/internal/templates/data/Info.plist @@ -26,9 +26,17 @@ 6.0 CFBundlePackageType APPL + {{- if .Languages}} + CFBundleLocalizations + + {{- range .Languages}} + {{.}} + {{- end}} + + {{- end}} LSApplicationCategoryType public.app-category.{{.Category}} LSMinimumSystemVersion 10.11 - \ No newline at end of file + From 628931dad4ce346d2704f18d61889e4b64782168 Mon Sep 17 00:00:00 2001 From: Simon Dassow Date: Wed, 20 Nov 2024 17:46:09 +0100 Subject: [PATCH 38/74] Put languages into FyneApp.toml instead --- cmd/fyne/internal/commands/package-darwin.go | 37 +------------------- cmd/fyne/internal/commands/package.go | 2 ++ internal/metadata/data.go | 1 + 3 files changed, 4 insertions(+), 36 deletions(-) diff --git a/cmd/fyne/internal/commands/package-darwin.go b/cmd/fyne/internal/commands/package-darwin.go index c62fbadf28..4b259e202a 100644 --- a/cmd/fyne/internal/commands/package-darwin.go +++ b/cmd/fyne/internal/commands/package-darwin.go @@ -1,14 +1,11 @@ package commands import ( - "errors" "fmt" "image" "image/color" - "io/fs" "os" "path/filepath" - "regexp" "strings" "fyne.io/fyne/v2/cmd/fyne/internal/templates" @@ -43,13 +40,8 @@ func (p *Packager) packageDarwin() (err error) { } }() - langs, err := findTranslationLanguages(p.srcDir) - if err != nil { - return fmt.Errorf("failed to find translation languages: %w", err) - } - tplData := darwinData{Name: p.Name, ExeName: exeName, AppID: p.AppID, Version: p.AppVersion, Build: p.AppBuild, - Category: strings.ToLower(p.category), Languages: langs} + Category: strings.ToLower(p.category), Languages: p.langs} if err := templates.InfoPlistDarwin.Execute(infoFile, tplData); err != nil { return fmt.Errorf("failed to write plist template: %w", err) } @@ -111,30 +103,3 @@ func processMacOSIcon(in image.Image) image.Image { return dc.Image() } - -func findTranslationLanguages(dir string) ([]string, error) { - langs := []string{} - re := regexp.MustCompile("(?:^|/)([a-z]{2}(?:[-][A-Z]{2})?)\\.json$") - err := filepath.Walk(dir, func(path string, fi fs.FileInfo, err error) error { - if err != nil { - if errors.Is(err, os.ErrNotExist) { - return nil - } - return err - } - - if fi.IsDir() || !fi.Mode().IsRegular() || filepath.Ext(path) != ".json" { - return nil - } - - m := re.FindStringSubmatch(path) - if len(m) < 2 { - return nil - } - - langs = append(langs, m[1]) - - return nil - }) - return langs, err -} diff --git a/cmd/fyne/internal/commands/package.go b/cmd/fyne/internal/commands/package.go index 367bd02e7e..2d1a1ae12e 100644 --- a/cmd/fyne/internal/commands/package.go +++ b/cmd/fyne/internal/commands/package.go @@ -134,6 +134,7 @@ type Packager struct { certificate, profile string // optional flags for releasing tags, category string tempDir string + langs []string customMetadata keyValueFlag linuxAndBSDMetadata *metadata.LinuxAndBSD @@ -344,6 +345,7 @@ func (p *Packager) validate() (err error) { p.appData.mergeMetadata(data) p.sourceMetadata = data.Source + p.langs = data.Languages p.linuxAndBSDMetadata = data.LinuxAndBSD } diff --git a/internal/metadata/data.go b/internal/metadata/data.go index 53f95b7e4b..99c9df3d37 100644 --- a/internal/metadata/data.go +++ b/internal/metadata/data.go @@ -8,6 +8,7 @@ type FyneApp struct { Release map[string]string `toml:",omitempty"` Source *AppSource `toml:",omitempty"` LinuxAndBSD *LinuxAndBSD `toml:",omitempty"` + Languages []string `toml:",omitempty"` } // AppDetails describes the build information, this group may be OS or arch specific From 3b8856e869757e6d7d6e14db4e1544eb15664fab Mon Sep 17 00:00:00 2001 From: Simon Dassow Date: Wed, 20 Nov 2024 17:47:58 +0100 Subject: [PATCH 39/74] Remove test again --- .../internal/commands/package-darwin_test.go | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 cmd/fyne/internal/commands/package-darwin_test.go diff --git a/cmd/fyne/internal/commands/package-darwin_test.go b/cmd/fyne/internal/commands/package-darwin_test.go deleted file mode 100644 index 1d1dc3f4f9..0000000000 --- a/cmd/fyne/internal/commands/package-darwin_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package commands - -import ( - "regexp" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestFindTranslationLanguages(t *testing.T) { - dir := t.TempDir() - - f, err := os.Create(filepath.Join(dir, "en.json")) - if err != nil { - t.Fatal(err) - } - f.Write([]byte("")) - f.Close() - - langs, err := findTranslationLanguages(dir) - if err != nil { - t.Fatal(err) - } - - assert.Equal(t, 1, len(langs)) - assert.Equal(t, "en", langs[0]) -} From c8970a818bf9b9bda691d2e4c2d379f0e7336ed4 Mon Sep 17 00:00:00 2001 From: Simon Dassow Date: Wed, 20 Nov 2024 18:06:35 +0100 Subject: [PATCH 40/74] Make sure to pass underscore as separator --- cmd/fyne/internal/commands/package-darwin.go | 10 +++++++++- cmd/fyne/internal/commands/package-darwin_test.go | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 cmd/fyne/internal/commands/package-darwin_test.go diff --git a/cmd/fyne/internal/commands/package-darwin.go b/cmd/fyne/internal/commands/package-darwin.go index 4b259e202a..5cf3feb55a 100644 --- a/cmd/fyne/internal/commands/package-darwin.go +++ b/cmd/fyne/internal/commands/package-darwin.go @@ -24,6 +24,14 @@ type darwinData struct { Languages []string } +func darwinLangs(langs []string) []string { + r := make([]string, len(langs)) + for n, lang := range langs { + r[n] = strings.Replace(lang, "-", "_", 1) + } + return r +} + func (p *Packager) packageDarwin() (err error) { appDir := util.EnsureSubDir(p.dir, p.Name+".app") exeName := filepath.Base(p.exe) @@ -41,7 +49,7 @@ func (p *Packager) packageDarwin() (err error) { }() tplData := darwinData{Name: p.Name, ExeName: exeName, AppID: p.AppID, Version: p.AppVersion, Build: p.AppBuild, - Category: strings.ToLower(p.category), Languages: p.langs} + Category: strings.ToLower(p.category), Languages: darwinLangs(p.langs)} if err := templates.InfoPlistDarwin.Execute(infoFile, tplData); err != nil { return fmt.Errorf("failed to write plist template: %w", err) } diff --git a/cmd/fyne/internal/commands/package-darwin_test.go b/cmd/fyne/internal/commands/package-darwin_test.go new file mode 100644 index 0000000000..97896ae053 --- /dev/null +++ b/cmd/fyne/internal/commands/package-darwin_test.go @@ -0,0 +1,15 @@ +package commands + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDarwinLangs(t *testing.T) { + langs := darwinLangs([]string{"en-GB", "de-CH"}) + + assert.Equal(t, 2, len(langs)) + assert.Equal(t, "en_GB", langs[0]) + assert.Equal(t, "de_CH", langs[1]) +} From 214a5e06319aea28b752f339346520d4eff0a510 Mon Sep 17 00:00:00 2001 From: Simon Dassow Date: Thu, 21 Nov 2024 17:55:51 +0100 Subject: [PATCH 41/74] Remove temporary file that accidentally slipped in --- cmd/fyne/internal/templates/bundled.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cmd/fyne/internal/templates/bundled.go b/cmd/fyne/internal/templates/bundled.go index ed47d501c7..a136ad242c 100644 --- a/cmd/fyne/internal/templates/bundled.go +++ b/cmd/fyne/internal/templates/bundled.go @@ -5,11 +5,6 @@ package templates import "fyne.io/fyne/v2" -var resourceInfoPlistSwp = &fyne.StaticResource{ - StaticName: ".Info.plist.swp", - StaticContent: []byte( - "b0VIM 9.0\x00\x00\x00\x00\x10\x00\x00s\f>g8ʽ\x03b\xcb\x00\x00simon\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00chaunax.errornet.nl\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00~simon/Code/fyne-dev/cmd/fyne/internal/templates/data/Info.plist\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00utf-8\r\x003210\x00\x00\x00\x00#\"! \x13\x12U\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tp\x01\x00\x7f\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00*\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ad\x00\x00\xba\n\x00\x00z\v\x00\x00\x00\x10\x00\x00*\x00\x00\x00\x00\x00\x00\x00\xd9\x0f\x00\x00i\x0f\x00\x00S\x0f\x00\x00L\x0f\x00\x003\x0f\x00\x00\x17\x0f\x00\x00\xf8\x0e\x00\x00\xd9\x0e\x00\x00\xba\x0e\x00\x00\x9d\x0e\x00\x00\x80\x0e\x00\x00d\x0e\x00\x00=\x0e\x00\x00\x1e\x0e\x00\x00\xf7\r\x00\x00\xee\r\x00\x00\xd4\r\x00\x00\xca\r\x00\x00\xae\r\x00\x00\x91\r\x00\x00m\r\x00\x00d\r\x00\x003\r\x00\x00*\r\x00\x00\x00\r\x00\x00\xea\f\x00\x00\xca\f\x00\x00\xb3\f\x00\x00\x9e\f\x00\x00|\f\x00\x00s\f\x00\x00Z\f\x00\x00A\f\x00\x005\f\x00\x00+\f\x00\x00 \f\x00\x00\xfa\v\x00\x00\xc6\v\x00\x00\xa3\v\x00\x00\x8b\v\x00\x00\x83\v\x00\x00z\v\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t10.11\x00\tLSMinimumSystemVersion\x00\tpublic.app-category.{{.Category}}\x00\tLSApplicationCategoryType\x00\t{{- end}}\x00\t\x00\t\t{{- end}}\x00\t\t{{.}}\x00\t\t{{- range .Languages}}\x00\t\x00\tCFBundleLocalizations\x00\t{{- if .Languages}}\x00\tAPPL\x00\tCFBundlePackageType\x00\t6.0\x00\tCFBundleInfoDictionaryVersion\x00\t\x00\tNSSupportsAutomaticGraphicsSwitching\x00\t\x00\tNSHighResolutionCapable\x00\t{{.Build}}\x00\tCFBundleVersion\x00\t\x00\t\tMacOSX\x00\t\x00\tCFBundleSupportedPlatforms\x00\t{{.Version}}\x00\tCFBundleShortVersionString\x00\ticon.icns\x00\tCFBundleIconFile\x00\t{{.AppID}}\x00\tCFBundleIdentifier\x00\t{{.ExeName}}\x00\tCFBundleExecutable\x00\t{{.Name}}\x00\tCFBundleName\x00\x00\x00\x00\x00"), -} var resourceAndroidManifestXml = &fyne.StaticResource{ StaticName: "AndroidManifest.xml", StaticContent: []byte( From 0ed4f2b4035858bca0e99ee4183c8ff07a5e4c72 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 26 Nov 2024 18:29:00 +0000 Subject: [PATCH 42/74] Be even more cautious about refreshing in icon refresh --- widget/fileicon.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/widget/fileicon.go b/widget/fileicon.go index ec93f693ef..b2249ca6f5 100644 --- a/widget/fileicon.go +++ b/widget/fileicon.go @@ -184,11 +184,6 @@ func (s *fileIconRenderer) Refresh() { s.file.setURI(s.file.URI) s.file.propertyLock.Unlock() - s.file.propertyLock.RLock() - s.img.Resource = s.file.resource - s.ext.Text = s.file.extension - s.file.propertyLock.RUnlock() - if s.file.Selected { s.background.Show() s.ext.Color = th.Color(theme.ColorNameSelection, v) @@ -203,9 +198,18 @@ func (s *fileIconRenderer) Refresh() { } } - s.img.Refresh() + s.file.propertyLock.RLock() + if s.img.Resource != s.file.resource { + s.img.Resource = s.file.resource + s.img.Refresh() + } + if s.ext.Text != s.file.extension { + s.ext.Text = s.file.extension + s.ext.Refresh() + } + s.file.propertyLock.RUnlock() + canvas.Refresh(s.file.super()) - canvas.Refresh(s.ext) } func trimmedExtension(uri fyne.URI) string { From d3dd8f770a153395309ec5bca9bbc8df94162111 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 26 Nov 2024 18:30:40 +0000 Subject: [PATCH 43/74] Reverting this forced refresh of all child images The reason was thread related and we are about to move from that problem anyway. Reverts https://github.com/fyne-io/fyne/commit/4efaa1be9593f62d6b35f1d77bfe07212fabd9d4 --- internal/driver/common/canvas.go | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/internal/driver/common/canvas.go b/internal/driver/common/canvas.go index 26e1d3ee0a..80c61ecf9a 100644 --- a/internal/driver/common/canvas.go +++ b/internal/driver/common/canvas.go @@ -336,23 +336,6 @@ func (c *Canvas) Painter() gl.Painter { // Refresh refreshes a canvas object. func (c *Canvas) Refresh(obj fyne.CanvasObject) { - walkNeeded := false - switch obj.(type) { - case *fyne.Container: - walkNeeded = true - case fyne.Widget: - walkNeeded = true - } - - if walkNeeded { - driver.WalkCompleteObjectTree(obj, func(co fyne.CanvasObject, p1, p2 fyne.Position, s fyne.Size) bool { - if i, ok := co.(*canvas.Image); ok { - i.Refresh() - } - return false - }, nil) - } - c.refreshQueue.In(obj) c.SetDirty() } From c773fba95bc6e17f135eaa851409a34248686a86 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 29 Nov 2024 19:43:52 +0000 Subject: [PATCH 44/74] Don't try and serialse menu item action --- menu.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menu.go b/menu.go index 7a3c54736b..eadf04856b 100644 --- a/menu.go +++ b/menu.go @@ -48,7 +48,7 @@ type MenuItem struct { IsQuit bool IsSeparator bool Label string - Action func() + Action func() `json:"-"` // Since: 2.1 Disabled bool // Since: 2.1 From 221d0d6808b396ae7e099ebdc42ecec65344ca41 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 29 Nov 2024 20:10:13 +0000 Subject: [PATCH 45/74] Add missing refresh calls --- widget/check.go | 2 ++ widget/radio_item.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/widget/check.go b/widget/check.go index 22cf72d465..8ecf04b702 100644 --- a/widget/check.go +++ b/widget/check.go @@ -363,7 +363,9 @@ func (c *checkRenderer) updateResource(th fyne.Theme) { bgRes.ColorName = theme.ColorNameBackground } c.icon.Resource = res + c.icon.Refresh() c.bg.Resource = bgRes + c.bg.Refresh() } // must be called while holding c.check.propertyLock for reading diff --git a/widget/radio_item.go b/widget/radio_item.go index fbcf075eb1..2aa9da110d 100644 --- a/widget/radio_item.go +++ b/widget/radio_item.go @@ -207,7 +207,9 @@ func (r *radioItemRenderer) update() { out.ColorName = theme.ColorNameDisabled } r.icon.Resource = in + r.icon.Refresh() r.over.Resource = out + r.over.Refresh() if r.item.Disabled() { r.focusIndicator.FillColor = color.Transparent From 50868440c63bcfc056dccd918ef11d626b31db77 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 28 Oct 2024 20:46:58 +0000 Subject: [PATCH 46/74] Defer to system theme when a custom theme delegates to DefaultTheme I still have to refactor the base config dir code --- theme/color_test.go | 4 ++-- theme/json.go | 8 ++++++-- theme/theme.go | 36 ++++++++++++++++++++++++++++++++++-- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/theme/color_test.go b/theme/color_test.go index d7af1f21fd..fd9a5c612f 100644 --- a/theme/color_test.go +++ b/theme/color_test.go @@ -12,12 +12,12 @@ import ( func Test_BackgroundColor(t *testing.T) { t.Run("dark theme", func(t *testing.T) { fyne.CurrentApp().Settings().SetTheme(theme.DarkTheme()) - assert.Equal(t, theme.DefaultTheme().Color(theme.ColorNameBackground, theme.VariantDark), theme.BackgroundColor(), "wrong dark theme background color") + assert.Equal(t, theme.Current().Color(theme.ColorNameBackground, theme.VariantDark), theme.BackgroundColor(), "wrong dark theme background color") }) t.Run("light theme", func(t *testing.T) { fyne.CurrentApp().Settings().SetTheme(theme.LightTheme()) bg := theme.BackgroundColor() - assert.Equal(t, theme.DefaultTheme().Color(theme.ColorNameBackground, theme.VariantLight), bg, "wrong light theme background color") + assert.Equal(t, theme.Current().Color(theme.ColorNameBackground, theme.VariantLight), bg, "wrong light theme background color") }) } diff --git a/theme/json.go b/theme/json.go index a0bbf81b67..78d26f1800 100644 --- a/theme/json.go +++ b/theme/json.go @@ -27,12 +27,16 @@ func FromJSON(data string) (fyne.Theme, error) { // // Since: 2.2 func FromJSONReader(r io.Reader) (fyne.Theme, error) { + return fromJSONWithFallback(r, DefaultTheme()) +} + +func fromJSONWithFallback(r io.Reader, fallback fyne.Theme) (fyne.Theme, error) { var th *schema if err := json.NewDecoder(r).Decode(&th); err != nil { - return DefaultTheme(), err + return fallback, err } - return &jsonTheme{data: th, fallback: DefaultTheme()}, nil + return &jsonTheme{data: th, fallback: fallback}, nil } type hexColor string diff --git a/theme/theme.go b/theme/theme.go index c977456626..681968de74 100644 --- a/theme/theme.go +++ b/theme/theme.go @@ -2,8 +2,10 @@ package theme // import "fyne.io/fyne/v2/theme" import ( + "bytes" "image/color" "os" + "path/filepath" "strings" "fyne.io/fyne/v2" @@ -24,7 +26,7 @@ const ( VariantLight = internaltheme.VariantLight ) -var defaultTheme fyne.Theme +var defaultTheme, systemTheme fyne.Theme // DarkTheme defines the built-in dark theme colors and sizes. // @@ -45,6 +47,11 @@ func DefaultTheme() fyne.Theme { defaultTheme = setupDefaultTheme() } + // check system too + if systemTheme != nil { + return systemTheme + } + return defaultTheme } @@ -376,7 +383,32 @@ func selectionColorNamed(name string) color.NRGBA { func setupDefaultTheme() fyne.Theme { theme := &builtinTheme{variant: internaltheme.VariantNameUserPreference} - theme.initFonts() + + systemTheme = setupSystemTheme(theme) + return theme } + +func setupSystemTheme(fallback fyne.Theme) fyne.Theme { + // TODO rootURI code to internal/app + home, _ := os.UserHomeDir() + root := filepath.Join(home, ".config", "fyne") + + path := filepath.Join(root, "theme.json") + data, err := fyne.LoadResourceFromPath(path) + if err != nil { + if !os.IsNotExist(err) { + fyne.LogError("Failed to load user theme file: "+path, err) + } + return nil + } + if data != nil && data.Content() != nil { + th, err := fromJSONWithFallback(bytes.NewReader(data.Content()), fallback) + if err == nil { + return th + } + fyne.LogError("Failed to parse user theme file: "+path, err) + } + return nil +} From 2d938e8d19639dfed465664e394803b673077a0a Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 6 Nov 2024 16:21:58 +0000 Subject: [PATCH 47/74] Use the app config lookup refactored from the app package --- app/app.go | 4 ++++ app/app_desktop_darwin.go | 8 -------- app/app_goxjs.go | 4 ---- app/app_mobile_and.go | 13 ------------- app/app_mobile_ios.go | 6 ------ app/app_other.go | 6 ------ app/app_windows.go | 7 ------- app/app_xdg.go | 6 ------ internal/app/config.go | 5 +++++ internal/app/config_desktop_darwin.go | 15 +++++++++++++++ internal/app/config_goxjs.go | 7 +++++++ internal/app/config_mobile_and.go | 13 +++++++++++++ internal/app/config_mobile_ios.go | 8 ++++++++ internal/app/config_other.go | 12 ++++++++++++ internal/app/config_windows.go | 10 ++++++++++ internal/app/config_xdg.go | 8 ++++++++ theme/theme.go | 5 ++--- 17 files changed, 84 insertions(+), 53 deletions(-) create mode 100644 internal/app/config.go create mode 100644 internal/app/config_desktop_darwin.go create mode 100644 internal/app/config_goxjs.go create mode 100644 internal/app/config_mobile_and.go create mode 100644 internal/app/config_mobile_ios.go create mode 100644 internal/app/config_other.go create mode 100644 internal/app/config_windows.go create mode 100644 internal/app/config_xdg.go diff --git a/app/app.go b/app/app.go index b1f1ca20b8..5108377cd9 100644 --- a/app/app.go +++ b/app/app.go @@ -167,6 +167,10 @@ func newAppWithDriver(d fyne.Driver, id string) fyne.App { return newApp } +func rootConfigDir() string { + return app.RootConfigDir() +} + // marker interface to pass system tray to supporting drivers type systrayDriver interface { SetSystemTrayMenu(*fyne.Menu) diff --git a/app/app_desktop_darwin.go b/app/app_desktop_darwin.go index 026e823d2b..01aca7c36e 100644 --- a/app/app_desktop_darwin.go +++ b/app/app_desktop_darwin.go @@ -16,7 +16,6 @@ import ( "net/url" "os" "os/exec" - "path/filepath" "fyne.io/fyne/v2" ) @@ -41,13 +40,6 @@ func (a *fyneApp) SetSystemTrayMenu(menu *fyne.Menu) { } } -func rootConfigDir() string { - homeDir, _ := os.UserHomeDir() - - desktopConfig := filepath.Join(filepath.Join(homeDir, "Library"), "Preferences") - return filepath.Join(desktopConfig, "fyne") -} - //export themeChanged func themeChanged() { fyne.CurrentApp().Settings().(*settings).setupTheme() diff --git a/app/app_goxjs.go b/app/app_goxjs.go index 08c2df7611..f3c4a49ec6 100644 --- a/app/app_goxjs.go +++ b/app/app_goxjs.go @@ -50,10 +50,6 @@ func (a *fyneApp) SendNotification(n *fyne.Notification) { } } -func rootConfigDir() string { - return "/data/" -} - var themeChanged = js.FuncOf(func(this js.Value, args []js.Value) interface{} { if len(args) > 0 && args[0].Type() == js.TypeObject { fyne.CurrentApp().Settings().(*settings).setupTheme() diff --git a/app/app_mobile_and.go b/app/app_mobile_and.go index 9abf04745c..fb1d8afa60 100644 --- a/app/app_mobile_and.go +++ b/app/app_mobile_and.go @@ -12,10 +12,7 @@ void sendNotification(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx, char */ import "C" import ( - "log" "net/url" - "os" - "path/filepath" "unsafe" "fyne.io/fyne/v2" @@ -44,13 +41,3 @@ func (a *fyneApp) SendNotification(n *fyne.Notification) { return nil }) } - -func rootConfigDir() string { - filesDir := os.Getenv("FILESDIR") - if filesDir == "" { - log.Println("FILESDIR env was not set by android native code") - return "/data/data" // probably won't work, but we can't make a better guess - } - - return filepath.Join(filesDir, "fyne") -} diff --git a/app/app_mobile_ios.go b/app/app_mobile_ios.go index 86b12d1fbb..82b8d25f49 100644 --- a/app/app_mobile_ios.go +++ b/app/app_mobile_ios.go @@ -15,15 +15,9 @@ void sendNotification(char *title, char *content); import "C" import ( "net/url" - "path/filepath" "unsafe" ) -func rootConfigDir() string { - root := C.documentsPath() - return filepath.Join(C.GoString(root), "fyne") -} - func (a *fyneApp) OpenURL(url *url.URL) error { urlStr := C.CString(url.String()) C.openURL(urlStr) diff --git a/app/app_other.go b/app/app_other.go index 83c336defd..72601c7a50 100644 --- a/app/app_other.go +++ b/app/app_other.go @@ -5,16 +5,10 @@ package app import ( "errors" "net/url" - "os" - "path/filepath" "fyne.io/fyne/v2" ) -func rootConfigDir() string { - return filepath.Join(os.TempDir(), "fyne-test") -} - func (a *fyneApp) OpenURL(_ *url.URL) error { return errors.New("Unable to open url for unknown operating system") } diff --git a/app/app_windows.go b/app/app_windows.go index e6eb30966d..ad3a6832db 100644 --- a/app/app_windows.go +++ b/app/app_windows.go @@ -29,13 +29,6 @@ $xml.LoadXml($toastXml.OuterXml) $toast = [Windows.UI.Notifications.ToastNotification]::new($xml) [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("%s").Show($toast);` -func rootConfigDir() string { - homeDir, _ := os.UserHomeDir() - - desktopConfig := filepath.Join(filepath.Join(homeDir, "AppData"), "Roaming") - return filepath.Join(desktopConfig, "fyne") -} - func (a *fyneApp) OpenURL(url *url.URL) error { cmd := exec.Command("rundll32", "url.dll,FileProtocolHandler", url.String()) cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr diff --git a/app/app_xdg.go b/app/app_xdg.go index 71aeae4142..a9068a4f87 100644 --- a/app/app_xdg.go +++ b/app/app_xdg.go @@ -6,7 +6,6 @@ import ( "net/url" "os" "os/exec" - "path/filepath" "sync/atomic" "github.com/godbus/dbus/v5" @@ -114,11 +113,6 @@ func (a *fyneApp) SetSystemTrayIcon(icon fyne.Resource) { } } -func rootConfigDir() string { - desktopConfig, _ := os.UserConfigDir() - return filepath.Join(desktopConfig, "fyne") -} - func watchTheme(s *settings) { go func() { // Theme lookup hangs on some desktops. Update theme variant cache from within goroutine. diff --git a/internal/app/config.go b/internal/app/config.go new file mode 100644 index 0000000000..bb31841f92 --- /dev/null +++ b/internal/app/config.go @@ -0,0 +1,5 @@ +package app + +func RootConfigDir() string { + return rootConfigDir() +} diff --git a/internal/app/config_desktop_darwin.go b/internal/app/config_desktop_darwin.go new file mode 100644 index 0000000000..1571b17ee2 --- /dev/null +++ b/internal/app/config_desktop_darwin.go @@ -0,0 +1,15 @@ +//go:build !ci && !ios && !wasm && !test_web_driver && !mobile + +package app + +import ( + "os" + "path/filepath" +) + +func rootConfigDir() string { + homeDir, _ := os.UserHomeDir() + + desktopConfig := filepath.Join(filepath.Join(homeDir, "Library"), "Preferences") + return filepath.Join(desktopConfig, "fyne") +} diff --git a/internal/app/config_goxjs.go b/internal/app/config_goxjs.go new file mode 100644 index 0000000000..c3441d2028 --- /dev/null +++ b/internal/app/config_goxjs.go @@ -0,0 +1,7 @@ +//go:build !ci && (!android || !ios || !mobile) && (wasm || test_web_driver) + +package app + +func rootConfigDir() string { + return "/data/" +} diff --git a/internal/app/config_mobile_and.go b/internal/app/config_mobile_and.go new file mode 100644 index 0000000000..3c85035f43 --- /dev/null +++ b/internal/app/config_mobile_and.go @@ -0,0 +1,13 @@ +//go:build !ci && android + +package app + +func rootConfigDir() string { + filesDir := os.Getenv("FILESDIR") + if filesDir == "" { + log.Println("FILESDIR env was not set by android native code") + return "/data/data" // probably won't work, but we can't make a better guess + } + + return filepath.Join(filesDir, "fyne") +} diff --git a/internal/app/config_mobile_ios.go b/internal/app/config_mobile_ios.go new file mode 100644 index 0000000000..25012f5a70 --- /dev/null +++ b/internal/app/config_mobile_ios.go @@ -0,0 +1,8 @@ +//go:build !ci && ios && !mobile + +package app + +func rootConfigDir() string { + root := C.documentsPath() + return filepath.Join(C.GoString(root), "fyne") +} diff --git a/internal/app/config_other.go b/internal/app/config_other.go new file mode 100644 index 0000000000..1b45477c2d --- /dev/null +++ b/internal/app/config_other.go @@ -0,0 +1,12 @@ +//go:build ci || (mobile && !android && !ios) || (!linux && !darwin && !windows && !freebsd && !openbsd && !netbsd && !wasm && !test_web_driver) + +package app + +import ( + "os" + "path/filepath" +) + +func rootConfigDir() string { + return filepath.Join(os.TempDir(), "fyne-test") +} diff --git a/internal/app/config_windows.go b/internal/app/config_windows.go new file mode 100644 index 0000000000..87337d893d --- /dev/null +++ b/internal/app/config_windows.go @@ -0,0 +1,10 @@ +//go:build !ci && !android && !ios && !wasm && !test_web_driver + +package app + +func rootConfigDir() string { + homeDir, _ := os.UserHomeDir() + + desktopConfig := filepath.Join(filepath.Join(homeDir, "AppData"), "Roaming") + return filepath.Join(desktopConfig, "fyne") +} diff --git a/internal/app/config_xdg.go b/internal/app/config_xdg.go new file mode 100644 index 0000000000..ac269f91fc --- /dev/null +++ b/internal/app/config_xdg.go @@ -0,0 +1,8 @@ +//go:build !ci && !wasm && !test_web_driver && !android && !ios && !mobile && (linux || openbsd || freebsd || netbsd) + +package app + +func rootConfigDir() string { + desktopConfig, _ := os.UserConfigDir() + return filepath.Join(desktopConfig, "fyne") +} diff --git a/theme/theme.go b/theme/theme.go index 681968de74..f59b4b8d9d 100644 --- a/theme/theme.go +++ b/theme/theme.go @@ -9,6 +9,7 @@ import ( "strings" "fyne.io/fyne/v2" + internalApp "fyne.io/fyne/v2/internal/app" "fyne.io/fyne/v2/internal/cache" internaltheme "fyne.io/fyne/v2/internal/theme" ) @@ -391,9 +392,7 @@ func setupDefaultTheme() fyne.Theme { } func setupSystemTheme(fallback fyne.Theme) fyne.Theme { - // TODO rootURI code to internal/app - home, _ := os.UserHomeDir() - root := filepath.Join(home, ".config", "fyne") + root := internalApp.RootConfigDir() path := filepath.Join(root, "theme.json") data, err := fyne.LoadResourceFromPath(path) From a60cfb9b6419fb83a8f9a0bee88e64b4f5564dde Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 6 Nov 2024 16:22:17 +0000 Subject: [PATCH 48/74] Avoid import loop, the imports were not really right anyway --- internal/app/theme_darwin.go | 2 +- internal/app/theme_other.go | 2 +- internal/app/theme_wasm.go | 2 +- internal/app/theme_web.go | 2 +- internal/app/theme_windows.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/app/theme_darwin.go b/internal/app/theme_darwin.go index f01ea3ace3..a74eb2ec49 100644 --- a/internal/app/theme_darwin.go +++ b/internal/app/theme_darwin.go @@ -13,7 +13,7 @@ bool isDarkMode(); import "C" import ( "fyne.io/fyne/v2" - "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/internal/theme" ) // DefaultVariant returns the systems default fyne.ThemeVariant. diff --git a/internal/app/theme_other.go b/internal/app/theme_other.go index f2d69da073..9a6cc4d05c 100644 --- a/internal/app/theme_other.go +++ b/internal/app/theme_other.go @@ -4,7 +4,7 @@ package app import ( "fyne.io/fyne/v2" - "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/internal/theme" ) // DefaultVariant returns the systems default fyne.ThemeVariant. diff --git a/internal/app/theme_wasm.go b/internal/app/theme_wasm.go index 427ae3fec6..ca00087285 100644 --- a/internal/app/theme_wasm.go +++ b/internal/app/theme_wasm.go @@ -6,7 +6,7 @@ import ( "syscall/js" "fyne.io/fyne/v2" - "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/internal/theme" ) // DefaultVariant returns the systems default fyne.ThemeVariant. diff --git a/internal/app/theme_web.go b/internal/app/theme_web.go index 310317c0c6..fcb86608f3 100644 --- a/internal/app/theme_web.go +++ b/internal/app/theme_web.go @@ -4,7 +4,7 @@ package app import ( "fyne.io/fyne/v2" - "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/internal/theme" ) // DefaultVariant returns the systems default fyne.ThemeVariant. diff --git a/internal/app/theme_windows.go b/internal/app/theme_windows.go index 7bb906bc7b..83a20e2dc1 100644 --- a/internal/app/theme_windows.go +++ b/internal/app/theme_windows.go @@ -8,7 +8,7 @@ import ( "golang.org/x/sys/windows/registry" "fyne.io/fyne/v2" - "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/internal/theme" ) const themeRegKey = `SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize` From 3fe9c6de13e64c8d16ebe880514cb5d11530dd6a Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 6 Nov 2024 16:39:51 +0000 Subject: [PATCH 49/74] Fix missing imports --- internal/app/config_mobile_and.go | 5 +++++ internal/app/config_mobile_ios.go | 4 ++++ internal/app/config_windows.go | 5 +++++ internal/app/config_xdg.go | 5 +++++ 4 files changed, 19 insertions(+) diff --git a/internal/app/config_mobile_and.go b/internal/app/config_mobile_and.go index 3c85035f43..1faff5a3d9 100644 --- a/internal/app/config_mobile_and.go +++ b/internal/app/config_mobile_and.go @@ -2,6 +2,11 @@ package app +import ( + "os" + "path/filepath" +) + func rootConfigDir() string { filesDir := os.Getenv("FILESDIR") if filesDir == "" { diff --git a/internal/app/config_mobile_ios.go b/internal/app/config_mobile_ios.go index 25012f5a70..193063e76b 100644 --- a/internal/app/config_mobile_ios.go +++ b/internal/app/config_mobile_ios.go @@ -2,6 +2,10 @@ package app +import ( + "path/filepath" +) + func rootConfigDir() string { root := C.documentsPath() return filepath.Join(C.GoString(root), "fyne") diff --git a/internal/app/config_windows.go b/internal/app/config_windows.go index 87337d893d..c0960036e9 100644 --- a/internal/app/config_windows.go +++ b/internal/app/config_windows.go @@ -2,6 +2,11 @@ package app +import ( + "os" + "path/filepath" +) + func rootConfigDir() string { homeDir, _ := os.UserHomeDir() diff --git a/internal/app/config_xdg.go b/internal/app/config_xdg.go index ac269f91fc..3a04463c21 100644 --- a/internal/app/config_xdg.go +++ b/internal/app/config_xdg.go @@ -2,6 +2,11 @@ package app +import ( + "os" + "path/filepath" +) + func rootConfigDir() string { desktopConfig, _ := os.UserConfigDir() return filepath.Join(desktopConfig, "fyne") From b4f724ccfcad327ca86034f2ab3019adb0b9f0c9 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 6 Nov 2024 16:50:12 +0000 Subject: [PATCH 50/74] Android compile fix... --- internal/app/config_mobile_and.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/app/config_mobile_and.go b/internal/app/config_mobile_and.go index 1faff5a3d9..9c3d855b80 100644 --- a/internal/app/config_mobile_and.go +++ b/internal/app/config_mobile_and.go @@ -3,6 +3,7 @@ package app import ( + "log" "os" "path/filepath" ) From b9828ad8adf1ffe367660c97ccf505da4b764d08 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Thu, 28 Nov 2024 09:49:27 +0000 Subject: [PATCH 51/74] Remove redundant method --- app/app.go | 4 ---- app/app_mobile_and_test.go | 4 +++- app/preferences_android.go | 8 ++++++-- app/preferences_ios.go | 4 +++- app/preferences_mobile.go | 8 ++++++-- app/preferences_other.go | 8 ++++++-- app/settings.go | 8 ++++---- 7 files changed, 28 insertions(+), 16 deletions(-) diff --git a/app/app.go b/app/app.go index 5108377cd9..b1f1ca20b8 100644 --- a/app/app.go +++ b/app/app.go @@ -167,10 +167,6 @@ func newAppWithDriver(d fyne.Driver, id string) fyne.App { return newApp } -func rootConfigDir() string { - return app.RootConfigDir() -} - // marker interface to pass system tray to supporting drivers type systrayDriver interface { SetSystemTrayMenu(*fyne.Menu) diff --git a/app/app_mobile_and_test.go b/app/app_mobile_and_test.go index da0244cb1f..408446dadc 100644 --- a/app/app_mobile_and_test.go +++ b/app/app_mobile_and_test.go @@ -6,6 +6,8 @@ import ( "os" "testing" + "fyne.io/fyne/v2/internal/app" + "github.com/stretchr/testify/assert" ) @@ -13,7 +15,7 @@ func Test_RootConfigDir(t *testing.T) { oldEnv := os.Getenv("FILESPATH") os.Setenv("FILESPATH", "/tmp") - assert.Equal(t, "/tmp", rootConfigDir()) + assert.Equal(t, "/tmp", app.RootConfigDir()) os.Setenv("FILESPATH", oldEnv) } diff --git a/app/preferences_android.go b/app/preferences_android.go index a2e7e47fbf..ac9f834cd7 100644 --- a/app/preferences_android.go +++ b/app/preferences_android.go @@ -2,7 +2,11 @@ package app -import "path/filepath" +import ( + "path/filepath" + + "fyne.io/fyne/v2/internal/app" +) // storagePath returns the location of the settings storage func (p *preferences) storagePath() string { @@ -12,7 +16,7 @@ func (p *preferences) storagePath() string { // storageRoot returns the location of the app storage func (a *fyneApp) storageRoot() string { - return rootConfigDir() // we are in a sandbox, so no app ID added to this path + return app.RootConfigDir() // we are in a sandbox, so no app ID added to this path } func (p *preferences) watch() { diff --git a/app/preferences_ios.go b/app/preferences_ios.go index 8720dcf331..b861eec475 100644 --- a/app/preferences_ios.go +++ b/app/preferences_ios.go @@ -4,6 +4,8 @@ package app import ( "path/filepath" + + "fyne.io/fyne/v2/internal/app" ) import "C" @@ -15,7 +17,7 @@ func (p *preferences) storagePath() string { // storageRoot returns the location of the app storage func (a *fyneApp) storageRoot() string { - return rootConfigDir() // we are in a sandbox, so no app ID added to this path + return app.RootConfigDir() // we are in a sandbox, so no app ID added to this path } func (p *preferences) watch() { diff --git a/app/preferences_mobile.go b/app/preferences_mobile.go index 0faa122672..1fd186f945 100644 --- a/app/preferences_mobile.go +++ b/app/preferences_mobile.go @@ -2,7 +2,11 @@ package app -import "path/filepath" +import ( + "path/filepath" + + "fyne.io/fyne/v2/internal/app" +) // storagePath returns the location of the settings storage func (p *preferences) storagePath() string { @@ -11,7 +15,7 @@ func (p *preferences) storagePath() string { // storageRoot returns the location of the app storage func (a *fyneApp) storageRoot() string { - return filepath.Join(rootConfigDir(), a.UniqueID()) + return filepath.Join(app.RootConfigDir(), a.UniqueID()) } func (p *preferences) watch() { diff --git a/app/preferences_other.go b/app/preferences_other.go index 472253747c..3e698c2c0a 100644 --- a/app/preferences_other.go +++ b/app/preferences_other.go @@ -2,7 +2,11 @@ package app -import "path/filepath" +import ( + "path/filepath" + + "fyne.io/fyne/v2/internal/app" +) // storagePath returns the location of the settings storage func (p *preferences) storagePath() string { @@ -11,7 +15,7 @@ func (p *preferences) storagePath() string { // storageRoot returns the location of the app storage func (a *fyneApp) storageRoot() string { - return filepath.Join(rootConfigDir(), a.UniqueID()) + return filepath.Join(app.RootConfigDir(), a.UniqueID()) } func (p *preferences) watch() { diff --git a/app/settings.go b/app/settings.go index fe92bc18c2..dc0cbf20c3 100644 --- a/app/settings.go +++ b/app/settings.go @@ -7,7 +7,7 @@ import ( "sync" "fyne.io/fyne/v2" - internalapp "fyne.io/fyne/v2/internal/app" + "fyne.io/fyne/v2/internal/app" "fyne.io/fyne/v2/internal/build" "fyne.io/fyne/v2/theme" ) @@ -27,7 +27,7 @@ type SettingsSchema struct { // StoragePath returns the location of the settings storage func (sc *SettingsSchema) StoragePath() string { - return filepath.Join(rootConfigDir(), "settings.json") + return filepath.Join(app.RootConfigDir(), "settings.json") } // Declare conformity with Settings interface @@ -136,7 +136,7 @@ func (s *settings) fileChanged() { } func (s *settings) loadSystemTheme() fyne.Theme { - path := filepath.Join(rootConfigDir(), "theme.json") + path := filepath.Join(app.RootConfigDir(), "theme.json") data, err := fyne.LoadResourceFromPath(path) if err != nil { if !os.IsNotExist(err) { @@ -160,7 +160,7 @@ func (s *settings) setupTheme() { name = env } - variant := internalapp.DefaultVariant() + variant := app.DefaultVariant() effectiveTheme := s.theme if !s.themeSpecified { effectiveTheme = s.loadSystemTheme() From bbf0196fd88f16ca3f1a31dbaa0b327c62c2beb1 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sat, 30 Nov 2024 22:07:14 +0000 Subject: [PATCH 52/74] Don't continue refreshing images when there is no content to render --- canvas/image.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/canvas/image.go b/canvas/image.go index 074bb6574d..e0dd7e13ff 100644 --- a/canvas/image.go +++ b/canvas/image.go @@ -100,7 +100,9 @@ func (i *Image) Hide() { // MinSize returns the specified minimum size, if set, or {1, 1} otherwise. func (i *Image) MinSize() fyne.Size { if i.Image == nil || i.aspect == 0 { - i.Refresh() + if i.File != "" || i.Resource != nil { + i.Refresh() + } } return i.baseObject.MinSize() } @@ -134,6 +136,8 @@ func (i *Image) Refresh() { return } rc = io.NopCloser(r) + } else { + return } if i.File != "" || i.Resource != nil { From be74082a263eea56800ad749f1f6bf34b2424291 Mon Sep 17 00:00:00 2001 From: ErikKalkoken Date: Wed, 20 Nov 2024 18:44:20 +0100 Subject: [PATCH 53/74] Add menu shortcut texts for windows & linux --- widget/menu_item.go | 65 +++---- widget/menu_item_darwin.go | 34 +++- widget/menu_item_other.go | 34 +++- .../menu/desktop/layout_shortcuts_other.png | Bin 15676 -> 25729 bytes .../menu/desktop/layout_shortcuts_other.xml | 172 ++++++++++-------- .../layout_shortcuts_other_theme_changed.png | Bin 13614 -> 21890 bytes 6 files changed, 181 insertions(+), 124 deletions(-) diff --git a/widget/menu_item.go b/widget/menu_item.go index eb70a3faf7..03f461291f 100644 --- a/widget/menu_item.go +++ b/widget/menu_item.go @@ -11,32 +11,6 @@ import ( "fyne.io/fyne/v2/theme" ) -// TODO - -const ( - runeModifierAlt = '⌥' - runeModifierControl = '⌃' - runeModifierShift = '⇧' -) - -var keySymbols = map[fyne.KeyName]rune{ - fyne.KeyBackspace: '⌫', - fyne.KeyDelete: '⌦', - fyne.KeyDown: '↓', - fyne.KeyEnd: '↘', - fyne.KeyEnter: '↩', - fyne.KeyEscape: '⎋', - fyne.KeyHome: '↖', - fyne.KeyLeft: '←', - fyne.KeyPageDown: '⇟', - fyne.KeyPageUp: '⇞', - fyne.KeyReturn: '↩', - fyne.KeyRight: '→', - fyne.KeySpace: '␣', - fyne.KeyTab: '⇥', - fyne.KeyUp: '↑', -} - var _ fyne.Widget = (*menuItem)(nil) // menuItem is a widget for displaying a fyne.menuItem. @@ -392,33 +366,38 @@ func shortcutColor(th fyne.Theme) color.Color { return color.NRGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: uint8(a)} } -func textsForShortcut(s fyne.KeyboardShortcut, th fyne.Theme) (texts []*canvas.Text) { +func textsForShortcut(sc fyne.KeyboardShortcut, th fyne.Theme) (texts []*canvas.Text) { + // add modifier b := strings.Builder{} - mods := s.Mod() + mods := sc.Mod() if mods&fyne.KeyModifierControl != 0 { - b.WriteRune(runeModifierControl) + b.WriteString(textModifierControl) } if mods&fyne.KeyModifierAlt != 0 { - b.WriteRune(runeModifierAlt) + b.WriteString(textModifierAlt) } if mods&fyne.KeyModifierShift != 0 { - b.WriteRune(runeModifierShift) + b.WriteString(textModifierShift) } if mods&fyne.KeyModifierSuper != 0 { - b.WriteRune(runeModifierSuper) - } - r := keySymbols[s.Key()] - if r != 0 { - b.WriteRune(r) + b.WriteString(textModifierSuper) } shortColor := shortcutColor(th) - t := canvas.NewText(b.String(), shortColor) - t.TextStyle.Symbol = true + if b.Len() > 0 { + t := canvas.NewText(b.String(), shortColor) + t.TextStyle = styleModifiers + texts = append(texts, t) + } + // add key + style := defaultStyleKeys + s, ok := keyTexts[sc.Key()] + if !ok { + s = string(sc.Key()) + } else if len(s) == 1 { + style = fyne.TextStyle{Symbol: true} + } + t := canvas.NewText(s, shortColor) + t.TextStyle = style texts = append(texts, t) - if r == 0 { - text := canvas.NewText(string(s.Key()), shortColor) - text.TextStyle.Monospace = true - texts = append(texts, text) - } return } diff --git a/widget/menu_item_darwin.go b/widget/menu_item_darwin.go index eb10eba3d3..099d2fb1d7 100644 --- a/widget/menu_item_darwin.go +++ b/widget/menu_item_darwin.go @@ -1,3 +1,35 @@ package widget -const runeModifierSuper = '⌘' +import ( + "fyne.io/fyne/v2" +) + +const ( + textModifierAlt = "⌥" + textModifierControl = "⌃" + textModifierShift = "⇧" + textModifierSuper = "⌘" +) + +var ( + styleModifiers = fyne.TextStyle{Symbol: true} + defaultStyleKeys = fyne.TextStyle{Monospace: true} +) + +var keyTexts = map[fyne.KeyName]string{ + fyne.KeyBackspace: "⌫", + fyne.KeyDelete: "⌦", + fyne.KeyDown: "↓", + fyne.KeyEnd: "↘", + fyne.KeyEnter: "↩", + fyne.KeyEscape: "⎋", + fyne.KeyHome: "↖", + fyne.KeyLeft: "←", + fyne.KeyPageDown: "⇟", + fyne.KeyPageUp: "⇞", + fyne.KeyReturn: "↩", + fyne.KeyRight: "→", + fyne.KeySpace: "␣", + fyne.KeyTab: "⇥", + fyne.KeyUp: "↑", +} diff --git a/widget/menu_item_other.go b/widget/menu_item_other.go index 6c8f55220e..6a63f64201 100644 --- a/widget/menu_item_other.go +++ b/widget/menu_item_other.go @@ -2,4 +2,36 @@ package widget -const runeModifierSuper = '❖' +import ( + "fyne.io/fyne/v2" +) + +const ( + textModifierAlt = "Alt+" + textModifierControl = "Ctrl+" + textModifierShift = "Shift+" + textModifierSuper = "Super+" +) + +var ( + styleModifiers = fyne.TextStyle{} + defaultStyleKeys = fyne.TextStyle{} +) + +var keyTexts = map[fyne.KeyName]string{ + fyne.KeyBackspace: "Backspace", + fyne.KeyDelete: "Del", + fyne.KeyDown: "↓", + fyne.KeyEnd: "End", + fyne.KeyEnter: "Enter", + fyne.KeyEscape: "Esc", + fyne.KeyHome: "Home", + fyne.KeyLeft: "←", + fyne.KeyPageDown: "PgDn", + fyne.KeyPageUp: "PgUp", + fyne.KeyReturn: "Return", + fyne.KeyRight: "→", + fyne.KeySpace: "Space", + fyne.KeyTab: "Tab", + fyne.KeyUp: "↑", +} diff --git a/widget/testdata/menu/desktop/layout_shortcuts_other.png b/widget/testdata/menu/desktop/layout_shortcuts_other.png index a48ebd8ae8b381cebcc9686b0783905f39493a66..a193ca199e0c3aaaf72bb37e1970d2fa3b575d44 100644 GIT binary patch literal 25729 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 Ty^!$LB0KbS3@;XGJ4F5uFRZ5d literal 15676 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Ë`NFNxjtV5zG!D&M=oNXhG;M?-#+=HK^Q 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 - - + + - - - - - + + + + + - - - - - - a - - A - - - b - ⌥⌫ - - - c - ❖⌦ - - - d - ⇧↓ - - - e - ⌃⌥↘ - - - f - ⌃❖↩ - - - g - ⌃⇧⎋ - - - h - ⌥❖↖ - - - i - ⌥⇧← - - - j - ⇧❖⇟ - - - k - ⌃⌥❖⇞ - - - l - ⌃⌥⇧↩ - - - m - ⌃⇧❖→ - - + + + + + + a + Ctrl+ + A + + + b + Alt+ + Backspace + + + c + Super+ + Del + + + d + Shift+ + + + + e + Ctrl+Alt+ + End + + + f + Ctrl+Super+ + Enter + + + g + Ctrl+Shift+ + Esc + + + h + Alt+Super+ + Home + + + i + Alt+Shift+ + + + + j + Shift+Super+ + PgDn + + + k + Ctrl+Alt+Super+ + PgUp + + + l + Ctrl+Alt+Shift+ + Return + + + m + Ctrl+Shift+Super+ + + + n - ⌃⌥⇧❖␣ - - - o - ⌃⇥ - - - p - ⌃↑ - - - q - - F6 + Ctrl+Alt+Shift+Super+ + Space + + + o + Ctrl+ + Tab + + + p + Ctrl+ + + + + q + F6 diff --git a/widget/testdata/menu/desktop/layout_shortcuts_other_theme_changed.png b/widget/testdata/menu/desktop/layout_shortcuts_other_theme_changed.png index 9c34e6381f9bf580e4dc5a0aa1f0dd779055bda7..5afbe5cb47dacd678ff82bc00a015d08bb8a99a2 100644 GIT binary patch literal 21890 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

D#Rkqmy;(e9$>&xVeUlzIooFC1GEygRLPa6 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 From 1799fe66fe305b921a063e6d8a1264e56a1c9be5 Mon Sep 17 00:00:00 2001 From: ErikKalkoken Date: Wed, 27 Nov 2024 19:35:19 +0100 Subject: [PATCH 54/74] Adjust window size in menu shortcut test --- widget/menu_desktop_test.go | 2 +- .../menu/desktop/layout_shortcuts_other.png | Bin 25729 -> 27370 bytes .../menu/desktop/layout_shortcuts_other.xml | 6 +++--- .../layout_shortcuts_other_theme_changed.png | Bin 21890 -> 23356 bytes 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/widget/menu_desktop_test.go b/widget/menu_desktop_test.go index ded09605e8..cae51e8dce 100644 --- a/widget/menu_desktop_test.go +++ b/widget/menu_desktop_test.go @@ -162,7 +162,7 @@ func TestMenu_Layout(t *testing.T) { want: "menu/desktop/layout_window_too_short.xml", }, "menu with shortcuts": { - windowSize: fyne.NewSize(300, 800), + windowSize: fyne.NewSize(450, 800), menuPos: fyne.NewPos(10, 10), mousePositions: []fyne.Position{ fyne.NewPos(32, 143), // open submenu diff --git a/widget/testdata/menu/desktop/layout_shortcuts_other.png b/widget/testdata/menu/desktop/layout_shortcuts_other.png index a193ca199e0c3aaaf72bb37e1970d2fa3b575d44..7b6a4b04c575cb828e6756dc51b394391755cc9c 100644 GIT binary patch literal 27370 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 literal 25729 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 Ty^!$LB0KbS3@;XGJ4F5uFRZ5d diff --git a/widget/testdata/menu/desktop/layout_shortcuts_other.xml b/widget/testdata/menu/desktop/layout_shortcuts_other.xml index 59f7d65887..5b4e068f42 100644 --- a/widget/testdata/menu/desktop/layout_shortcuts_other.xml +++ b/widget/testdata/menu/desktop/layout_shortcuts_other.xml @@ -1,9 +1,9 @@ - + - + - + diff --git a/widget/testdata/menu/desktop/layout_shortcuts_other_theme_changed.png b/widget/testdata/menu/desktop/layout_shortcuts_other_theme_changed.png index 5afbe5cb47dacd678ff82bc00a015d08bb8a99a2..2956fc2ffa1c685e37275e9b346e54f6ff199fe0 100644 GIT binary patch literal 23356 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 literal 21890 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

D#Rkqmy;(e9$>&xVeUlzIooFC1GEygRLPa6 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 Date: Mon, 2 Dec 2024 17:52:32 +0000 Subject: [PATCH 55/74] Reflecting new coverage baseline from develop --- .github/workflows/static_analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index 8c2cb77a78..3541a93653 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -53,7 +53,7 @@ jobs: set -e xvfb-run go test -covermode=atomic -coverprofile=coverage.out ./... coverage=`go tool cover -func coverage.out | grep total | tr -s '\t' | cut -f 3 | grep -o '[^%]*'` - if (( $(echo "$coverage < 63" | bc) )); then echo "Test coverage lowered"; exit 1; fi + if (( $(echo "$coverage < 60" | bc) )); then echo "Test coverage lowered"; exit 1; fi - name: Update PR Coverage uses: shogo82148/actions-goveralls@v1 From 96f2ae1a90ad7b848f8f775058c485e0679144ba Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 2 Dec 2024 19:52:18 +0000 Subject: [PATCH 56/74] Preparing v2.5.3 --- CHANGELOG.md | 23 +++++++++++++++++++++++ cmd/fyne_demo/FyneApp.toml | 2 +- cmd/hello/FyneApp.toml | 2 +- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f3f63a0c7..28b5e9f538 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,29 @@ 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.3 - 6 November 2024 + +### Changed + +* Smoothly decelerate scroll on mobile +* Added Spanish translation + +### Fixed + +* Starting location can be wrong in file dialogs with custom repository (#5200) +* Improve how shortcut special keys for menu items are rendered on Windows and Linux (#5108) +* Blank page in Chrome for Android +* Mobile Entry: cursor arrows don't work (#5258) +* FileDialog does not handle relative file URIs well. (#5234) +* [Linux] Only change variant when color scheme changes +* [Linux] Window with list flickers in Wayland (#5133) +* Package command fails on OpenBSD (#5195) +* System theme fallback is not working with custom themes +* Performance regression when scrolling inside the file dialog (#4307) +* Empty but visible images consume high CPU on 2.4.x (#4345) +* Improved performance of text render caching + + ## 2.5.2 - 15 October 2024 ### Fixed diff --git a/cmd/fyne_demo/FyneApp.toml b/cmd/fyne_demo/FyneApp.toml index 672030555b..8ca47fa14c 100644 --- a/cmd/fyne_demo/FyneApp.toml +++ b/cmd/fyne_demo/FyneApp.toml @@ -2,7 +2,7 @@ Icon = "../../theme/icons/fyne.png" Name = "Fyne Demo" ID = "io.fyne.demo" - Version = "2.4.0" + Version = "2.5.3" Build = 11 [Source] diff --git a/cmd/hello/FyneApp.toml b/cmd/hello/FyneApp.toml index 12bf58730b..d9266e7b17 100644 --- a/cmd/hello/FyneApp.toml +++ b/cmd/hello/FyneApp.toml @@ -2,5 +2,5 @@ Icon = "../../theme/icons/fyne.png" Name = "Fyne Hello" ID = "io.fyne.hello" - Version = "2.4.0" + Version = "2.5.3" Build = 2 From 21050204981d0ed332567e63d6d018a6eb202fa9 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 18 Oct 2024 20:06:00 +0100 Subject: [PATCH 57/74] Cache text texture per-canvas so it matches the OpenGL context --- internal/cache/text.go | 3 ++- internal/painter/gl/texture.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/cache/text.go b/internal/cache/text.go index 84381fafac..17f546c2a7 100644 --- a/internal/cache/text.go +++ b/internal/cache/text.go @@ -29,7 +29,8 @@ type fontSizeEntry struct { type FontCacheEntry struct { fontSizeEntry - Color color.Color + Canvas fyne.Canvas + Color color.Color } // GetFontMetrics looks up a calculated size and baseline required for the specified text parameters. diff --git a/internal/painter/gl/texture.go b/internal/painter/gl/texture.go index 79dedf704b..5cfda8beba 100644 --- a/internal/painter/gl/texture.go +++ b/internal/painter/gl/texture.go @@ -38,7 +38,7 @@ func (p *painter) getTexture(object fyne.CanvasObject, creator func(canvasObject if t.FontSource != nil { custom = t.FontSource.Name() } - ent := cache.FontCacheEntry{Color: t.Color} + ent := cache.FontCacheEntry{Color: t.Color, Canvas: p.canvas} ent.Text = t.Text ent.Size = t.TextSize ent.Style = t.TextStyle From b1a2077671e5e2489b11386f77c8cce549b4319d Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 2 Dec 2024 20:09:53 +0000 Subject: [PATCH 58/74] Fixing the date - oops --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28b5e9f538..e684c6d0dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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.3 - 6 November 2024 +## 2.5.3 - 6 December 2024 ### Changed From 3afb077aacc04c0856d4c17dbfdd4c220799ac30 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 2 Dec 2024 21:33:25 +0000 Subject: [PATCH 59/74] Parent should not be exposed in the menu items --- widget/menu_item.go | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/widget/menu_item.go b/widget/menu_item.go index 03f461291f..9eecdd082a 100644 --- a/widget/menu_item.go +++ b/widget/menu_item.go @@ -16,16 +16,15 @@ var _ fyne.Widget = (*menuItem)(nil) // menuItem is a widget for displaying a fyne.menuItem. type menuItem struct { widget.Base - Item *fyne.MenuItem - Parent *Menu + Item *fyne.MenuItem - alignment fyne.TextAlign - child *Menu + alignment fyne.TextAlign + child, parent *Menu } // newMenuItem creates a new menuItem. func newMenuItem(item *fyne.MenuItem, parent *Menu) *menuItem { - i := &menuItem{Item: item, Parent: parent} + i := &menuItem{Item: item, parent: parent} i.alignment = parent.alignment i.ExtendBaseWidget(i) return i @@ -35,7 +34,7 @@ func (i *menuItem) Child() *Menu { if i.Item.ChildMenu != nil && i.child == nil { child := NewMenu(i.Item.ChildMenu) child.Hide() - child.OnDismiss = i.Parent.Dismiss + child.OnDismiss = i.parent.Dismiss i.child = child } return i.child @@ -45,7 +44,7 @@ func (i *menuItem) Child() *Menu { // // Implements: fyne.Widget func (i *menuItem) CreateRenderer() fyne.WidgetRenderer { - th := i.Parent.Theme() + th := i.parent.Theme() v := fyne.CurrentApp().Settings().ThemeVariant() background := canvas.NewRectangle(th.Color(theme.ColorNameHover, v)) @@ -139,7 +138,7 @@ func (i *menuItem) activate() { if i.Child() != nil { i.Child().Show() } - i.Parent.activateItem(i) + i.parent.activateItem(i) } func (i *menuItem) activateLastSubmenu() bool { @@ -158,7 +157,7 @@ func (i *menuItem) deactivate() { if i.Child() != nil { i.Child().Hide() } - i.Parent.DeactivateChild() + i.parent.DeactivateChild() } func (i *menuItem) deactivateLastSubmenu() bool { @@ -173,7 +172,7 @@ func (i *menuItem) deactivateLastSubmenu() bool { } func (i *menuItem) isActive() bool { - return i.Parent.activeItem == i + return i.parent.activeItem == i } func (i *menuItem) isSubmenuOpen() bool { @@ -181,7 +180,7 @@ func (i *menuItem) isSubmenuOpen() bool { } func (i *menuItem) trigger() { - i.Parent.Dismiss() + i.parent.Dismiss() if i.Item.Action != nil { i.Item.Action() } @@ -209,7 +208,7 @@ type menuItemRenderer struct { } func (r *menuItemRenderer) Layout(size fyne.Size) { - th := r.i.Parent.Theme() + th := r.i.parent.Theme() innerPad := th.Size(theme.SizeNameInnerPadding) inlineIcon := th.Size(theme.SizeNameInlineIcon) @@ -258,7 +257,7 @@ func (r *menuItemRenderer) MinSize() fyne.Size { return r.minSize } - th := r.i.Parent.Theme() + th := r.i.parent.Theme() innerPad := th.Size(theme.SizeNameInnerPadding) inlineIcon := th.Size(theme.SizeNameInlineIcon) innerPad2 := innerPad * 2 @@ -282,7 +281,7 @@ func (r *menuItemRenderer) MinSize() fyne.Size { } func (r *menuItemRenderer) updateVisuals() { - th := r.i.Parent.Theme() + th := r.i.parent.Theme() v := fyne.CurrentApp().Settings().ThemeVariant() r.background.CornerRadius = th.Size(theme.SizeNameSelectionRadius) if fyne.CurrentDevice().IsMobile() { @@ -316,14 +315,14 @@ func (r *menuItemRenderer) Refresh() { } func (r *menuItemRenderer) checkSpace() float32 { - if r.i.Parent.containsCheck { + if r.i.parent.containsCheck { return theme.IconInlineSize() + theme.InnerPadding() } return 0 } func (r *menuItemRenderer) minSizeUnchanged() bool { - th := r.i.Parent.Theme() + th := r.i.parent.Theme() return !r.minSize.IsZero() && r.text.TextSize == th.Size(theme.SizeNameText) && @@ -343,7 +342,7 @@ func (r *menuItemRenderer) updateIcon(img *canvas.Image, rsc fyne.Resource) { } func (r *menuItemRenderer) refreshText(text *canvas.Text, shortcut bool) { - th := r.i.Parent.Theme() + th := r.i.parent.Theme() v := fyne.CurrentApp().Settings().ThemeVariant() text.TextSize = th.Size(theme.SizeNameText) From 32ae8e2a4189ebf377a733c721ddea84f1f20e79 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 4 Dec 2024 10:53:41 +0000 Subject: [PATCH 60/74] Don't crash if ColorDialog is resized before show Fixes #5236 --- dialog/base.go | 4 +++- dialog/color_test.go | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/dialog/base.go b/dialog/base.go index 3105d36ba8..af713e9011 100644 --- a/dialog/base.go +++ b/dialog/base.go @@ -77,7 +77,9 @@ func (d *dialog) Refresh() { // Resize dialog, call this function after dialog show func (d *dialog) Resize(size fyne.Size) { d.desiredSize = size - d.win.Resize(size) + if d.win != nil { // could be called before popup is created! + d.win.Resize(size) + } } // SetDismissText allows custom text to be set in the dismiss button diff --git a/dialog/color_test.go b/dialog/color_test.go index b60eed07a5..7641c5d669 100644 --- a/dialog/color_test.go +++ b/dialog/color_test.go @@ -12,6 +12,24 @@ import ( "github.com/stretchr/testify/assert" ) +func TestColorDialog_Resize(t *testing.T) { + test.NewTempApp(t) + + w := test.NewTempWindow(t, canvas.NewRectangle(color.Transparent)) + w.Resize(fyne.NewSize(1000, 800)) + + d := NewColorPicker("Color Picker", "Pick a Color", nil, w) + d.Resize(fyne.NewSize(800, 600)) + d.Show() + + size := d.dialog.content.Size() + + d.Resize(fyne.NewSize(900, 600)) + newSize := d.dialog.content.Size() + + assert.Equal(t, float32(100), newSize.Width-size.Width) +} + func TestColorDialog_Theme(t *testing.T) { test.NewTempApp(t) From fc4fbfb45e404f905176951a646d78916b0538aa Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 4 Dec 2024 17:58:30 +0000 Subject: [PATCH 61/74] Fix issue where we would not be able to "empty" an image once it had content --- canvas/image.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/canvas/image.go b/canvas/image.go index e0dd7e13ff..fce634b5c7 100644 --- a/canvas/image.go +++ b/canvas/image.go @@ -136,8 +136,6 @@ func (i *Image) Refresh() { return } rc = io.NopCloser(r) - } else { - return } if i.File != "" || i.Resource != nil { From 771e15ecb896e574586d04a61c82bc454e6c8d81 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 4 Dec 2024 17:59:39 +0000 Subject: [PATCH 62/74] Updating for an rc2 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e684c6d0dd..93ca1a9d66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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.3 - 6 December 2024 +## 2.5.3 - 8 December 2024 ### Changed From bbaeb3244c140bd090841fb52819844aba17f54d Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 4 Dec 2024 14:36:41 +0000 Subject: [PATCH 63/74] Enable alpha blend of images in software painter --- internal/painter/software/draw.go | 24 +++++++++++++++--------- test/markup_renderer_test.go | 4 ++-- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/internal/painter/software/draw.go b/internal/painter/software/draw.go index bd81c0b50a..6cc880fba1 100644 --- a/internal/painter/software/draw.go +++ b/internal/painter/software/draw.go @@ -3,6 +3,7 @@ package software import ( "fmt" "image" + "image/color" "math" "fyne.io/fyne/v2" @@ -46,7 +47,7 @@ func drawGradient(c fyne.Canvas, g gradient, pos fyne.Position, base *image.NRGB width := scale.ToScreenCoordinate(c, bounds.Width) height := scale.ToScreenCoordinate(c, bounds.Height) tex := g.Generate(width, height) - drawTex(scale.ToScreenCoordinate(c, pos.X), scale.ToScreenCoordinate(c, pos.Y), width, height, base, tex, clip) + drawTex(scale.ToScreenCoordinate(c, pos.X), scale.ToScreenCoordinate(c, pos.Y), width, height, base, tex, clip, 1.0) } func drawImage(c fyne.Canvas, img *canvas.Image, pos fyne.Position, base *image.NRGBA, clip image.Rectangle) { @@ -75,13 +76,13 @@ func drawImage(c fyne.Canvas, img *canvas.Image, pos fyne.Position, base *image. } } - drawPixels(scaledX, scaledY, width, height, img.ScaleMode, base, origImg, clip) + drawPixels(scaledX, scaledY, width, height, img.ScaleMode, base, origImg, clip, img.Alpha()) } -func drawPixels(x, y, width, height int, mode canvas.ImageScale, base *image.NRGBA, origImg image.Image, clip image.Rectangle) { +func drawPixels(x, y, width, height int, mode canvas.ImageScale, base *image.NRGBA, origImg image.Image, clip image.Rectangle, alpha float64) { if origImg.Bounds().Dx() == width && origImg.Bounds().Dy() == height { // do not scale or duplicate image since not needed, draw directly - drawTex(x, y, width, height, base, origImg, clip) + drawTex(x, y, width, height, base, origImg, clip, alpha) return } @@ -99,7 +100,7 @@ func drawPixels(x, y, width, height int, mode canvas.ImageScale, base *image.NRG draw.CatmullRom.Scale(scaledImg, scaledBounds, origImg, origImg.Bounds(), draw.Over, nil) } - drawTex(x, y, width, height, base, scaledImg, clip) + drawTex(x, y, width, height, base, scaledImg, clip, alpha) } func drawLine(c fyne.Canvas, line *canvas.Line, pos fyne.Position, base *image.NRGBA, clip image.Rectangle) { @@ -124,11 +125,16 @@ func drawLine(c fyne.Canvas, line *canvas.Line, pos fyne.Position, base *image.N draw.Draw(base, bounds, raw, image.Point{offX, offY}, draw.Over) } -func drawTex(x, y, width, height int, base *image.NRGBA, tex image.Image, clip image.Rectangle) { +func drawTex(x, y, width, height int, base *image.NRGBA, tex image.Image, clip image.Rectangle, alpha float64) { outBounds := image.Rect(x, y, x+width, y+height) clippedBounds := clip.Intersect(outBounds) srcPt := image.Point{X: clippedBounds.Min.X - outBounds.Min.X, Y: clippedBounds.Min.Y - outBounds.Min.Y} - draw.Draw(base, clippedBounds, tex, srcPt, draw.Over) + if alpha == 1.0 { + draw.Draw(base, clippedBounds, tex, srcPt, draw.Over) + } else { + mask := &image.Uniform{C: color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: uint8(float64(0xff) * alpha)}} + draw.DrawMask(base, clippedBounds, tex, srcPt, mask, srcPt, draw.Over) + } } func drawText(c fyne.Canvas, text *canvas.Text, pos fyne.Position, base *image.NRGBA, clip image.Rectangle) { @@ -176,9 +182,9 @@ func drawRaster(c fyne.Canvas, rast *canvas.Raster, pos fyne.Position, base *ima pix := rast.Generator(width, height) if pix.Bounds().Bounds().Dx() != width || pix.Bounds().Dy() != height { - drawPixels(scaledX, scaledY, width, height, rast.ScaleMode, base, pix, clip) + drawPixels(scaledX, scaledY, width, height, rast.ScaleMode, base, pix, clip, 1.0) } else { - drawTex(scaledX, scaledY, width, height, base, pix, clip) + drawTex(scaledX, scaledY, width, height, base, pix, clip, 1.0) } } diff --git a/test/markup_renderer_test.go b/test/markup_renderer_test.go index d2f3ae91e0..22aa3ee14a 100644 --- a/test/markup_renderer_test.go +++ b/test/markup_renderer_test.go @@ -140,12 +140,12 @@ func Test_snapshot(t *testing.T) { "image with translucency": { content: func() fyne.CanvasObject { img := fynecanvas.NewImageFromResource(theme.ZoomOutIcon()) - img.Translucency = 1.3 + img.Translucency = 0.3 return img }(), want: "\n" + "\t\n" + - "\t\t\n" + + "\t\t\n" + "\t\n" + "\n", }, From 228e69af67476c030ceadcb2655908822ac1a034 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 4 Dec 2024 14:48:57 +0000 Subject: [PATCH 64/74] Actually set up alpha blend correctly and discard the pixels tending to invisible --- internal/painter/gl/draw.go | 10 ++-------- internal/painter/gl/shaders.go | 2 +- internal/painter/gl/shaders/simple.frag | 5 ++++- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/internal/painter/gl/draw.go b/internal/painter/gl/draw.go index e1fc6abf9c..d1fc5ba820 100644 --- a/internal/painter/gl/draw.go +++ b/internal/painter/gl/draw.go @@ -264,14 +264,8 @@ func (p *painter) drawTextureWithDetails(o fyne.CanvasObject, creator func(canva p.defineVertexArray(p.program, "vert", 3, 5, 0) p.defineVertexArray(p.program, "vertTexCoord", 2, 5, 3) - // here we have to choose between blending the image alpha or fading it... - // TODO find a way to support both - if alpha != 1.0 { - p.ctx.BlendColor(0, 0, 0, alpha) - p.ctx.BlendFunc(constantAlpha, oneMinusConstantAlpha) - } else { - p.ctx.BlendFunc(one, oneMinusSrcAlpha) - } + p.ctx.BlendColor(0xff, 0xff, 0xff, alpha) + p.ctx.BlendFunc(constantAlpha, oneMinusConstantAlpha) p.logError() p.ctx.ActiveTexture(texture0) diff --git a/internal/painter/gl/shaders.go b/internal/painter/gl/shaders.go index fb7cf999b9..81e0b5b4e4 100644 --- a/internal/painter/gl/shaders.go +++ b/internal/painter/gl/shaders.go @@ -58,7 +58,7 @@ var shaderRoundrectangleesFrag = &fyne.StaticResource{ var shaderSimpleFrag = &fyne.StaticResource{ StaticName: "simple.frag", StaticContent: []byte( - "#version 110\n\nuniform sampler2D tex;\n\nvarying vec2 fragTexCoord;\n\nvoid main() {\n gl_FragColor = texture2D(tex, fragTexCoord);\n}\n"), + "#version 110\n\nuniform sampler2D tex;\n\nvarying vec2 fragTexCoord;\n\nvoid main() {\n vec4 texColor = texture2D(tex, fragTexCoord);\n if(texColor.a < 0.01)\n discard;\n gl_FragColor = texColor;\n}\n"), } var shaderSimpleVert = &fyne.StaticResource{ StaticName: "simple.vert", diff --git a/internal/painter/gl/shaders/simple.frag b/internal/painter/gl/shaders/simple.frag index 0051187280..6af409ee93 100644 --- a/internal/painter/gl/shaders/simple.frag +++ b/internal/painter/gl/shaders/simple.frag @@ -5,5 +5,8 @@ uniform sampler2D tex; varying vec2 fragTexCoord; void main() { - gl_FragColor = texture2D(tex, fragTexCoord); + vec4 texColor = texture2D(tex, fragTexCoord); + if(texColor.a < 0.01) + discard; + gl_FragColor = texColor; } From e2af2c43a5256e98d53c9d4d4ff0c562c3d11543 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 4 Dec 2024 15:18:51 +0000 Subject: [PATCH 65/74] Add an image alpha test too --- internal/painter/software/painter_test.go | 14 ++++++++++++++ .../software/testdata/draw_image_alpha.png | Bin 0 -> 1612 bytes 2 files changed, 14 insertions(+) create mode 100644 internal/painter/software/testdata/draw_image_alpha.png diff --git a/internal/painter/software/painter_test.go b/internal/painter/software/painter_test.go index 57eb1f5c4d..345c6fd418 100644 --- a/internal/painter/software/painter_test.go +++ b/internal/painter/software/painter_test.go @@ -78,6 +78,20 @@ func TestPainter_paintImage(t *testing.T) { test.AssertImageMatches(t, "draw_image_default.png", target) } +func TestPainter_paintImageAlpha(t *testing.T) { + img := canvas.NewImageFromImage(makeTestImage(3, 3)) + img.Translucency = 0.5 + + c := test.NewCanvas() + c.SetPadded(false) + c.SetContent(img) + c.Resize(fyne.NewSize(50, 50)) + p := software.NewPainter() + + target := p.Paint(c) + test.AssertImageMatches(t, "draw_image_alpha.png", target) +} + func TestPainter_paintImage_clipped(t *testing.T) { test.ApplyTheme(t, test.Theme()) img := canvas.NewImageFromImage(makeTestImage(5, 5)) diff --git a/internal/painter/software/testdata/draw_image_alpha.png b/internal/painter/software/testdata/draw_image_alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..8c01d03eca85ff8649bbc3e5ed263baf149fe525 GIT binary patch literal 1612 zcmV-S2DABzP)a0A6U1k;?7dOm00O@3^KFwT;Z3C67mv!~hl)AIUQDG1E1DRkt(QY&QQZ z1INwHO}W0lE>~Aq+<^gn$9tqOv-dRE%W)jESJl&TCMiZ zi^Zb5H=E7M+1c4p(aOo-Crc@t&1UcHJHAVq>e}C@|Ey6-B48on9 z)ThFx;#pq#~1!IJoXwabIyDtanr(FE20Mk!i(& z0OH%XZ{_>);$UkP5$)sRGy!o_rN%i2m&BKe*D#OXik--agBL=O)SpmO2X+KO)MeN9M9l4Xa)0Pea)%4BM7ud=ysDtD-)5tT2_r<9FS{7i%3BJ zmVo$DW`25BKy=nM*NEl@gw~26A}ipSh{cwKB4mJwT9`oGZ9w4c1cb5;(J`{QM%aA^ z!TlBh!uLefn-Q632Fk&?Mzn|)_GkL|n45^6XLAH1<75aO&m9oe5%FRiwF!1=-I_1E7QG7%9bsZ+*Im@Y0rll_YMj);Q2cZec6F7F+(hxl(5P$#v-2o!u6^lv|652xE zaG(Yilg4YXbNELrbu<4WVquyL&5?y?Xnf>)m{T8!GPWJ`Umm?0 zA&f@elkH?y0x)pY7)CSAvJUG?hJ#JZon5xB$w+vZ2ZAS<)9K)ZNTTzsO|+r1bHQr~ z(Gwlf5B3ZQzeXNwCdB!CKANIX-Juaerqd40t3^@VR9t8YhK~r;Ad0eFE)OD&IKL)w zs^Q6Q2x-*cQE(Ek7ihhtakX0Q9A+$y$Fy){ryXNxJZ6L`3ead8FO8H>6jsIh!j!6q z6jrfWmz}e*E38~2hm^X$bG02TJ*BSar`YNlT#X;?$vJI&vgx1(vZMB2s*o0><1+r?hw;iPLTSheKJTK^)M=hv#Y^|20LFaiQ z<_zU4x{;AhR2y`&*=%sjG=g9f%#Kc^iXy;dEmOj+w^Y5R5r{v3{(MNl&6cTu0&X?2 zBZyjN7Z7Ph2Tg>sZDQ*gXot60T_esSD4V^5Uu|N0C-(SkRvfL*cd@9-O65-jDajYv>}zb_7ui~c z$cS3Wz6>7)$e3^BwK3*f{XYScWmO+B54C{!vp%u{@902k)Sx8)dJzTm(MKvHn4<&9 zz?6iZ1E>XL6aJT_#*jzJHs;}sKD#in@X1EsbFDR6 Date: Wed, 4 Dec 2024 15:43:03 +0000 Subject: [PATCH 66/74] Turns out this IF is still needed for some scenarios --- internal/painter/gl/draw.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/painter/gl/draw.go b/internal/painter/gl/draw.go index d1fc5ba820..c4c1aecd62 100644 --- a/internal/painter/gl/draw.go +++ b/internal/painter/gl/draw.go @@ -264,8 +264,12 @@ func (p *painter) drawTextureWithDetails(o fyne.CanvasObject, creator func(canva p.defineVertexArray(p.program, "vert", 3, 5, 0) p.defineVertexArray(p.program, "vertTexCoord", 2, 5, 3) - p.ctx.BlendColor(0xff, 0xff, 0xff, alpha) - p.ctx.BlendFunc(constantAlpha, oneMinusConstantAlpha) + if alpha != 1.0 { + p.ctx.BlendColor(alpha, alpha, alpha, alpha) + p.ctx.BlendFunc(constantAlpha, oneMinusConstantAlpha) + } else { + p.ctx.BlendFunc(one, oneMinusSrcAlpha) + } p.logError() p.ctx.ActiveTexture(texture0) From bb5ec5555df45bb5ca018c957c59562755fa8451 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 4 Dec 2024 18:10:38 +0000 Subject: [PATCH 67/74] Another fix picked in --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93ca1a9d66..bdd98b2ec6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ More detailed release notes can be found on the [releases page](https://github.c * [Linux] Window with list flickers in Wayland (#5133) * Package command fails on OpenBSD (#5195) * System theme fallback is not working with custom themes +* Translucency and images with Alpha channel (#1977) * Performance regression when scrolling inside the file dialog (#4307) * Empty but visible images consume high CPU on 2.4.x (#4345) * Improved performance of text render caching From c57f7f064894514a9019124ec12d6afdffe16147 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sun, 8 Dec 2024 20:22:20 +0000 Subject: [PATCH 68/74] Fix issue with writing tiny files on iOS --- internal/driver/mobile/file_ios.m | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/driver/mobile/file_ios.m b/internal/driver/mobile/file_ios.m index 3bf743236a..0520d2938f 100644 --- a/internal/driver/mobile/file_ios.m +++ b/internal/driver/mobile/file_ios.m @@ -39,6 +39,7 @@ bool iosExistsPath(const char* path) { void iosCloseFileWriter(void* handlePtr) { NSFileHandle* handle = (NSFileHandle*)handlePtr; + [handle synchronizeFile]; [handle closeFile]; } From dbc933ce08f6cc4c56e303fd2aa7772b65ecb73b Mon Sep 17 00:00:00 2001 From: Simon Dassow Date: Sat, 7 Dec 2024 15:54:37 +0100 Subject: [PATCH 69/74] Add fix and test for serializing SVG without width or height attributes --- internal/svg/svg.go | 4 ++-- theme/icons_test.go | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/internal/svg/svg.go b/internal/svg/svg.go index c89be41d8b..9300c06c03 100644 --- a/internal/svg/svg.go +++ b/internal/svg/svg.go @@ -119,8 +119,8 @@ func IsResourceSVG(res fyne.Resource) bool { type svg struct { XMLName xml.Name `xml:"svg"` XMLNS string `xml:"xmlns,attr"` - Width string `xml:"width,attr"` - Height string `xml:"height,attr"` + Width string `xml:"width,attr,omitempty"` + Height string `xml:"height,attr,omitempty"` ViewBox string `xml:"viewBox,attr,omitempty"` Paths []*pathObj `xml:"path"` Rects []*rectObj `xml:"rect"` diff --git a/theme/icons_test.go b/theme/icons_test.go index 2f13f80a5b..091441a7b8 100644 --- a/theme/icons_test.go +++ b/theme/icons_test.go @@ -3,6 +3,7 @@ package theme_test import ( "fmt" "path/filepath" + "regexp" "testing" "github.com/stretchr/testify/assert" @@ -50,6 +51,18 @@ func TestIconThemeChangeContent(t *testing.T) { assert.NotEqual(t, content, cancel.Content()) } +func TestIconThemeResourceWithViewboxOnly(t *testing.T) { + r1 := &fyne.StaticResource{ + StaticName: "unsplash.svg", + StaticContent: []byte(``), + } + r2 := theme.NewThemedResource(r1) + re := regexp.MustCompile(`\s+fill\S+`) + content := re.ReplaceAll(r2.Content(), []byte("")) + + assert.Equal(t, r1.Content(), content) +} + func TestNewThemedResource_StaticResourceSupport(t *testing.T) { fyne.CurrentApp().Settings().SetTheme(theme.DarkTheme()) custom := theme.NewThemedResource(helperNewStaticResource()) From bd680ad8ccb7ebfb9ed1e4fdecc3a4de7581843d Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sun, 8 Dec 2024 20:01:51 +0000 Subject: [PATCH 70/74] Fix race in window fullscreen test (#5296) --- internal/driver/glfw/window_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/driver/glfw/window_test.go b/internal/driver/glfw/window_test.go index 00c5e13222..1646a54dae 100644 --- a/internal/driver/glfw/window_test.go +++ b/internal/driver/glfw/window_test.go @@ -1115,6 +1115,7 @@ func TestWindow_TappedSecondary(t *testing.T) { o := &tappableObject{Rectangle: canvas.NewRectangle(color.White)} o.SetMinSize(fyne.NewSize(100, 100)) w.SetContent(o) + waitForMain() w.mousePos = fyne.NewPos(50, 60) w.mouseClicked(w.viewport, glfw.MouseButton2, glfw.Press, 0) @@ -1135,6 +1136,7 @@ func TestWindow_TappedSecondary_OnPrimaryOnlyTarget(t *testing.T) { tapped = true }) w.SetContent(o) + waitForMain() ensureCanvasSize(t, w, fyne.NewSize(53, 44)) w.mousePos = fyne.NewPos(10, 25) @@ -1759,17 +1761,15 @@ func TestWindow_SetContent_Twice(t *testing.T) { } func TestWindow_SetFullScreen(t *testing.T) { - w := createWindow("Full").(*window) + w := d.CreateWindow("Full").(*window) w.SetFullScreen(true) + w.create() + w.create() w.doShow() waitForMain() - - // initial state - no window size set (except darwin?) - if runtime.GOOS != "darwin" { - assert.Zero(t, w.width) - assert.Zero(t, w.height) - } + assert.Zero(t, w.width) + assert.Zero(t, w.height) w.SetFullScreen(false) waitForMain() From 1f3d047515b065eeec6ad9ecc2e2e4581a85d14d Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sun, 8 Dec 2024 20:20:06 +0000 Subject: [PATCH 71/74] Fixing a iOS compile error after last refactor --- app/app_mobile_ios.go | 1 - app/app_mobile_ios.m | 5 ----- internal/app/config_mobile_ios.go | 7 +++++++ internal/app/config_mobile_ios.m | 9 +++++++++ 4 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 internal/app/config_mobile_ios.m diff --git a/app/app_mobile_ios.go b/app/app_mobile_ios.go index 82b8d25f49..833ae4fdfa 100644 --- a/app/app_mobile_ios.go +++ b/app/app_mobile_ios.go @@ -8,7 +8,6 @@ package app #include -char *documentsPath(void); void openURL(char *urlStr); void sendNotification(char *title, char *content); */ diff --git a/app/app_mobile_ios.m b/app/app_mobile_ios.m index d1e6ab48b6..bfdbfebef0 100644 --- a/app/app_mobile_ios.m +++ b/app/app_mobile_ios.m @@ -8,8 +8,3 @@ void openURL(char *urlStr) { [app openURL:url options:@{} completionHandler:nil]; } -char *documentsPath() { - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *path = paths.firstObject; - return [path UTF8String]; -} diff --git a/internal/app/config_mobile_ios.go b/internal/app/config_mobile_ios.go index 193063e76b..0516fe1c10 100644 --- a/internal/app/config_mobile_ios.go +++ b/internal/app/config_mobile_ios.go @@ -6,6 +6,13 @@ import ( "path/filepath" ) +/* +#include + +char *documentsPath(void); +*/ +import "C" + func rootConfigDir() string { root := C.documentsPath() return filepath.Join(C.GoString(root), "fyne") diff --git a/internal/app/config_mobile_ios.m b/internal/app/config_mobile_ios.m new file mode 100644 index 0000000000..059005aa8a --- /dev/null +++ b/internal/app/config_mobile_ios.m @@ -0,0 +1,9 @@ +//go:build !ci && ios + +#import + +char *documentsPath() { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *path = paths.firstObject; + return [path UTF8String]; +} \ No newline at end of file From 2034fd5691e09954721b56edc13a49b1c2601a84 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sun, 8 Dec 2024 20:31:03 +0000 Subject: [PATCH 72/74] Preparing another release --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdd98b2ec6..0dc29a8ae4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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.3 - 8 December 2024 +## 2.5.3 - 12 December 2024 ### Changed @@ -25,6 +25,9 @@ More detailed release notes can be found on the [releases page](https://github.c * Performance regression when scrolling inside the file dialog (#4307) * Empty but visible images consume high CPU on 2.4.x (#4345) * Improved performance of text render caching +* nil pointer dereference in dialog.Resize() for color picker (#5236) +* Tiny files written in iOS may be empty +* Some SVG resources don't update appearance correctly with the theme (#3900) ## 2.5.2 - 15 October 2024 @@ -225,7 +228,6 @@ More detailed release notes can be found on the [releases page](https://github.c * Avoid memory leak in Android driver code * Entry Field on Android in Landscape Mode Shows "0" (#4036) * DocTabs Indicator remains visible after last tab is removed (#4220) -* Some SVG resources don't update appearance correctly with the theme (#3900) * Fix mobile simulation builds on OpenBSD * Fix alignment of menu button on mobile * Fix Compilation with Android NDK r26 From e37d7417cd5de45bd259806bd764e7656c8cd594 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sun, 8 Dec 2024 20:46:54 +0000 Subject: [PATCH 73/74] Also fix transform property in SVG Fixes #3900 --- internal/svg/svg.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/svg/svg.go b/internal/svg/svg.go index 9300c06c03..7bf598e67d 100644 --- a/internal/svg/svg.go +++ b/internal/svg/svg.go @@ -140,6 +140,7 @@ type pathObj struct { StrokeLineJoin string `xml:"stroke-linejoin,attr,omitempty"` StrokeDashArray string `xml:"stroke-dasharray,attr,omitempty"` D string `xml:"d,attr"` + Transform string `xml:"transform,attr"` } type rectObj struct { @@ -155,6 +156,7 @@ type rectObj struct { Y string `xml:"y,attr,omitempty"` Width string `xml:"width,attr,omitempty"` Height string `xml:"height,attr,omitempty"` + Transform string `xml:"transform,attr"` } type circleObj struct { @@ -169,6 +171,7 @@ type circleObj struct { CX string `xml:"cx,attr,omitempty"` CY string `xml:"cy,attr,omitempty"` R string `xml:"r,attr,omitempty"` + Transform string `xml:"transform,attr"` } type ellipseObj struct { @@ -184,6 +187,7 @@ type ellipseObj struct { CY string `xml:"cy,attr,omitempty"` RX string `xml:"rx,attr,omitempty"` RY string `xml:"ry,attr,omitempty"` + Transform string `xml:"transform,attr"` } type polygonObj struct { @@ -196,6 +200,7 @@ type polygonObj struct { StrokeLineJoin string `xml:"stroke-linejoin,attr,omitempty"` StrokeDashArray string `xml:"stroke-dasharray,attr,omitempty"` Points string `xml:"points,attr"` + Transform string `xml:"transform,attr"` } type objGroup struct { @@ -207,6 +212,7 @@ type objGroup struct { StrokeLineCap string `xml:"stroke-linecap,attr,omitempty"` StrokeLineJoin string `xml:"stroke-linejoin,attr,omitempty"` StrokeDashArray string `xml:"stroke-dasharray,attr,omitempty"` + Transform string `xml:"transform,attr"` Paths []*pathObj `xml:"path"` Circles []*circleObj `xml:"circle"` Ellipses []*ellipseObj `xml:"ellipse"` From c89253dca6a6ca0d54384423f487892023287d76 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sun, 8 Dec 2024 20:50:32 +0000 Subject: [PATCH 74/74] Don't add empty transform --- internal/svg/svg.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/svg/svg.go b/internal/svg/svg.go index 7bf598e67d..9e78efc3cb 100644 --- a/internal/svg/svg.go +++ b/internal/svg/svg.go @@ -140,7 +140,7 @@ type pathObj struct { StrokeLineJoin string `xml:"stroke-linejoin,attr,omitempty"` StrokeDashArray string `xml:"stroke-dasharray,attr,omitempty"` D string `xml:"d,attr"` - Transform string `xml:"transform,attr"` + Transform string `xml:"transform,attr,omitempty"` } type rectObj struct { @@ -156,7 +156,7 @@ type rectObj struct { Y string `xml:"y,attr,omitempty"` Width string `xml:"width,attr,omitempty"` Height string `xml:"height,attr,omitempty"` - Transform string `xml:"transform,attr"` + Transform string `xml:"transform,attr,omitempty"` } type circleObj struct { @@ -171,7 +171,7 @@ type circleObj struct { CX string `xml:"cx,attr,omitempty"` CY string `xml:"cy,attr,omitempty"` R string `xml:"r,attr,omitempty"` - Transform string `xml:"transform,attr"` + Transform string `xml:"transform,attr,omitempty"` } type ellipseObj struct { @@ -187,7 +187,7 @@ type ellipseObj struct { CY string `xml:"cy,attr,omitempty"` RX string `xml:"rx,attr,omitempty"` RY string `xml:"ry,attr,omitempty"` - Transform string `xml:"transform,attr"` + Transform string `xml:"transform,attr,omitempty"` } type polygonObj struct { @@ -200,7 +200,7 @@ type polygonObj struct { StrokeLineJoin string `xml:"stroke-linejoin,attr,omitempty"` StrokeDashArray string `xml:"stroke-dasharray,attr,omitempty"` Points string `xml:"points,attr"` - Transform string `xml:"transform,attr"` + Transform string `xml:"transform,attr,omitempty"` } type objGroup struct { @@ -212,7 +212,7 @@ type objGroup struct { StrokeLineCap string `xml:"stroke-linecap,attr,omitempty"` StrokeLineJoin string `xml:"stroke-linejoin,attr,omitempty"` StrokeDashArray string `xml:"stroke-dasharray,attr,omitempty"` - Transform string `xml:"transform,attr"` + Transform string `xml:"transform,attr,omitempty"` Paths []*pathObj `xml:"path"` Circles []*circleObj `xml:"circle"` Ellipses []*ellipseObj `xml:"ellipse"`