From 93f32313112e0dfd16d06a5b5fe631ad236fe1c6 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sat, 4 Feb 2023 11:28:28 +0000 Subject: [PATCH 01/52] Fix typo causing bad vertical scroll cursor position --- widget/entry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/entry.go b/widget/entry.go index bde09f27e5..35a3626961 100644 --- a/widget/entry.go +++ b/widget/entry.go @@ -1683,7 +1683,7 @@ func (r *entryContentRenderer) ensureCursorVisible() { } if cy1 < offset.Y { move.DY -= offset.Y - cy1 - } else if cy2 >= offset.X+size.Height { + } else if cy2 >= offset.Y+size.Height { move.DY += cy2 - (offset.Y + size.Height) } if r.content.scroll.Content != nil { From 09205dd4828a330c1a8f98be139725b387d6422b Mon Sep 17 00:00:00 2001 From: Jacalz Date: Tue, 20 Dec 2022 11:23:17 +0100 Subject: [PATCH 02/52] storage/reository: Remove workaround for bug in old fredbi/uri version --- storage/repository/parse.go | 38 ++++++++----------------------------- 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/storage/repository/parse.go b/storage/repository/parse.go index c768e66d18..3021689683 100644 --- a/storage/repository/parse.go +++ b/storage/repository/parse.go @@ -30,10 +30,7 @@ func NewFileURI(path string) fyne.URI { return &uri{ scheme: "file", haveAuthority: true, - authority: "", path: path, - query: "", - fragment: "", } } @@ -81,44 +78,25 @@ func ParseURI(s string) (fyne.URI, error) { // There was no repository registered, or it did not provide a parser - // Ugly hack to work around fredbi/uri. Technically, something like - // foo:/// is invalid because it implies a host, but also has an empty - // host. However, this is a very common occurrence, so we convert a - // leading ":///" to "://". - rest := strings.TrimPrefix(s, scheme+":") - dummyHost := false - if len(rest) >= 3 && rest[0:3] == "///" { - rest = "//" + "TEMP.TEMP/" + strings.TrimPrefix(rest, "///") - dummyHost = true - } - s = scheme + ":" + rest - l, err := uriParser.Parse(s) if err != nil { return nil, err } authority := "" - if !dummyHost { - // User info makes no sense without a host, see next comment. - if userInfo := l.Authority().UserInfo(); len(userInfo) > 0 { - authority += userInfo + "@" - } - // In this case, we had to insert a "host" to make the parser - // happy, but it isn't really a host, so we can just drop it. - // If dummyHost isn't set, then we should have a valid host and - // we can include it as normal. - authority += l.Authority().Host() + if userInfo := l.Authority().UserInfo(); len(userInfo) > 0 { + authority += userInfo + "@" + } - // Port obviously makes no sense without a host. - if port := l.Authority().Port(); len(port) > 0 { - authority += ":" + port - } + authority += l.Authority().Host() + + if port := l.Authority().Port(); len(port) > 0 { + authority += ":" + port } return &uri{ - scheme: l.Scheme(), + scheme: scheme, authority: authority, // workaround for net/url, see type uri struct comments haveAuthority: true, From 5bc66cf647cca65256c4446dc821a20fb9e0f0ce Mon Sep 17 00:00:00 2001 From: Jacalz Date: Tue, 20 Dec 2022 11:28:00 +0100 Subject: [PATCH 03/52] storage/repository: Remove field that always was set to true --- storage/repository/parse.go | 13 +++++-------- storage/repository/uri.go | 16 ++++------------ 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/storage/repository/parse.go b/storage/repository/parse.go index 3021689683..db4e25962e 100644 --- a/storage/repository/parse.go +++ b/storage/repository/parse.go @@ -28,9 +28,8 @@ func NewFileURI(path string) fyne.URI { } return &uri{ - scheme: "file", - haveAuthority: true, - path: path, + scheme: "file", + path: path, } } @@ -98,10 +97,8 @@ func ParseURI(s string) (fyne.URI, error) { return &uri{ scheme: scheme, authority: authority, - // workaround for net/url, see type uri struct comments - haveAuthority: true, - path: l.Authority().Path(), - query: l.Query().Encode(), - fragment: l.Fragment(), + path: l.Authority().Path(), + query: l.Query().Encode(), + fragment: l.Fragment(), }, nil } diff --git a/storage/repository/uri.go b/storage/repository/uri.go index c3c96eed23..bb07544dfb 100644 --- a/storage/repository/uri.go +++ b/storage/repository/uri.go @@ -16,13 +16,9 @@ var _ fyne.URI = &uri{} type uri struct { scheme string authority string - // haveAuthority lets us distinguish between a present-but-empty - // authority, and having no authority. This is needed because net/url - // incorrectly handles scheme:/absolute/path URIs. - haveAuthority bool - path string - query string - fragment string + path string + query string + fragment string } func (u *uri) Extension() string { @@ -65,11 +61,7 @@ func (u *uri) String() string { // NOTE: this string reconstruction is mandated by IETF RFC3986, // section 5.3, pp. 35. - s := u.scheme + ":" - if u.haveAuthority { - s += "//" + u.authority - } - s += u.path + s := u.scheme + "://" + u.authority + u.path if len(u.query) > 0 { s += "?" + u.query } From 4610e58595271c72afd1d0d77314d97ea2aad98c Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 26 Dec 2022 18:54:22 +0000 Subject: [PATCH 04/52] Fix issue in software rendering ignoring scale for text --- internal/painter/software/draw.go | 2 +- internal/painter/software/painter_test.go | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/internal/painter/software/draw.go b/internal/painter/software/draw.go index af85bf98cc..e067b75a1d 100644 --- a/internal/painter/software/draw.go +++ b/internal/painter/software/draw.go @@ -142,7 +142,7 @@ func drawText(c fyne.Canvas, text *canvas.Text, pos fyne.Position, base *image.N color = theme.ForegroundColor() } - face, measureFace := painter.CachedFontFace(text.TextStyle, text.TextSize, 1) + face, measureFace := painter.CachedFontFace(text.TextStyle, text.TextSize*c.Scale(), 1) painter.DrawString(txtImg, text.Text, color, face, measureFace, text.TextSize, c.Scale(), height, text.TextStyle.TabWidth) size := text.Size() diff --git a/internal/painter/software/painter_test.go b/internal/painter/software/painter_test.go index 1763d59496..dccbecad5d 100644 --- a/internal/painter/software/painter_test.go +++ b/internal/painter/software/painter_test.go @@ -364,3 +364,18 @@ func TestPainter_paintText_boldItalicClip(t *testing.T) { test.AssertImageMatches(t, "draw_text_bolditalic.png", p.Paint(c)) } + +func TestPainter_paintText_scale2(t *testing.T) { + test.ApplyTheme(t, test.Theme()) + text := canvas.NewText("scale2", theme.ForegroundColor()) + text.TextSize = 18 + c := test.NewCanvas() + c.SetPadded(false) + c.SetContent(text) + c.Resize(fyne.NewSize(70, text.MinSize().Height)) + + c.SetScale(2) + p := software.NewPainter() + + test.AssertImageMatches(t, "draw_text_scale2.png", p.Paint(c)) +} From 0142f8ec76e61116c26799652f0d9a5c72b77cf7 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 26 Dec 2022 19:00:27 +0000 Subject: [PATCH 05/52] Add missing test file --- .../software/testdata/draw_text_scale2.png | Bin 0 -> 2114 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 internal/painter/software/testdata/draw_text_scale2.png diff --git a/internal/painter/software/testdata/draw_text_scale2.png b/internal/painter/software/testdata/draw_text_scale2.png new file mode 100644 index 0000000000000000000000000000000000000000..25d7086984218ffc055f3ce377af79dcefc83a9e GIT binary patch literal 2114 zcmV-I2)*}-P)$j(h!vdFH<4)2vMbtps9AOH*lE(exL+AirCNvkA1E@>yQ z30MN0)oWV|PA?)1V8Ca9&2&Z|a2s&cVAxlO|C=0UumQ$N`hlePCAW4-S|jOSlHMfg zR7uD9y%6y~CFyivhMB$E1JeuYL4*O$2X;p~p8zfc-dxdkHn1*EQu;EiqC9uK?}+4y3+K7|3KS5*>lVn*8qNtAilz*?72%Nzy>W9$i%j*}F;eVe3p zX0|O?5pbx>nIP#1uNIy%vwvpD7%k~|Nh2k_C~2da?aZ|$7vM-qCrKJ3X^W(%&FtTu z^fj}M8LIx8oQG#!nP=@F1B?Q$0sh`1n|}&e1k40lmgc}L;BH`N6IuTNZUv@R^gYDK z_uIYntYRy0vnQFY?8u5R;bQ?l>$yi2zX#^Ed;T1N$%&4ScT4tw0L%rpw{VmyE@~oc zH1P8bIbTfrza4lwqyG-ziWbk-vBLnTw)tRUep;2t)&ZEF$ez&+x!q*{kQB0dNk8_X z=e0|c9+LE=hyP)c!gze1r19S78A;1hp}?_{{v>Hqax0XSPe^*+ho{qA-yTUnO4@Fa zG&(weMbg7zNc7{-KaBCG`27)*LZn%3X0^jOaIU2LCB<_gYb8A*>Df^7N;*fAc%Q$kp$YKI$g?-Knso%u2EG+ScEvqQWCb1r&I?0K(srZ|IafE) z6NbI}fjL!E=%I=Tktxx&w27>?5yod^MOW!^UCXJNuwkU_v&p- z7gWSQ_I63v0iE=V^OmgP7r08&H=;}5H?x~^Wb_oTS{8c3m$K6p`o-D3T?$>dcT&Jw0!)KeDau3V#lFH5>Hy0k;mt&)~YdNf^s3Jxymv0Oa~+oR)ElCJ8f zyh?zNNV+r1^?oJk;yUkX$l@jE#lR!U2NlZZv!iy`B==9Lq3gtCAIY=8TiWzFs|yik zX>>nl!LVb1i;`zMXd+94j>b$g>oc=^B~49|w8JFLkIuIwUDKt3PPkOkq0#v#%xpuO zKG}|0K^(Ov`AU7)0A_pD*m$;|Nt$!8MV722%N^&2q+E_ynbS3#*)?oBBRUQ*vy&m?vy)!EzIkk8 zB5OG?vBmos?;EXF_>REF_4g;6#{K}D?9ZF?H>CdMWPelv{^MW5zEHYKTH*U$Uhusk z$4dI3&j2?F(PfI6#U*K@%w6d-u?=?newK}{_h`>OPV)Qn%xp0*Owyl|yd>zk$NP?R z8!m7efd!JTh-^FG%vK~eZIg7QpI&cf&73AU+pj%3Ht6&HIjh}ZQ0r_8-^+e zB)YzyG0A_hQNFnMoG&D9HnW%OC<2c21-^+s!@R@8{F+utATUDG$&%jc_6A$ln%VPp z?5Jt`=*ad_ZhIK2*PB__7FY{~|2YoM)zL!FC=OG|qBu+;i{dbaEQ-SvvM3Hy$f7t* sA&cTLg)EB06tXA|Q^?}K0RRC1|Ma!HKgb${+W-In07*qoM6N<$f-%(^8vp Date: Thu, 29 Dec 2022 17:46:09 +0200 Subject: [PATCH 06/52] Fixes #3507 window is max size at all times --- widget/richtext.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/widget/richtext.go b/widget/richtext.go index 9f4bfaf492..66eba2636f 100644 --- a/widget/richtext.go +++ b/widget/richtext.go @@ -45,7 +45,10 @@ type RichText struct { func NewRichText(segments ...RichTextSegment) *RichText { t := &RichText{Segments: segments} t.Scroll = widget.ScrollNone + t.ExtendBaseWidget(t) + t.updateRowBounds() + t.Refresh() return t } From 671dfd87d200c8c2e5d691ed769017e7c637190d Mon Sep 17 00:00:00 2001 From: Alex Ballas Date: Thu, 29 Dec 2022 20:31:03 +0200 Subject: [PATCH 07/52] Update richtext.go --- widget/richtext.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/widget/richtext.go b/widget/richtext.go index 66eba2636f..ffdd8dea08 100644 --- a/widget/richtext.go +++ b/widget/richtext.go @@ -44,11 +44,8 @@ type RichText struct { // Since: 2.1 func NewRichText(segments ...RichTextSegment) *RichText { t := &RichText{Segments: segments} - t.Scroll = widget.ScrollNone - t.ExtendBaseWidget(t) - + t.Scroll = widget.ScrollNone t.updateRowBounds() - t.Refresh() return t } @@ -797,7 +794,7 @@ func findSpaceIndex(text []rune, fallback int) int { // lineBounds returns a slice containing the boundary metadata of each line with the given wrapping applied. func lineBounds(seg *TextSegment, wrap fyne.TextWrap, firstWidth, maxWidth float32, measurer func([]rune) float32) []rowBoundary { lines := splitLines(seg) - if wrap == fyne.TextWrapOff { + if maxWidth < 0 || wrap == fyne.TextWrapOff { return lines } From 1dadf23ec35b19c4e84509276434c1da4d2561b9 Mon Sep 17 00:00:00 2001 From: Alex Ballas Date: Thu, 29 Dec 2022 20:40:04 +0200 Subject: [PATCH 08/52] Fix formating error --- widget/richtext.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/richtext.go b/widget/richtext.go index ffdd8dea08..64c675f0a5 100644 --- a/widget/richtext.go +++ b/widget/richtext.go @@ -44,7 +44,7 @@ type RichText struct { // Since: 2.1 func NewRichText(segments ...RichTextSegment) *RichText { t := &RichText{Segments: segments} - t.Scroll = widget.ScrollNone + t.Scroll = widget.ScrollNone t.updateRowBounds() return t } From b51e79942836a93def58ccf19ed15736a2c56e89 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 26 Dec 2022 21:03:53 +0000 Subject: [PATCH 09/52] Fix issue where a menu item would not appear disabled initially --- widget/menu_item.go | 4 +++- widget/menu_item_test.go | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 widget/menu_item_test.go diff --git a/widget/menu_item.go b/widget/menu_item.go index eb3e3d9843..d4164b3515 100644 --- a/widget/menu_item.go +++ b/widget/menu_item.go @@ -96,7 +96,7 @@ func (i *menuItem) CreateRenderer() fyne.WidgetRenderer { } objects = append(objects, checkIcon) - return &menuItemRenderer{ + r := &menuItemRenderer{ BaseRenderer: widget.NewBaseRenderer(objects), i: i, expandIcon: expandIcon, @@ -106,6 +106,8 @@ func (i *menuItem) CreateRenderer() fyne.WidgetRenderer { text: text, background: background, } + r.Refresh() // ensure text and icon resources match state + return r } // MouseIn activates the item which shows the submenu if the item has one. diff --git a/widget/menu_item_test.go b/widget/menu_item_test.go new file mode 100644 index 0000000000..fac1a0007b --- /dev/null +++ b/widget/menu_item_test.go @@ -0,0 +1,21 @@ +package widget + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/internal/cache" + "fyne.io/fyne/v2/theme" +) + +func TestMenuItem_Disabled(t *testing.T) { + i := fyne.NewMenuItem("Disabled", func() {}) + m := fyne.NewMenu("top", []*fyne.MenuItem{i}...) + i.Disabled = true + w := newMenuItem(i, NewMenu(m)) + r := cache.Renderer(w) + + assert.Equal(t, theme.DisabledColor(), r.(*menuItemRenderer).text.Color) +} From b78b038035f0e39cb573967b104ba5618d837775 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 8 Feb 2023 15:27:12 +0000 Subject: [PATCH 10/52] Correct the icon colour for danger and warning buttons --- widget/button.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/button.go b/widget/button.go index c26558775e..04b7ff1664 100644 --- a/widget/button.go +++ b/widget/button.go @@ -362,12 +362,12 @@ func (r *buttonRenderer) applyTheme() { if r.icon != nil && r.icon.Resource != nil { switch res := r.icon.Resource.(type) { case *theme.ThemedResource: - if r.button.Importance == HighImportance { + if r.button.Importance == HighImportance || r.button.Importance == DangerImportance || r.button.Importance == WarningImportance { r.icon.Resource = theme.NewInvertedThemedResource(res) r.icon.Refresh() } case *theme.InvertedThemedResource: - if r.button.Importance != HighImportance { + if r.button.Importance != HighImportance && r.button.Importance != DangerImportance && r.button.Importance != WarningImportance { r.icon.Resource = res.Original() r.icon.Refresh() } From 4cacece68013e769073e702b0ee8b5fbcbac2c2c Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Thu, 29 Dec 2022 22:09:25 +0000 Subject: [PATCH 11/52] Don't log a common error - lots of environments don't have this exported Fixes #3515 --- app/app_xdg.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/app_xdg.go b/app/app_xdg.go index 396080460e..3565750208 100644 --- a/app/app_xdg.go +++ b/app/app_xdg.go @@ -48,7 +48,7 @@ func findFreedestktopColorScheme() fyne.ThemeVariant { "color-scheme", ) if call.Err != nil { - fyne.LogError("failed to read theme variant from D-Bus", call.Err) + // many desktops don't have this exported yet return theme.VariantDark } From 5b23f1e0b5c2520cf8476a749b3c9d61d74700c6 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sun, 8 Jan 2023 22:32:40 +0000 Subject: [PATCH 12/52] Add some negative bounds checking Fixes #3292 --- widget/richtext.go | 6 ++++++ widget/richtext_test.go | 3 +++ 2 files changed, 9 insertions(+) diff --git a/widget/richtext.go b/widget/richtext.go index 64c675f0a5..0e56d5d99e 100644 --- a/widget/richtext.go +++ b/widget/richtext.go @@ -251,6 +251,12 @@ func (t *RichText) len() int { // lineSizeToColumn returns the rendered size for the line specified by row up to the col position func (t *RichText) lineSizeToColumn(col, row int) fyne.Size { + if row < 0 { + row = 0 + } + if col < 0 { + col = 0 + } bound := t.rowBoundary(row) total := fyne.NewSize(0, 0) counted := 0 diff --git a/widget/richtext_test.go b/widget/richtext_test.go index 25120bd31b..a7261edcb3 100644 --- a/widget/richtext_test.go +++ b/widget/richtext_test.go @@ -370,6 +370,9 @@ func TestTextProvider_LineSizeToColumn(t *testing.T) { fullSize := provider.lineSizeToColumn(4, 0) assert.Equal(t, fullSize, provider.lineSizeToColumn(10, 0)) assert.Greater(t, fullSize.Width, provider.lineSizeToColumn(2, 0).Width) + + out := provider.lineSizeToColumn(-1, -1) + assert.Equal(t, out, provider.lineSizeToColumn(0, 0)) } func TestText_splitLines(t *testing.T) { From 35240b2b77f5398a1ea534bb4a560d4d94c569d5 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sun, 8 Jan 2023 22:56:10 +0000 Subject: [PATCH 13/52] Another minor bug fix Fixes #3290 --- widget/entry.go | 3 +++ widget/entry_test.go | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/widget/entry.go b/widget/entry.go index 35a3626961..d25c55a10e 100644 --- a/widget/entry.go +++ b/widget/entry.go @@ -438,6 +438,9 @@ func (e *Entry) SelectedText() string { } start, stop := e.selection() + if start == stop { + return "" + } e.propertyLock.RLock() defer e.propertyLock.RUnlock() r := ([]rune)(e.textProvider().String()) diff --git a/widget/entry_test.go b/widget/entry_test.go index 2c53d14669..8ea6955023 100644 --- a/widget/entry_test.go +++ b/widget/entry_test.go @@ -311,6 +311,11 @@ func TestEntry_EmptySelection(t *testing.T) { typeKeys(entry, fyne.KeyRight, keyShiftLeftDown, fyne.KeyRight, fyne.KeyLeft, keyShiftLeftUp) assert.Equal(t, "", entry.SelectedText()) assert.Equal(t, 1, entry.CursorColumn) + + // manually setting to empty selection + typeKeys(entry, keyShiftLeftDown, fyne.KeyRight) + entry.CursorColumn = 1 + assert.Equal(t, "", entry.SelectedText()) } func TestEntry_Focus(t *testing.T) { From babd21da975ab2b5cd1990680c48ad65f3d9c742 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 8 Feb 2023 15:32:53 +0000 Subject: [PATCH 14/52] Fix trailing lines --- internal/driver/glfw/window_goxjs.go | 2 +- internal/driver/glfw/window_js.go | 11 +++++++++++ internal/driver/glfw/window_wasm.go | 8 ++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 internal/driver/glfw/window_js.go create mode 100644 internal/driver/glfw/window_wasm.go diff --git a/internal/driver/glfw/window_goxjs.go b/internal/driver/glfw/window_goxjs.go index 7bf3f1f977..36f111aa02 100644 --- a/internal/driver/glfw/window_goxjs.go +++ b/internal/driver/glfw/window_goxjs.go @@ -185,7 +185,7 @@ func (w *window) setCustomCursor(rawCursor *Cursor, isCustomCursor bool) { } func (w *window) mouseMoved(_ *glfw.Window, xpos, ypos float64) { - w.processMouseMoved(xpos, ypos) + w.processMouseMoved(w.scaleInput(xpos), w.scaleInput(ypos)) } func (w *window) mouseClicked(viewport *glfw.Window, btn glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) { diff --git a/internal/driver/glfw/window_js.go b/internal/driver/glfw/window_js.go new file mode 100644 index 0000000000..c1516d57f7 --- /dev/null +++ b/internal/driver/glfw/window_js.go @@ -0,0 +1,11 @@ +//go:build js && !wasm && !test_web_driver +// +build js,!wasm,!test_web_driver + +package glfw + +import "math" + +func (w *window) scaleInput(in float64) float64 { + return math.Ceil(in * float64(w.canvas.Scale())) + +} diff --git a/internal/driver/glfw/window_wasm.go b/internal/driver/glfw/window_wasm.go new file mode 100644 index 0000000000..8a5c99b238 --- /dev/null +++ b/internal/driver/glfw/window_wasm.go @@ -0,0 +1,8 @@ +//go:build wasm || test_web_driver +// +build wasm test_web_driver + +package glfw + +func (w *window) scaleInput(in float64) float64 { + return in +} From 5f19fdaafd4dc615212b93ff5ac7130c13a372d5 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sat, 21 Jan 2023 11:24:54 -0800 Subject: [PATCH 15/52] Fix issue where markdown image without title was lost Fixes #3577 --- widget/markdown.go | 6 ++++-- widget/markdown_test.go | 9 +++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/widget/markdown.go b/widget/markdown.go index e3dda2e879..626dbbdf7b 100644 --- a/widget/markdown.go +++ b/widget/markdown.go @@ -143,13 +143,15 @@ func (m *markdownRenderer) Render(_ io.Writer, source []byte, n ast.Node) error link.Text = link.Text + trimmed } - if !m.heading { + _, isImage := m.nextSeg.(*ImageSegment) + if !m.heading && !isImage { m.segs = append(m.segs, m.nextSeg) } case "Blockquote": m.blockquote = true case "Image": - m.nextSeg = makeImage(n.(*ast.Image)) + m.nextSeg = makeImage(n.(*ast.Image)) // remember this for applying title + m.segs = append(m.segs, m.nextSeg) } return ast.WalkContinue, nil diff --git a/widget/markdown_test.go b/widget/markdown_test.go index 70574b6d7b..ff8fc0cba6 100644 --- a/widget/markdown_test.go +++ b/widget/markdown_test.go @@ -149,6 +149,15 @@ func TestRichTextMarkdown_Image(t *testing.T) { } else { t.Error("Segment should be a Image") } + + r = NewRichTextFromMarkdown("![](../../theme/icons/fyne.png)") + + assert.Equal(t, 1, len(r.Segments)) + if img, ok := r.Segments[0].(*ImageSegment); ok { + assert.Equal(t, storage.NewFileURI("../../theme/icons/fyne.png"), img.Source) + } else { + t.Error("Segment should be a Image") + } } func TestRichTextMarkdown_Lines(t *testing.T) { From a702aa07a2f74fe9ab71ed56e72a2fb425be119f Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sun, 22 Jan 2023 09:47:58 -0800 Subject: [PATCH 16/52] De-complexify --- widget/markdown.go | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/widget/markdown.go b/widget/markdown.go index 626dbbdf7b..32a8d5070c 100644 --- a/widget/markdown.go +++ b/widget/markdown.go @@ -122,25 +122,9 @@ func (m *markdownRenderer) Render(_ io.Writer, source []byte, n ast.Node) error Style: RichTextStyleStrong, } case "Text": - trimmed := string(n.Text(source)) - trimmed = strings.ReplaceAll(trimmed, "\n", " ") // newline inside paragraph is not newline - if trimmed == "" { - return ast.WalkContinue, nil - } - if t, ok := m.nextSeg.(*TextSegment); ok { - next := n.(*ast.Text).NextSibling() - if next != nil { - if nextText, ok := next.(*ast.Text); ok { - if nextText.Segment.Start > n.(*ast.Text).Segment.Stop { // detect presence of a trailing newline - trimmed = trimmed + " " - } - } - } - - t.Text = t.Text + trimmed - } - if link, ok := m.nextSeg.(*HyperlinkSegment); ok { - link.Text = link.Text + trimmed + ret := addTextToSegment(string(n.Text(source)), m.nextSeg, n) + if ret != 0 { + return ret, nil } _, isImage := m.nextSeg.(*ImageSegment) @@ -186,6 +170,29 @@ func (m *markdownRenderer) handleExitNode(n ast.Node) error { return nil } +func addTextToSegment(text string, s RichTextSegment, node ast.Node) ast.WalkStatus { + trimmed := strings.ReplaceAll(text, "\n", " ") // newline inside paragraph is not newline + if trimmed == "" { + return ast.WalkContinue + } + if t, ok := s.(*TextSegment); ok { + next := node.(*ast.Text).NextSibling() + if next != nil { + if nextText, ok := next.(*ast.Text); ok { + if nextText.Segment.Start > node.(*ast.Text).Segment.Stop { // detect presence of a trailing newline + trimmed = trimmed + " " + } + } + } + + t.Text = t.Text + trimmed + } + if link, ok := s.(*HyperlinkSegment); ok { + link.Text = link.Text + trimmed + } + return 0 +} + func makeImage(n *ast.Image) *ImageSegment { dest := string(n.Destination) u, err := storage.ParseURI(dest) From 27a340e6918427239330a02cdeea61939fe0c177 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sat, 28 Jan 2023 14:41:39 -0800 Subject: [PATCH 17/52] Don't panic if there is no monitor detected Seems this can happen when display asleep or working through VNC etc. Fixes #3609, #2972 --- internal/driver/glfw/window_desktop.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/driver/glfw/window_desktop.go b/internal/driver/glfw/window_desktop.go index 916560e0d3..4be8d1ab27 100644 --- a/internal/driver/glfw/window_desktop.go +++ b/internal/driver/glfw/window_desktop.go @@ -281,6 +281,10 @@ func (w *window) detectScale() float32 { return 1.0 } monitor := w.getMonitorForWindow() + if monitor == nil { + return 1.0 + } + widthMm, _ := monitor.GetPhysicalSize() widthPx := monitor.GetVideoMode().Width From fc3393b43820e600660528c09b329ba4cf96ad7d Mon Sep 17 00:00:00 2001 From: Alex Ballas Date: Sat, 31 Dec 2022 10:35:40 +0200 Subject: [PATCH 18/52] Updated fix for #3291 --- widget/richtext.go | 7 ++++++- widget/richtext_test.go | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/widget/richtext.go b/widget/richtext.go index 0e56d5d99e..6614d9667e 100644 --- a/widget/richtext.go +++ b/widget/richtext.go @@ -873,7 +873,12 @@ func lineBounds(seg *TextSegment, wrap fyne.TextWrap, firstWidth, maxWidth float return bounds } } else { - high = low + findSpaceIndex(sub, fallback) + spaceIndex := findSpaceIndex(sub, fallback) + if spaceIndex == 0 { + spaceIndex = fallback + } + + high = low + spaceIndex } if high == fallback && subWidth <= maxWidth { // add a newline as there is more space on next bounds = append(bounds, rowBoundary{[]RichTextSegment{seg}, reuse, low, low}) diff --git a/widget/richtext_test.go b/widget/richtext_test.go index a7261edcb3..d7cb201489 100644 --- a/widget/richtext_test.go +++ b/widget/richtext_test.go @@ -896,6 +896,10 @@ func TestText_findSpaceIndex(t *testing.T) { text: "ww wwww www wwwww", want: 11, }, + "space beginning": { + text: " ww", + want: 0, + }, } { t.Run(name, func(t *testing.T) { assert.Equal(t, tt.want, findSpaceIndex([]rune(tt.text), len(tt.text)-1)) From 24a1b57844a0376a468bebf65dd6569c03a1520e Mon Sep 17 00:00:00 2001 From: Alex Ballas Date: Sat, 31 Dec 2022 16:55:30 +0200 Subject: [PATCH 19/52] set the spaceIndex to 1 if we receive a 0 value from the findSpaceIndex function --- widget/richtext.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/richtext.go b/widget/richtext.go index 6614d9667e..bc2a0f64ad 100644 --- a/widget/richtext.go +++ b/widget/richtext.go @@ -875,7 +875,7 @@ func lineBounds(seg *TextSegment, wrap fyne.TextWrap, firstWidth, maxWidth float } else { spaceIndex := findSpaceIndex(sub, fallback) if spaceIndex == 0 { - spaceIndex = fallback + spaceIndex = 1 } high = low + spaceIndex From bf222c7d5e1c4ade32a3eee304eb2dae166f4700 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Thu, 12 Jan 2023 14:30:06 -0700 Subject: [PATCH 20/52] Correctly build the srcdir using the package like go build does. --- cmd/fyne/internal/commands/build.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/cmd/fyne/internal/commands/build.go b/cmd/fyne/internal/commands/build.go index e69a74796e..439ed57912 100644 --- a/cmd/fyne/internal/commands/build.go +++ b/cmd/fyne/internal/commands/build.go @@ -197,14 +197,28 @@ func (b *Builder) build() error { } } + srcdir := b.srcdir + if b.goPackage != "" { + if strings.HasPrefix(b.goPackage, "./") { + srcdir = filepath.Join(srcdir, b.goPackage) + } else { + // get the output of go env GOROOT + goroot, err := fyneGoModRunner.runOutput("env", "GOROOT") + if err != nil { + return err + } + srcdir = filepath.Join(strings.TrimSpace(string(goroot)), "src", b.goPackage) + } + } + if b.icon == "" { - defaultIcon := filepath.Join(b.srcdir, "Icon.png") + defaultIcon := filepath.Join(srcdir, "Icon.png") if util.Exists(defaultIcon) { b.icon = defaultIcon } } - close, err := injectMetadataIfPossible(fyneGoModRunner, b.srcdir, b.appData, createMetadataInitFile) + close, err := injectMetadataIfPossible(fyneGoModRunner, srcdir, b.appData, createMetadataInitFile) if err != nil { fyne.LogError("Failed to inject metadata init file, omitting metadata", err) } else if close != nil { From d4ca996d4fdf90a89aea63aa2d8bbf8929865bc2 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Thu, 12 Jan 2023 14:56:01 -0700 Subject: [PATCH 21/52] Add test coverage. --- cmd/fyne/internal/commands/build_test.go | 34 ++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/cmd/fyne/internal/commands/build_test.go b/cmd/fyne/internal/commands/build_test.go index 07feab1be6..f5c2ba5bbf 100644 --- a/cmd/fyne/internal/commands/build_test.go +++ b/cmd/fyne/internal/commands/build_test.go @@ -230,6 +230,40 @@ func Test_BuildWasmOldVersion(t *testing.T) { wasmBuildTest.verifyExpectation() } +func Test_BuildLinuxReleaseVersion(t *testing.T) { + expected := []mockRunner{ + { + expectedValue: expectedValue{args: []string{"env", "GOROOT"}}, + mockReturn: mockReturn{ + ret: []byte("/usr/lib/go"), + }, + }, + { + expectedValue: expectedValue{args: []string{"mod", "edit", "-json"}}, + mockReturn: mockReturn{ + ret: []byte("{ \"Module\": { \"Path\": \"fyne.io/fyne/v2\"} }"), + }, + }, + { + expectedValue: expectedValue{ + args: []string{"build", "-ldflags", "-s -w", "-trimpath", "-tags", "release", "cmd/terminal"}, + env: []string{"CGO_ENABLED=1", "GOOS=linux"}, + osEnv: true, + dir: "myTest", + }, + mockReturn: mockReturn{ + ret: []byte(""), + }, + }, + } + + linuxBuildTest := &testCommandRuns{runs: expected, t: t} + b := &Builder{appData: &appData{}, os: "linux", srcdir: "myTest", release: true, runner: linuxBuildTest, goPackage: "cmd/terminal"} + err := b.build() + assert.Nil(t, err) + linuxBuildTest.verifyExpectation() +} + type jsonTest struct { expected bool json []byte From f9c2c103dd7ee18166235a5b0fbb5003b1a8827a Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Thu, 12 Jan 2023 15:04:25 -0700 Subject: [PATCH 22/52] Refactor code out. --- cmd/fyne/internal/commands/build.go | 34 +++++++++++++++++++---------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/cmd/fyne/internal/commands/build.go b/cmd/fyne/internal/commands/build.go index 439ed57912..f04dfd760f 100644 --- a/cmd/fyne/internal/commands/build.go +++ b/cmd/fyne/internal/commands/build.go @@ -197,18 +197,9 @@ func (b *Builder) build() error { } } - srcdir := b.srcdir - if b.goPackage != "" { - if strings.HasPrefix(b.goPackage, "./") { - srcdir = filepath.Join(srcdir, b.goPackage) - } else { - // get the output of go env GOROOT - goroot, err := fyneGoModRunner.runOutput("env", "GOROOT") - if err != nil { - return err - } - srcdir = filepath.Join(strings.TrimSpace(string(goroot)), "src", b.goPackage) - } + srcdir, err := b.computeSrcDir(fyneGoModRunner) + if err != nil { + return err } if b.icon == "" { @@ -296,6 +287,25 @@ func (b *Builder) build() error { return err } +func (b *Builder) computeSrcDir(fyneGoModRunner runner) (string, error) { + if b.goPackage == "" { + return b.srcdir, nil + } + + srcdir := b.srcdir + if strings.HasPrefix(b.goPackage, "./") { + srcdir = filepath.Join(srcdir, b.goPackage) + } else { + // get the output of go env GOROOT + goroot, err := fyneGoModRunner.runOutput("env", "GOROOT") + if err != nil { + return "", err + } + srcdir = filepath.Join(strings.TrimSpace(string(goroot)), "src", b.goPackage) + } + return srcdir, nil +} + func createMetadataInitFile(srcdir string, app *appData) (func(), error) { data, err := metadata.LoadStandard(srcdir) if err == nil { From 550f45203873ed52263d59ddd0d587c06e8bd3fd Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Fri, 13 Jan 2023 11:09:44 -0700 Subject: [PATCH 23/52] fyne build will only support building relative and absolute path for the moment. --- cmd/fyne/internal/commands/build.go | 14 ++++++-------- cmd/fyne/internal/commands/build_test.go | 10 ++-------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/cmd/fyne/internal/commands/build.go b/cmd/fyne/internal/commands/build.go index f04dfd760f..30a069b69c 100644 --- a/cmd/fyne/internal/commands/build.go +++ b/cmd/fyne/internal/commands/build.go @@ -288,20 +288,18 @@ func (b *Builder) build() error { } func (b *Builder) computeSrcDir(fyneGoModRunner runner) (string, error) { - if b.goPackage == "" { + if b.goPackage == "" || b.goPackage == "." { return b.srcdir, nil } srcdir := b.srcdir - if strings.HasPrefix(b.goPackage, "./") { + if strings.HasPrefix(b.goPackage, "."+string(os.PathSeparator)) || + strings.HasPrefix(b.goPackage, ".."+string(os.PathSeparator)) { srcdir = filepath.Join(srcdir, b.goPackage) + } else if strings.HasPrefix(b.goPackage, string(os.PathSeparator)) { + srcdir = b.goPackage } else { - // get the output of go env GOROOT - goroot, err := fyneGoModRunner.runOutput("env", "GOROOT") - if err != nil { - return "", err - } - srcdir = filepath.Join(strings.TrimSpace(string(goroot)), "src", b.goPackage) + return "", fmt.Errorf("unrecognized go package: %s", b.goPackage) } return srcdir, nil } diff --git a/cmd/fyne/internal/commands/build_test.go b/cmd/fyne/internal/commands/build_test.go index f5c2ba5bbf..8ee67def79 100644 --- a/cmd/fyne/internal/commands/build_test.go +++ b/cmd/fyne/internal/commands/build_test.go @@ -232,12 +232,6 @@ func Test_BuildWasmOldVersion(t *testing.T) { func Test_BuildLinuxReleaseVersion(t *testing.T) { expected := []mockRunner{ - { - expectedValue: expectedValue{args: []string{"env", "GOROOT"}}, - mockReturn: mockReturn{ - ret: []byte("/usr/lib/go"), - }, - }, { expectedValue: expectedValue{args: []string{"mod", "edit", "-json"}}, mockReturn: mockReturn{ @@ -246,7 +240,7 @@ func Test_BuildLinuxReleaseVersion(t *testing.T) { }, { expectedValue: expectedValue{ - args: []string{"build", "-ldflags", "-s -w", "-trimpath", "-tags", "release", "cmd/terminal"}, + args: []string{"build", "-ldflags", "-s -w", "-trimpath", "-tags", "release", "./cmd/terminal"}, env: []string{"CGO_ENABLED=1", "GOOS=linux"}, osEnv: true, dir: "myTest", @@ -258,7 +252,7 @@ func Test_BuildLinuxReleaseVersion(t *testing.T) { } linuxBuildTest := &testCommandRuns{runs: expected, t: t} - b := &Builder{appData: &appData{}, os: "linux", srcdir: "myTest", release: true, runner: linuxBuildTest, goPackage: "cmd/terminal"} + b := &Builder{appData: &appData{}, os: "linux", srcdir: "myTest", release: true, runner: linuxBuildTest, goPackage: "./cmd/terminal"} err := b.build() assert.Nil(t, err) linuxBuildTest.verifyExpectation() From 29e91bd2ee07b8d324908c4d4c53dd9700f228d6 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Fri, 13 Jan 2023 11:20:06 -0700 Subject: [PATCH 24/52] Abstract OS in tests too. --- cmd/fyne/internal/commands/build_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/fyne/internal/commands/build_test.go b/cmd/fyne/internal/commands/build_test.go index 8ee67def79..1d4685a3cd 100644 --- a/cmd/fyne/internal/commands/build_test.go +++ b/cmd/fyne/internal/commands/build_test.go @@ -2,6 +2,7 @@ package commands import ( "fmt" + "path/filepath" "runtime" "testing" @@ -240,7 +241,7 @@ func Test_BuildLinuxReleaseVersion(t *testing.T) { }, { expectedValue: expectedValue{ - args: []string{"build", "-ldflags", "-s -w", "-trimpath", "-tags", "release", "./cmd/terminal"}, + args: []string{"build", "-ldflags", "-s -w", "-trimpath", "-tags", "release", filepath.Join(".", "cmd", "terminal")}, env: []string{"CGO_ENABLED=1", "GOOS=linux"}, osEnv: true, dir: "myTest", @@ -252,7 +253,7 @@ func Test_BuildLinuxReleaseVersion(t *testing.T) { } linuxBuildTest := &testCommandRuns{runs: expected, t: t} - b := &Builder{appData: &appData{}, os: "linux", srcdir: "myTest", release: true, runner: linuxBuildTest, goPackage: "./cmd/terminal"} + b := &Builder{appData: &appData{}, os: "linux", srcdir: "myTest", release: true, runner: linuxBuildTest, goPackage: filepath.Join(".", "cmd", "terminal")} err := b.build() assert.Nil(t, err) linuxBuildTest.verifyExpectation() From 9f2e7ebf4746f5dd28f445e8474bbf0bf56d67b9 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Fri, 13 Jan 2023 11:29:46 -0700 Subject: [PATCH 25/52] Actually force produce a relative path. --- cmd/fyne/internal/commands/build_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/fyne/internal/commands/build_test.go b/cmd/fyne/internal/commands/build_test.go index 1d4685a3cd..43bb5471fe 100644 --- a/cmd/fyne/internal/commands/build_test.go +++ b/cmd/fyne/internal/commands/build_test.go @@ -2,6 +2,7 @@ package commands import ( "fmt" + "os" "path/filepath" "runtime" "testing" @@ -232,6 +233,8 @@ func Test_BuildWasmOldVersion(t *testing.T) { } func Test_BuildLinuxReleaseVersion(t *testing.T) { + relativePath := "." + string(os.PathSeparator) + filepath.Join("cmd", "terminal") + expected := []mockRunner{ { expectedValue: expectedValue{args: []string{"mod", "edit", "-json"}}, @@ -241,7 +244,7 @@ func Test_BuildLinuxReleaseVersion(t *testing.T) { }, { expectedValue: expectedValue{ - args: []string{"build", "-ldflags", "-s -w", "-trimpath", "-tags", "release", filepath.Join(".", "cmd", "terminal")}, + args: []string{"build", "-ldflags", "-s -w", "-trimpath", "-tags", "release", relativePath}, env: []string{"CGO_ENABLED=1", "GOOS=linux"}, osEnv: true, dir: "myTest", @@ -253,7 +256,7 @@ func Test_BuildLinuxReleaseVersion(t *testing.T) { } linuxBuildTest := &testCommandRuns{runs: expected, t: t} - b := &Builder{appData: &appData{}, os: "linux", srcdir: "myTest", release: true, runner: linuxBuildTest, goPackage: filepath.Join(".", "cmd", "terminal")} + b := &Builder{appData: &appData{}, os: "linux", srcdir: "myTest", release: true, runner: linuxBuildTest, goPackage: relativePath} err := b.build() assert.Nil(t, err) linuxBuildTest.verifyExpectation() From 3b3903f26f5d160c5e003aabaaf91b79d1cfc6ab Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Fri, 13 Jan 2023 15:53:08 -0700 Subject: [PATCH 26/52] Make sure that all the build put their result in p.dir. --- cmd/fyne/internal/commands/package-unix.go | 2 +- cmd/fyne/internal/commands/package-windows.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/fyne/internal/commands/package-unix.go b/cmd/fyne/internal/commands/package-unix.go index 819cf388e7..806523153b 100644 --- a/cmd/fyne/internal/commands/package-unix.go +++ b/cmd/fyne/internal/commands/package-unix.go @@ -68,7 +68,7 @@ func (p *Packager) packageUNIX() error { } var buf bytes.Buffer - tarCmd := execabs.Command("tar", "-Jcf", p.Name+".tar.xz", "-C", tempDir, "usr", "Makefile") + tarCmd := execabs.Command("tar", "-Jcf", filepath.Join(p.dir, p.Name+".tar.xz"), "-C", filepath.Join(p.dir, tempDir), "usr", "Makefile") tarCmd.Stderr = &buf if err = tarCmd.Run(); err != nil { return fmt.Errorf("failed to create archive with tar: %s - %w", buf.String(), err) diff --git a/cmd/fyne/internal/commands/package-windows.go b/cmd/fyne/internal/commands/package-windows.go index 72b21b978d..036f8a2451 100644 --- a/cmd/fyne/internal/commands/package-windows.go +++ b/cmd/fyne/internal/commands/package-windows.go @@ -115,7 +115,7 @@ func (p *Packager) packageWindows() error { if filepath.Ext(p.Name) != ".exe" { appName = appName + ".exe" } - appPath = filepath.Join(filepath.Dir(p.exe), appName) + appPath = filepath.Join(p.dir, appName) os.Rename(filepath.Base(p.exe), appName) } From cfb29ff0cbb010e1620810af730a539dc107d310 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Fri, 13 Jan 2023 12:45:13 -0700 Subject: [PATCH 27/52] Honor GOFLAGS and allow for -ldflags command parameter to fyne build. --- cmd/fyne/internal/commands/build.go | 39 +++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/cmd/fyne/internal/commands/build.go b/cmd/fyne/internal/commands/build.go index 30a069b69c..fd7a951dc1 100644 --- a/cmd/fyne/internal/commands/build.go +++ b/cmd/fyne/internal/commands/build.go @@ -25,6 +25,7 @@ type Builder struct { release bool tags []string tagsToParse string + ldFlags string customMetadata keyValueFlag @@ -72,6 +73,11 @@ func Build() *cli.Command { Usage: "Specify custom metadata key value pair that you do not want to store in your FyneApp.toml (key=value)", Value: &b.customMetadata, }, + &cli.StringFlag{ + Name: "ldflags", + Usage: "A string of flags to pass to the linker. See https://golang.org/cmd/link/ for more information.", + Destination: &b.ldFlags, + }, }, Action: func(ctx *cli.Context) error { argCount := ctx.Args().Len() @@ -227,17 +233,25 @@ func (b *Builder) build() error { if !isWeb(goos) { env = append(env, "CGO_ENABLED=1") // in case someone is trying to cross-compile... + if b.release { + appendEnv(&env, "GOFLAGS", "-s -w") + args = append(args, "-trimpath") + } + if goos == "windows" { - if b.release { - args = append(args, "-ldflags", "-s -w -H=windowsgui", "-trimpath") - } else { - args = append(args, "-ldflags", "-H=windowsgui ") - } - } else if b.release { - args = append(args, "-ldflags", "-s -w", "-trimpath") + appendEnv(&env, "GOFLAGS", "-H=windowsgui") } } + if b.ldFlags != "" { + appendEnv(&env, "GOFLAGS", b.ldFlags) + } + + ldFlags := getEnv(env, "GOFLAGS") + if len(ldFlags) > 0 { + args = append(args, "-ldflags", ldFlags) + } + if b.target != "" { args = append(args, "-o", b.target) } @@ -379,3 +393,14 @@ func appendEnv(env *[]string, varName, value string) { *env = append(*env, varName+"="+value) } + +func getEnv(env []string, varName string) string { + for _, e := range env { + keyValue := strings.SplitN(e, "=", 2) + + if keyValue[0] == "GOFLAGS" { + return keyValue[1] + } + } + return "" +} From d86cc108ab77b393b618613e59985418d7c3ec48 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Mon, 16 Jan 2023 11:21:20 -0700 Subject: [PATCH 28/52] Properly lookup for ldflags inside GOFLAGS. --- cmd/fyne/internal/commands/build.go | 43 +++++++++++++++++------- cmd/fyne/internal/commands/build_test.go | 21 ++++++++++++ 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/cmd/fyne/internal/commands/build.go b/cmd/fyne/internal/commands/build.go index fd7a951dc1..cb8b42c4dc 100644 --- a/cmd/fyne/internal/commands/build.go +++ b/cmd/fyne/internal/commands/build.go @@ -234,22 +234,27 @@ func (b *Builder) build() error { env = append(env, "CGO_ENABLED=1") // in case someone is trying to cross-compile... if b.release { - appendEnv(&env, "GOFLAGS", "-s -w") + b.ldFlags += " -s -w" args = append(args, "-trimpath") } if goos == "windows" { - appendEnv(&env, "GOFLAGS", "-H=windowsgui") + b.ldFlags += " -H=windowsgui" } } - if b.ldFlags != "" { - appendEnv(&env, "GOFLAGS", b.ldFlags) + goFlags := os.Getenv("GOFLAGS") + goLdFlags, goFlags := extractLdFlags(goFlags) + if goLdFlags != "" { + b.ldFlags += " " + goLdFlags + } + if goFlags != "" { + os.Setenv("GOFLAGS", goFlags) } - ldFlags := getEnv(env, "GOFLAGS") - if len(ldFlags) > 0 { - args = append(args, "-ldflags", ldFlags) + if len(b.ldFlags) > 0 { + b.ldFlags = strings.TrimSpace(b.ldFlags) + args = append(args, "-ldflags", b.ldFlags) } if b.target != "" { @@ -394,13 +399,25 @@ func appendEnv(env *[]string, varName, value string) { *env = append(*env, varName+"="+value) } -func getEnv(env []string, varName string) string { - for _, e := range env { - keyValue := strings.SplitN(e, "=", 2) +func extractLdFlags(goFlags string) (string, string) { + if goFlags == "" { + return "", "" + } + + flags := strings.Fields(goFlags) + ldflags := "" + newGoFlags := "" - if keyValue[0] == "GOFLAGS" { - return keyValue[1] + for _, flag := range flags { + if strings.HasPrefix(flag, "-ldflags=") { + ldflags += strings.TrimPrefix(flag, "-ldflags=") + " " + } else { + newGoFlags += flag + " " } } - return "" + + ldflags = strings.TrimSpace(ldflags) + newGoFlags = strings.TrimSpace(newGoFlags) + + return ldflags, newGoFlags } diff --git a/cmd/fyne/internal/commands/build_test.go b/cmd/fyne/internal/commands/build_test.go index 43bb5471fe..f0c4f78c13 100644 --- a/cmd/fyne/internal/commands/build_test.go +++ b/cmd/fyne/internal/commands/build_test.go @@ -313,3 +313,24 @@ func Test_AppendEnv(t *testing.T) { assert.Equal(t, "foo2=baz2", env[3]) } } + +type extractTest struct { + value string + wantLdFlags string + wantGoFlags string +} + +func Test_ExtractLdFlags(t *testing.T) { + goFlagsTests := []extractTest{ + {"-ldflags=-w", "-w", ""}, + {"-ldflags=-s", "-s", ""}, + {"-ldflags=-w -ldflags=-s", "-w -s", ""}, + {"-mod=vendor", "", "-mod=vendor"}, + } + + for _, test := range goFlagsTests { + ldFlags, goFlags := extractLdFlags(test.value) + assert.Equal(t, test.wantLdFlags, ldFlags) + assert.Equal(t, test.wantGoFlags, goFlags) + } +} From 19cc6da8d12137954d6e03f04694510f2caa51a4 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Mon, 16 Jan 2023 11:33:19 -0700 Subject: [PATCH 29/52] Correct tests after merging develop. --- cmd/fyne/internal/commands/build_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/fyne/internal/commands/build_test.go b/cmd/fyne/internal/commands/build_test.go index f0c4f78c13..80c8ac3c19 100644 --- a/cmd/fyne/internal/commands/build_test.go +++ b/cmd/fyne/internal/commands/build_test.go @@ -244,7 +244,7 @@ func Test_BuildLinuxReleaseVersion(t *testing.T) { }, { expectedValue: expectedValue{ - args: []string{"build", "-ldflags", "-s -w", "-trimpath", "-tags", "release", relativePath}, + args: []string{"build", "-trimpath", "-ldflags", "-s -w", "-tags", "release", relativePath}, env: []string{"CGO_ENABLED=1", "GOOS=linux"}, osEnv: true, dir: "myTest", From 84e7bb1d89648796c5f1cd6c728c9fef9bd78242 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Mon, 16 Jan 2023 11:41:07 -0700 Subject: [PATCH 30/52] Address go cyclo issue. --- cmd/fyne/internal/commands/build.go | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/cmd/fyne/internal/commands/build.go b/cmd/fyne/internal/commands/build.go index cb8b42c4dc..85fc633591 100644 --- a/cmd/fyne/internal/commands/build.go +++ b/cmd/fyne/internal/commands/build.go @@ -243,17 +243,9 @@ func (b *Builder) build() error { } } - goFlags := os.Getenv("GOFLAGS") - goLdFlags, goFlags := extractLdFlags(goFlags) - if goLdFlags != "" { - b.ldFlags += " " + goLdFlags - } - if goFlags != "" { - os.Setenv("GOFLAGS", goFlags) - } + b.updateGoLdFlags() if len(b.ldFlags) > 0 { - b.ldFlags = strings.TrimSpace(b.ldFlags) args = append(args, "-ldflags", b.ldFlags) } @@ -306,6 +298,19 @@ func (b *Builder) build() error { return err } +func (b *Builder) updateGoLdFlags() { + goFlags := os.Getenv("GOFLAGS") + goLdFlags, goFlags := extractLdFlags(goFlags) + if goLdFlags != "" { + b.ldFlags += " " + goLdFlags + } + if goFlags != "" { + os.Setenv("GOFLAGS", goFlags) + } + + b.ldFlags = strings.TrimSpace(b.ldFlags) +} + func (b *Builder) computeSrcDir(fyneGoModRunner runner) (string, error) { if b.goPackage == "" || b.goPackage == "." { return b.srcdir, nil From 74f581b07b817b040ed6ed537ce68fcdf32b6158 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Tue, 17 Jan 2023 11:26:17 -0700 Subject: [PATCH 31/52] Remove -ldflags parameters to fyne. --- cmd/fyne/internal/commands/build.go | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/cmd/fyne/internal/commands/build.go b/cmd/fyne/internal/commands/build.go index 85fc633591..c6fc43ec70 100644 --- a/cmd/fyne/internal/commands/build.go +++ b/cmd/fyne/internal/commands/build.go @@ -25,7 +25,6 @@ type Builder struct { release bool tags []string tagsToParse string - ldFlags string customMetadata keyValueFlag @@ -73,11 +72,6 @@ func Build() *cli.Command { Usage: "Specify custom metadata key value pair that you do not want to store in your FyneApp.toml (key=value)", Value: &b.customMetadata, }, - &cli.StringFlag{ - Name: "ldflags", - Usage: "A string of flags to pass to the linker. See https://golang.org/cmd/link/ for more information.", - Destination: &b.ldFlags, - }, }, Action: func(ctx *cli.Context) error { argCount := ctx.Args().Len() @@ -230,23 +224,23 @@ func (b *Builder) build() error { appendEnv(&env, "CGO_LDFLAGS", "-mmacosx-version-min=10.11") } + ldFlags := "" if !isWeb(goos) { env = append(env, "CGO_ENABLED=1") // in case someone is trying to cross-compile... if b.release { - b.ldFlags += " -s -w" + ldFlags += " -s -w" args = append(args, "-trimpath") } if goos == "windows" { - b.ldFlags += " -H=windowsgui" + ldFlags += " -H=windowsgui" } } - b.updateGoLdFlags() - - if len(b.ldFlags) > 0 { - args = append(args, "-ldflags", b.ldFlags) + ldFlags = updateGoLdFlags(ldFlags) + if len(ldFlags) > 0 { + args = append(args, "-ldflags", ldFlags) } if b.target != "" { @@ -298,17 +292,15 @@ func (b *Builder) build() error { return err } -func (b *Builder) updateGoLdFlags() { +func updateGoLdFlags(ldFlags string) string { goFlags := os.Getenv("GOFLAGS") goLdFlags, goFlags := extractLdFlags(goFlags) if goLdFlags != "" { - b.ldFlags += " " + goLdFlags - } - if goFlags != "" { - os.Setenv("GOFLAGS", goFlags) + ldFlags += " " + goLdFlags } + os.Setenv("GOFLAGS", goFlags) - b.ldFlags = strings.TrimSpace(b.ldFlags) + return strings.TrimSpace(ldFlags) } func (b *Builder) computeSrcDir(fyneGoModRunner runner) (string, error) { From 9c13e735994885d2372944509f57fc2693d58198 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Tue, 17 Jan 2023 11:33:19 -0700 Subject: [PATCH 32/52] Don't set environment when it isn't necessary. --- cmd/fyne/internal/commands/build.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/fyne/internal/commands/build.go b/cmd/fyne/internal/commands/build.go index c6fc43ec70..e1097288e7 100644 --- a/cmd/fyne/internal/commands/build.go +++ b/cmd/fyne/internal/commands/build.go @@ -298,7 +298,11 @@ func updateGoLdFlags(ldFlags string) string { if goLdFlags != "" { ldFlags += " " + goLdFlags } - os.Setenv("GOFLAGS", goFlags) + if goFlags != "" { + os.Setenv("GOFLAGS", goFlags) + } else { + os.Unsetenv("GOFLAGS") + } return strings.TrimSpace(ldFlags) } From d407dea636f3fec700fff4e581c19cd889db64e9 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Tue, 17 Jan 2023 14:44:28 -0700 Subject: [PATCH 33/52] Refactor code by extracting the ldflags first. --- cmd/fyne/internal/commands/build.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/cmd/fyne/internal/commands/build.go b/cmd/fyne/internal/commands/build.go index e1097288e7..11753b18d1 100644 --- a/cmd/fyne/internal/commands/build.go +++ b/cmd/fyne/internal/commands/build.go @@ -224,7 +224,7 @@ func (b *Builder) build() error { appendEnv(&env, "CGO_LDFLAGS", "-mmacosx-version-min=10.11") } - ldFlags := "" + ldFlags := extractLdflagsFromGoFlags() if !isWeb(goos) { env = append(env, "CGO_ENABLED=1") // in case someone is trying to cross-compile... @@ -238,9 +238,8 @@ func (b *Builder) build() error { } } - ldFlags = updateGoLdFlags(ldFlags) if len(ldFlags) > 0 { - args = append(args, "-ldflags", ldFlags) + args = append(args, "-ldflags", strings.TrimSpace(ldFlags)) } if b.target != "" { @@ -292,19 +291,17 @@ func (b *Builder) build() error { return err } -func updateGoLdFlags(ldFlags string) string { +func extractLdflagsFromGoFlags() string { goFlags := os.Getenv("GOFLAGS") - goLdFlags, goFlags := extractLdFlags(goFlags) - if goLdFlags != "" { - ldFlags += " " + goLdFlags - } + + ldFlags, goFlags := extractLdFlags(goFlags) if goFlags != "" { os.Setenv("GOFLAGS", goFlags) } else { os.Unsetenv("GOFLAGS") } - return strings.TrimSpace(ldFlags) + return ldFlags } func (b *Builder) computeSrcDir(fyneGoModRunner runner) (string, error) { From 574d9069662997998b2f0407ebdf20c48d9a542a Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Tue, 17 Jan 2023 14:45:59 -0700 Subject: [PATCH 34/52] Group function together. --- cmd/fyne/internal/commands/build.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/cmd/fyne/internal/commands/build.go b/cmd/fyne/internal/commands/build.go index 11753b18d1..2d360aefef 100644 --- a/cmd/fyne/internal/commands/build.go +++ b/cmd/fyne/internal/commands/build.go @@ -291,19 +291,6 @@ func (b *Builder) build() error { return err } -func extractLdflagsFromGoFlags() string { - goFlags := os.Getenv("GOFLAGS") - - ldFlags, goFlags := extractLdFlags(goFlags) - if goFlags != "" { - os.Setenv("GOFLAGS", goFlags) - } else { - os.Unsetenv("GOFLAGS") - } - - return ldFlags -} - func (b *Builder) computeSrcDir(fyneGoModRunner runner) (string, error) { if b.goPackage == "" || b.goPackage == "." { return b.srcdir, nil @@ -397,6 +384,19 @@ func appendEnv(env *[]string, varName, value string) { *env = append(*env, varName+"="+value) } +func extractLdflagsFromGoFlags() string { + goFlags := os.Getenv("GOFLAGS") + + ldFlags, goFlags := extractLdFlags(goFlags) + if goFlags != "" { + os.Setenv("GOFLAGS", goFlags) + } else { + os.Unsetenv("GOFLAGS") + } + + return ldFlags +} + func extractLdFlags(goFlags string) (string, string) { if goFlags == "" { return "", "" From cb3a0ffd4346ef08b4159e5974b64ef19eecbc38 Mon Sep 17 00:00:00 2001 From: Mauro Grassia Date: Tue, 24 Jan 2023 00:03:37 +0100 Subject: [PATCH 35/52] Call systray.Quit() when the application is closing --- internal/driver/glfw/driver_desktop.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/driver/glfw/driver_desktop.go b/internal/driver/glfw/driver_desktop.go index 0496ac662c..95d04655f8 100644 --- a/internal/driver/glfw/driver_desktop.go +++ b/internal/driver/glfw/driver_desktop.go @@ -54,6 +54,9 @@ func (d *gLDriver) SetSystemTrayMenu(m *fyne.Menu) { w.SetCloseIntercept(func() { d.Quit() }) + w.SetOnClosed(func() { + systray.Quit() + }) }) d.refreshSystray(m) From 77da9c32fd7376edc2476647734a63443a6c01cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0smail=20Bulut?= <73897211+hismailbulut@users.noreply.github.com> Date: Sun, 8 Jan 2023 18:11:13 +0300 Subject: [PATCH 36/52] Fix #3459 by changing all srcdir's to it's absolute path --- cmd/fyne/internal/commands/build.go | 11 +++++++++++ cmd/fyne/internal/commands/package.go | 15 ++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/cmd/fyne/internal/commands/build.go b/cmd/fyne/internal/commands/build.go index 2d360aefef..ed0f41d0e8 100644 --- a/cmd/fyne/internal/commands/build.go +++ b/cmd/fyne/internal/commands/build.go @@ -90,6 +90,13 @@ func Build() *cli.Command { // Build parse the tags and start building func (b *Builder) Build() error { if b.srcdir != "" { + // Use absolute srcdir + if !filepath.IsAbs(b.srcdir) { + absSrcDir, err := filepath.Abs(b.srcdir) + if err == nil { + b.srcdir = absSrcDir + } + } dirStat, err := os.Stat(b.srcdir) if err != nil { return err @@ -323,6 +330,10 @@ func createMetadataInitFile(srcdir string, app *appData) (func(), error) { app.ResGoString = "nil" if app.icon != "" { + // Icon path should relative to FyneApp.toml and it is in the srcdir + if !filepath.IsAbs(app.icon) { + app.icon = filepath.Join(srcdir, app.icon) + } res, err := fyne.LoadResourceFromPath(app.icon) if err != nil { fyne.LogError("Unable to load medadata icon file "+app.icon, err) diff --git a/cmd/fyne/internal/commands/package.go b/cmd/fyne/internal/commands/package.go index 157867a7d2..f545e03482 100644 --- a/cmd/fyne/internal/commands/package.go +++ b/cmd/fyne/internal/commands/package.go @@ -346,9 +346,18 @@ func (p *Packager) validate() (err error) { } if p.srcDir == "" { p.srcDir = baseDir - } else if p.os == "ios" || p.os == "android" { - return errors.New("parameter -sourceDir is currently not supported for mobile builds. " + - "Change directory to the main package and try again") + } else { + if p.os == "ios" || p.os == "android" { + return errors.New("parameter -sourceDir is currently not supported for mobile builds. " + + "Change directory to the main package and try again") + } + // Use absolute srcdir + if !filepath.IsAbs(p.srcDir) { + absSrcDir, err := filepath.Abs(p.srcDir) + if err == nil { + p.srcDir = absSrcDir + } + } } os.Chdir(p.srcDir) From d445f6d928a3aab4d8d5e5639afb821c0ba2c478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0smail=20Bulut?= <73897211+hismailbulut@users.noreply.github.com> Date: Mon, 23 Jan 2023 18:19:42 +0300 Subject: [PATCH 37/52] Add required utility functions --- cmd/fyne/internal/commands/build.go | 15 +++-------- .../commands/package-util-mock_test.go | 10 ++++++++ cmd/fyne/internal/commands/package-util.go | 10 ++++++++ cmd/fyne/internal/commands/package.go | 11 +++----- cmd/fyne/internal/util/file.go | 25 +++++++++++++++++++ 5 files changed, 53 insertions(+), 18 deletions(-) diff --git a/cmd/fyne/internal/commands/build.go b/cmd/fyne/internal/commands/build.go index ed0f41d0e8..842a5e433d 100644 --- a/cmd/fyne/internal/commands/build.go +++ b/cmd/fyne/internal/commands/build.go @@ -90,13 +90,7 @@ func Build() *cli.Command { // Build parse the tags and start building func (b *Builder) Build() error { if b.srcdir != "" { - // Use absolute srcdir - if !filepath.IsAbs(b.srcdir) { - absSrcDir, err := filepath.Abs(b.srcdir) - if err == nil { - b.srcdir = absSrcDir - } - } + b.srcdir = util.EnsureAbsPath(b.srcdir) dirStat, err := os.Stat(b.srcdir) if err != nil { return err @@ -318,6 +312,9 @@ func (b *Builder) computeSrcDir(fyneGoModRunner runner) (string, error) { func createMetadataInitFile(srcdir string, app *appData) (func(), error) { data, err := metadata.LoadStandard(srcdir) if err == nil { + // When icon path specified in metadata file, we should make it relative to metadata file + data.Details.Icon = util.MakePathRelative(srcdir, data.Details.Icon) + app.mergeMetadata(data) } @@ -330,10 +327,6 @@ func createMetadataInitFile(srcdir string, app *appData) (func(), error) { app.ResGoString = "nil" if app.icon != "" { - // Icon path should relative to FyneApp.toml and it is in the srcdir - if !filepath.IsAbs(app.icon) { - app.icon = filepath.Join(srcdir, app.icon) - } res, err := fyne.LoadResourceFromPath(app.icon) if err != nil { fyne.LogError("Unable to load medadata icon file "+app.icon, err) diff --git a/cmd/fyne/internal/commands/package-util-mock_test.go b/cmd/fyne/internal/commands/package-util-mock_test.go index dafea0a29a..86780e4956 100644 --- a/cmd/fyne/internal/commands/package-util-mock_test.go +++ b/cmd/fyne/internal/commands/package-util-mock_test.go @@ -12,6 +12,8 @@ var utilCopyFileMock func(source string, target string) error var utilCopyExeFileMock func(src, tgt string) error var utilWriteFileMock func(target string, data []byte) error var utilEnsureSubDirMock func(parent, name string) string +var utilEnsureAbsPathMock func(path string) string +var utilMakePathRelativeMock func(root, path string) string var utilRequireAndroidSDKMock func() error var utilAndroidBuildToolsPathMock func() string @@ -139,6 +141,14 @@ func (m mockUtil) EnsureSubDir(parent, name string) string { return utilEnsureSubDirMock(parent, name) } +func (m mockUtil) EnsureAbsPath(path string) string { + return utilEnsureAbsPathMock(path) +} + +func (m mockUtil) MakePathRelative(root, path string) string { + return utilMakePathRelativeMock(root, path) +} + func (m mockUtil) RequireAndroidSDK() error { return utilRequireAndroidSDKMock() } diff --git a/cmd/fyne/internal/commands/package-util.go b/cmd/fyne/internal/commands/package-util.go index b1b232211a..4192a1639e 100644 --- a/cmd/fyne/internal/commands/package-util.go +++ b/cmd/fyne/internal/commands/package-util.go @@ -12,6 +12,8 @@ type packagerUtil interface { CopyExeFile(src, tgt string) error WriteFile(target string, data []byte) error EnsureSubDir(parent, name string) string + EnsureAbsPath(path string) string + MakePathRelative(root, path string) string RequireAndroidSDK() error AndroidBuildToolsPath() string @@ -43,6 +45,14 @@ func (d defaultUtil) EnsureSubDir(parent, name string) string { return realUtil.EnsureSubDir(parent, name) } +func (d defaultUtil) EnsureAbsPath(path string) string { + return realUtil.EnsureAbsPath(path) +} + +func (d defaultUtil) MakePathRelative(root, path string) string { + return realUtil.MakePathRelative(root, path) +} + func (d defaultUtil) RequireAndroidSDK() error { return realUtil.RequireAndroidSDK() } diff --git a/cmd/fyne/internal/commands/package.go b/cmd/fyne/internal/commands/package.go index f545e03482..4ec5a68cbc 100644 --- a/cmd/fyne/internal/commands/package.go +++ b/cmd/fyne/internal/commands/package.go @@ -351,13 +351,7 @@ func (p *Packager) validate() (err error) { return errors.New("parameter -sourceDir is currently not supported for mobile builds. " + "Change directory to the main package and try again") } - // Use absolute srcdir - if !filepath.IsAbs(p.srcDir) { - absSrcDir, err := filepath.Abs(p.srcDir) - if err == nil { - p.srcDir = absSrcDir - } - } + p.srcDir = util.EnsureAbsPath(p.srcDir) } os.Chdir(p.srcDir) @@ -365,6 +359,9 @@ func (p *Packager) validate() (err error) { data, err := metadata.LoadStandard(p.srcDir) if err == nil { + // When icon path specified in metadata file, we should make it relative to metadata file + data.Details.Icon = util.MakePathRelative(p.srcDir, data.Details.Icon) + p.appData.Release = p.release p.appData.mergeMetadata(data) } diff --git a/cmd/fyne/internal/util/file.go b/cmd/fyne/internal/util/file.go index cd21f07d62..b49c8f7387 100644 --- a/cmd/fyne/internal/util/file.go +++ b/cmd/fyne/internal/util/file.go @@ -39,6 +39,31 @@ func EnsureSubDir(parent, name string) string { return path } +// EnsureAbsPath returns the absolute path of the given file if it is not already absolute +func EnsureAbsPath(path string) string { + if !filepath.IsAbs(path) { + abs, err := filepath.Abs(path) + if err != nil { + fyne.LogError("Failed to find absolute path", err) + } else { + return abs + } + } + return path +} + +// MakePathRelative joins root with path if path is not absolute and exists in root +// both root and path must non-empty, otherwise path will be returned +func MakePathRelative(root, path string) string { + if root != "" && path != "" && !filepath.IsAbs(path) { + joined := filepath.Join(root, path) + if Exists(joined) { + return joined + } + } + return path +} + func copyFileMode(src, tgt string, perm os.FileMode) (err error) { if _, err := os.Stat(src); err != nil { return err From da455e5feca5374871840c7c49f168e10f412570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0smail=20Bulut?= <73897211+hismailbulut@users.noreply.github.com> Date: Tue, 24 Jan 2023 23:42:07 +0300 Subject: [PATCH 38/52] Change style --- cmd/fyne/internal/commands/build.go | 2 +- .../commands/package-util-mock_test.go | 6 ++-- cmd/fyne/internal/commands/package-util.go | 6 ++-- cmd/fyne/internal/commands/package.go | 4 +-- cmd/fyne/internal/util/file.go | 34 +++++++++---------- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/cmd/fyne/internal/commands/build.go b/cmd/fyne/internal/commands/build.go index 842a5e433d..ad8931fd8e 100644 --- a/cmd/fyne/internal/commands/build.go +++ b/cmd/fyne/internal/commands/build.go @@ -313,7 +313,7 @@ func createMetadataInitFile(srcdir string, app *appData) (func(), error) { data, err := metadata.LoadStandard(srcdir) if err == nil { // When icon path specified in metadata file, we should make it relative to metadata file - data.Details.Icon = util.MakePathRelative(srcdir, data.Details.Icon) + data.Details.Icon = util.MakePathRelativeTo(srcdir, data.Details.Icon) app.mergeMetadata(data) } diff --git a/cmd/fyne/internal/commands/package-util-mock_test.go b/cmd/fyne/internal/commands/package-util-mock_test.go index 86780e4956..68684493a2 100644 --- a/cmd/fyne/internal/commands/package-util-mock_test.go +++ b/cmd/fyne/internal/commands/package-util-mock_test.go @@ -13,7 +13,7 @@ var utilCopyExeFileMock func(src, tgt string) error var utilWriteFileMock func(target string, data []byte) error var utilEnsureSubDirMock func(parent, name string) string var utilEnsureAbsPathMock func(path string) string -var utilMakePathRelativeMock func(root, path string) string +var utilMakePathRelativeToMock func(root, path string) string var utilRequireAndroidSDKMock func() error var utilAndroidBuildToolsPathMock func() string @@ -145,8 +145,8 @@ func (m mockUtil) EnsureAbsPath(path string) string { return utilEnsureAbsPathMock(path) } -func (m mockUtil) MakePathRelative(root, path string) string { - return utilMakePathRelativeMock(root, path) +func (m mockUtil) MakePathRelativeTo(root, path string) string { + return utilMakePathRelativeToMock(root, path) } func (m mockUtil) RequireAndroidSDK() error { diff --git a/cmd/fyne/internal/commands/package-util.go b/cmd/fyne/internal/commands/package-util.go index 4192a1639e..707b7f1813 100644 --- a/cmd/fyne/internal/commands/package-util.go +++ b/cmd/fyne/internal/commands/package-util.go @@ -13,7 +13,7 @@ type packagerUtil interface { WriteFile(target string, data []byte) error EnsureSubDir(parent, name string) string EnsureAbsPath(path string) string - MakePathRelative(root, path string) string + MakePathRelativeTo(root, path string) string RequireAndroidSDK() error AndroidBuildToolsPath() string @@ -49,8 +49,8 @@ func (d defaultUtil) EnsureAbsPath(path string) string { return realUtil.EnsureAbsPath(path) } -func (d defaultUtil) MakePathRelative(root, path string) string { - return realUtil.MakePathRelative(root, path) +func (d defaultUtil) MakePathRelativeTo(root, path string) string { + return realUtil.MakePathRelativeTo(root, path) } func (d defaultUtil) RequireAndroidSDK() error { diff --git a/cmd/fyne/internal/commands/package.go b/cmd/fyne/internal/commands/package.go index 4ec5a68cbc..e09273071a 100644 --- a/cmd/fyne/internal/commands/package.go +++ b/cmd/fyne/internal/commands/package.go @@ -360,8 +360,8 @@ func (p *Packager) validate() (err error) { data, err := metadata.LoadStandard(p.srcDir) if err == nil { // When icon path specified in metadata file, we should make it relative to metadata file - data.Details.Icon = util.MakePathRelative(p.srcDir, data.Details.Icon) - + data.Details.Icon = util.MakePathRelativeTo(p.srcDir, data.Details.Icon) + p.appData.Release = p.release p.appData.mergeMetadata(data) } diff --git a/cmd/fyne/internal/util/file.go b/cmd/fyne/internal/util/file.go index b49c8f7387..33fbd4e62f 100644 --- a/cmd/fyne/internal/util/file.go +++ b/cmd/fyne/internal/util/file.go @@ -41,27 +41,27 @@ func EnsureSubDir(parent, name string) string { // EnsureAbsPath returns the absolute path of the given file if it is not already absolute func EnsureAbsPath(path string) string { - if !filepath.IsAbs(path) { - abs, err := filepath.Abs(path) - if err != nil { - fyne.LogError("Failed to find absolute path", err) - } else { - return abs - } + if filepath.IsAbs(path) { + return path } - return path + abs, err := filepath.Abs(path) + if err != nil { + fyne.LogError("Failed to find absolute path", err) + return path + } + return abs } -// MakePathRelative joins root with path if path is not absolute and exists in root -// both root and path must non-empty, otherwise path will be returned -func MakePathRelative(root, path string) string { - if root != "" && path != "" && !filepath.IsAbs(path) { - joined := filepath.Join(root, path) - if Exists(joined) { - return joined - } +// MakePathRelativeTo joins root with path if path is not absolute and exists in root +func MakePathRelativeTo(root, path string) string { + if filepath.IsAbs(path) { + return path } - return path + joined := filepath.Join(root, path) + if !Exists(joined) { + return path + } + return joined } func copyFileMode(src, tgt string, perm os.FileMode) (err error) { From d63a19034510e4fc17b20250c12ded33606a0951 Mon Sep 17 00:00:00 2001 From: Andy Williams <> Date: Wed, 25 Jan 2023 16:37:46 -0800 Subject: [PATCH 39/52] Upgrade systray lib --- go.mod | 2 +- go.sum | 4 +- vendor/fyne.io/systray/README.md | 45 ++++++++++++++------- vendor/fyne.io/systray/systray.go | 27 +++++++++++-- vendor/fyne.io/systray/systray.h | 1 + vendor/fyne.io/systray/systray_darwin.go | 8 +++- vendor/fyne.io/systray/systray_darwin.m | 35 +++++++++++----- vendor/fyne.io/systray/systray_menu_unix.go | 36 +++++++++++++++++ vendor/fyne.io/systray/systray_unix.go | 2 +- vendor/fyne.io/systray/systray_windows.go | 42 ++++++++++++++++++- vendor/modules.txt | 2 +- 11 files changed, 170 insertions(+), 34 deletions(-) diff --git a/go.mod b/go.mod index 28320a54f8..970e4b75bd 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module fyne.io/fyne/v2 go 1.14 require ( - fyne.io/systray v1.10.1-0.20221115204952-d16a6177e6f1 + fyne.io/systray v1.10.1-0.20230126002350-01b67b43df38 github.com/BurntSushi/toml v1.1.0 github.com/fredbi/uri v0.1.0 github.com/fsnotify/fsnotify v1.5.4 diff --git a/go.sum b/go.sum index 87ef1181ba..0987c66919 100644 --- a/go.sum +++ b/go.sum @@ -37,8 +37,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -fyne.io/systray v1.10.1-0.20221115204952-d16a6177e6f1 h1:OiHw+bZAGEaSreHsA8dDkBOVJmSFzsNTOc/htpM+fOc= -fyne.io/systray v1.10.1-0.20221115204952-d16a6177e6f1/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE= +fyne.io/systray v1.10.1-0.20230126002350-01b67b43df38 h1:GiOT2f0WjANRg0EhEbAZBaGixt26q2nND0m4k4DASvc= +fyne.io/systray v1.10.1-0.20230126002350-01b67b43df38/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= diff --git a/vendor/fyne.io/systray/README.md b/vendor/fyne.io/systray/README.md index e6b22576be..b174f33ccc 100644 --- a/vendor/fyne.io/systray/README.md +++ b/vendor/fyne.io/systray/README.md @@ -37,6 +37,27 @@ func onExit() { } ``` +### Running in a Fyne app + +This repository is designed to allow any toolkit to integrate system tray without any additional dependencies. +It is maintained by the Fyne team, but if you are using Fyne there is an even easier to use API in the main repository that wraps this project. + +In your app you can use a standard `fyne.Menu` structure and pass it to `SetSystemTrayMenu` when your app is a desktop app, as follows: + +```go + menu := fyne.NewMenu("MyApp", + fyne.NewMenuItem("Show", func() { + log.Println("Tapped show") + })) + + if desk, ok := myApp.(desktop.App); ok { + desk.SetSystemTrayMenu(menu) + } +``` + +You can find out more in the toolkit documentation: +[System Tray Menu](https://developer.fyne.io/explore/systray). + ### Run in another toolkit Most graphical toolkits will grab the main loop so the `Run` code above is not possible. @@ -50,26 +71,18 @@ Note: this package requires cgo, so make sure you set `CGO_ENABLED=1` before bui ## Try the example app! -Have go v1.12+ or higher installed? Here's an example to get started on macOS: +Have go v1.12+ or higher installed? Here's an example to get started on macOS or Linux: ```sh git clone https://github.com/fyne-io/systray cd systray/example -env GO111MODULE=on go build -./example +go run . ``` -On Windows, you should build like this: +On Windows, you should follow the instructions above, but use the followign run command: ``` -env GO111MODULE=on go build -ldflags "-H=windowsgui" -``` - -The following text will then appear on the console: - - -```sh -go: finding github.com/fyne-io/systray latest +go run -ldflags "-H=windowsgui" . ``` Now look for *Awesome App* in your menu bar! @@ -82,6 +95,10 @@ Now look for *Awesome App* in your menu bar! This implementation uses DBus to communicate through the SystemNotifier/AppIndicator spec, older tray implementations may not load the icon. +If you are running an older desktop environment, or system tray provider, you may require a proxy app which can convert the new DBus calls to the old format. +The recommended tool for Gnome based trays is [snixembed](https://git.sr.ht/~steef/snixembed), others are available. +Search for "StatusNotifierItems XEmbedded" in your package manager. + ### Windows * To avoid opening a console at application startup, use "fyne package" for your app or manually use these compile flags: @@ -104,10 +121,10 @@ SystrayApp.app/ SystrayApp.icns ``` -When running as an app bundle, you may want to add one or both of the following to your Info.plist: +If bundling manually, you may want to add one or both of the following to your Info.plist: ```xml - + NSHighResolutionCapable True diff --git a/vendor/fyne.io/systray/systray.go b/vendor/fyne.io/systray/systray.go index d0a0dfaed6..3d607a93b4 100644 --- a/vendor/fyne.io/systray/systray.go +++ b/vendor/fyne.io/systray/systray.go @@ -10,15 +10,25 @@ import ( ) var ( - systrayReady func() - systrayExit func() - menuItems = make(map[uint32]*MenuItem) - menuItemsLock sync.RWMutex + systrayReady func() + systrayExit func() + systrayExitCalled bool + menuItems = make(map[uint32]*MenuItem) + menuItemsLock sync.RWMutex currentID = uint32(0) quitOnce sync.Once ) +// This helper function allows us to call systrayExit only once, +// without accidentally calling it twice in the same lifetime. +func runSystrayExit() { + if !systrayExitCalled { + systrayExitCalled = true + systrayExit() + } +} + func init() { runtime.LockOSThread() } @@ -111,6 +121,7 @@ func Register(onReady func(), onExit func()) { onExit = func() {} } systrayExit = onExit + systrayExitCalled = false registerSystray() } @@ -203,6 +214,14 @@ func (item *MenuItem) Hide() { hideMenuItem(item) } +// Remove removes a menu item +func (item *MenuItem) Remove() { + removeMenuItem(item) + menuItemsLock.Lock() + delete(menuItems, item.id) + menuItemsLock.Unlock() +} + // Show shows a previously hidden menu item func (item *MenuItem) Show() { showMenuItem(item) diff --git a/vendor/fyne.io/systray/systray.h b/vendor/fyne.io/systray/systray.h index 1bc03460d1..4858eb4d3e 100644 --- a/vendor/fyne.io/systray/systray.h +++ b/vendor/fyne.io/systray/systray.h @@ -15,6 +15,7 @@ void setTooltip(char* tooltip); void add_or_update_menu_item(int menuId, int parentMenuId, char* title, char* tooltip, short disabled, short checked, short isCheckable); void add_separator(int menuId); void hide_menu_item(int menuId); +void remove_menu_item(int menuId); void show_menu_item(int menuId); void reset_menu(); void quit(); diff --git a/vendor/fyne.io/systray/systray_darwin.go b/vendor/fyne.io/systray/systray_darwin.go index 21fb057247..e917a326c0 100644 --- a/vendor/fyne.io/systray/systray_darwin.go +++ b/vendor/fyne.io/systray/systray_darwin.go @@ -127,6 +127,12 @@ func showMenuItem(item *MenuItem) { ) } +func removeMenuItem(item *MenuItem) { + C.remove_menu_item( + C.int(item.id), + ) +} + func resetMenu() { C.reset_menu() } @@ -138,7 +144,7 @@ func systray_ready() { //export systray_on_exit func systray_on_exit() { - systrayExit() + runSystrayExit() } //export systray_menu_item_selected diff --git a/vendor/fyne.io/systray/systray_darwin.m b/vendor/fyne.io/systray/systray_darwin.m index 268e170005..f244ca5f2f 100644 --- a/vendor/fyne.io/systray/systray_darwin.m +++ b/vendor/fyne.io/systray/systray_darwin.m @@ -203,6 +203,14 @@ - (void) show_menu_item:(NSNumber*) menuId } } +- (void) remove_menu_item:(NSNumber*) menuId +{ + NSMenuItem* menuItem = find_menu_item(menu, menuId); + if (menuItem != NULL) { + [menuItem.menu removeItem:menuItem]; + } +} + - (void) reset_menu { [self->menu removeAllItems]; @@ -266,19 +274,23 @@ void runInMainThread(SEL method, id object) { void setIcon(const char* iconBytes, int length, bool template) { NSData* buffer = [NSData dataWithBytes: iconBytes length:length]; - NSImage *image = [[NSImage alloc] initWithData:buffer]; - [image setSize:NSMakeSize(16, 16)]; - image.template = template; - runInMainThread(@selector(setIcon:), (id)image); + @autoreleasepool { + NSImage *image = [[NSImage alloc] initWithData:buffer]; + [image setSize:NSMakeSize(16, 16)]; + image.template = template; + runInMainThread(@selector(setIcon:), (id)image); + } } void setMenuItemIcon(const char* iconBytes, int length, int menuId, bool template) { NSData* buffer = [NSData dataWithBytes: iconBytes length:length]; - NSImage *image = [[NSImage alloc] initWithData:buffer]; - [image setSize:NSMakeSize(16, 16)]; - image.template = template; - NSNumber *mId = [NSNumber numberWithInt:menuId]; - runInMainThread(@selector(setMenuItemIcon:), @[image, (id)mId]); + @autoreleasepool { + NSImage *image = [[NSImage alloc] initWithData:buffer]; + [image setSize:NSMakeSize(16, 16)]; + image.template = template; + NSNumber *mId = [NSNumber numberWithInt:menuId]; + runInMainThread(@selector(setMenuItemIcon:), @[image, (id)mId]); + } } void setTitle(char* ctitle) { @@ -312,6 +324,11 @@ void hide_menu_item(int menuId) { runInMainThread(@selector(hide_menu_item:), (id)mId); } +void remove_menu_item(int menuId) { + NSNumber *mId = [NSNumber numberWithInt:menuId]; + runInMainThread(@selector(remove_menu_item:), (id)mId); +} + void show_menu_item(int menuId) { NSNumber *mId = [NSNumber numberWithInt:menuId]; runInMainThread(@selector(show_menu_item:), (id)mId); diff --git a/vendor/fyne.io/systray/systray_menu_unix.go b/vendor/fyne.io/systray/systray_menu_unix.go index c0e64854da..1ce9c0673a 100644 --- a/vendor/fyne.io/systray/systray_menu_unix.go +++ b/vendor/fyne.io/systray/systray_menu_unix.go @@ -253,6 +253,42 @@ func findSubLayout(id int32, vals []dbus.Variant) (*menuLayout, bool) { return nil, false } +func removeSubLayout(id int32, vals []dbus.Variant) ([]dbus.Variant, bool) { + for idx, i := range vals { + item := i.Value().(*menuLayout) + if item.V0 == id { + return append(vals[:idx], vals[idx+1:]...), true + } + + if len(item.V2) > 0 { + if child, removed := removeSubLayout(id, item.V2); removed { + return child, true + } + } + } + + return vals, false +} + +func removeMenuItem(item *MenuItem) { + instance.menuLock.Lock() + defer instance.menuLock.Unlock() + + parent := instance.menu + if item.parent != nil { + m, ok := findLayout(int32(item.parent.id)) + if !ok { + return + } + parent = m + } + + if items, removed := removeSubLayout(int32(item.id), parent.V2); removed { + parent.V2 = items + refresh() + } +} + func hideMenuItem(item *MenuItem) { instance.menuLock.Lock() defer instance.menuLock.Unlock() diff --git a/vendor/fyne.io/systray/systray_unix.go b/vendor/fyne.io/systray/systray_unix.go index 053bb5aa93..d0ce2a8270 100644 --- a/vendor/fyne.io/systray/systray_unix.go +++ b/vendor/fyne.io/systray/systray_unix.go @@ -154,7 +154,7 @@ func nativeLoop() int { } func nativeEnd() { - systrayExit() + runSystrayExit() instance.conn.Close() } diff --git a/vendor/fyne.io/systray/systray_windows.go b/vendor/fyne.io/systray/systray_windows.go index f6ce3060ed..f2957c04d6 100644 --- a/vendor/fyne.io/systray/systray_windows.go +++ b/vendor/fyne.io/systray/systray_windows.go @@ -41,6 +41,7 @@ var ( pCreatePopupMenu = u32.NewProc("CreatePopupMenu") pCreateWindowEx = u32.NewProc("CreateWindowExW") pDefWindowProc = u32.NewProc("DefWindowProcW") + pDeleteMenu = u32.NewProc("DeleteMenu") pRemoveMenu = u32.NewProc("RemoveMenu") pDestroyWindow = u32.NewProc("DestroyWindow") pDispatchMessage = u32.NewProc("DispatchMessageW") @@ -323,7 +324,7 @@ func (t *winTray) wndProc(hWnd windows.Handle, message uint32, wParam, lParam ui t.nid.delete() } t.muNID.Unlock() - systrayExit() + runSystrayExit() case t.wmSystrayMessage: switch lParam { case WM_RBUTTONUP, WM_LBUTTONUP: @@ -673,6 +674,30 @@ func (t *winTray) addSeparatorMenuItem(menuItemId, parentId uint32) error { return nil } +func (t *winTray) removeMenuItem(menuItemId, parentId uint32) error { + if !wt.isReady() { + return ErrTrayNotReadyYet + } + + const MF_BYCOMMAND = 0x00000000 + const ERROR_SUCCESS syscall.Errno = 0 + + t.muMenus.RLock() + menu := uintptr(t.menus[parentId]) + t.muMenus.RUnlock() + res, _, err := pDeleteMenu.Call( + menu, + uintptr(menuItemId), + MF_BYCOMMAND, + ) + if res == 0 && err.(syscall.Errno) != ERROR_SUCCESS { + return err + } + t.delFromVisibleItems(parentId, menuItemId) + + return nil +} + func (t *winTray) hideMenuItem(menuItemId, parentId uint32) error { if !wt.isReady() { return ErrTrayNotReadyYet @@ -926,6 +951,13 @@ func quit() { 0, 0, ) + + wt.muNID.Lock() + if wt.nid != nil { + wt.nid.delete() + } + wt.muNID.Unlock() + runSystrayExit() } func setInternalLoop(bool) { @@ -1051,6 +1083,14 @@ func hideMenuItem(item *MenuItem) { } } +func removeMenuItem(item *MenuItem) { + err := wt.removeMenuItem(uint32(item.id), item.parentId()) + if err != nil { + log.Printf("systray error: unable to removeMenuItem: %s\n", err) + return + } +} + func showMenuItem(item *MenuItem) { addOrUpdateMenuItem(item) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 268ea8c8bf..4376802272 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ -# fyne.io/systray v1.10.1-0.20221115204952-d16a6177e6f1 +# fyne.io/systray v1.10.1-0.20230126002350-01b67b43df38 ## explicit fyne.io/systray fyne.io/systray/internal/generated/menu From 72c534e90187eda8a544acc2935f787dc07a6cf5 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Thu, 19 Jan 2023 16:16:55 -0800 Subject: [PATCH 40/52] Reset hovered flag when a list item is set up Fixes #3584 --- widget/list.go | 3 ++- widget/list_test.go | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/list.go b/widget/list.go index 3079ce4823..682fa1d3e0 100644 --- a/widget/list.go +++ b/widget/list.go @@ -542,7 +542,8 @@ func (l *listLayout) setupListItem(li *listItem, id ListItemID) { break } } - if previousIndicator != li.selected { + if previousIndicator != li.selected || li.hovered { + li.hovered = false li.Refresh() } if f := l.list.UpdateItem; f != nil { diff --git a/widget/list_test.go b/widget/list_test.go index 3dc7d4fafd..6af698f108 100644 --- a/widget/list_test.go +++ b/widget/list_test.go @@ -539,7 +539,6 @@ func TestList_Focus(t *testing.T) { canvas.Focused().TypedKey(&fyne.KeyEvent{Name: fyne.KeySpace}) assert.True(t, canvas.Focused().(*listItem).selected) - assert.True(t, canvas.Focused().(*listItem).hovered) } func createList(items int) *List { From 160772ae3768dd4a72aed89d15958f3fd700265b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0smail=20Bulut?= <73897211+hismailbulut@users.noreply.github.com> Date: Wed, 25 Jan 2023 17:22:41 +0300 Subject: [PATCH 41/52] We should only correct icon path if it is specified in metadata file --- cmd/fyne/internal/commands/build.go | 4 +++- cmd/fyne/internal/commands/package.go | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd/fyne/internal/commands/build.go b/cmd/fyne/internal/commands/build.go index ad8931fd8e..ce21708748 100644 --- a/cmd/fyne/internal/commands/build.go +++ b/cmd/fyne/internal/commands/build.go @@ -313,7 +313,9 @@ func createMetadataInitFile(srcdir string, app *appData) (func(), error) { data, err := metadata.LoadStandard(srcdir) if err == nil { // When icon path specified in metadata file, we should make it relative to metadata file - data.Details.Icon = util.MakePathRelativeTo(srcdir, data.Details.Icon) + if data.Details.Icon != "" { + data.Details.Icon = util.MakePathRelativeTo(srcdir, data.Details.Icon) + } app.mergeMetadata(data) } diff --git a/cmd/fyne/internal/commands/package.go b/cmd/fyne/internal/commands/package.go index e09273071a..76fee58742 100644 --- a/cmd/fyne/internal/commands/package.go +++ b/cmd/fyne/internal/commands/package.go @@ -360,7 +360,9 @@ func (p *Packager) validate() (err error) { data, err := metadata.LoadStandard(p.srcDir) if err == nil { // When icon path specified in metadata file, we should make it relative to metadata file - data.Details.Icon = util.MakePathRelativeTo(p.srcDir, data.Details.Icon) + if data.Details.Icon != "" { + data.Details.Icon = util.MakePathRelativeTo(p.srcDir, data.Details.Icon) + } p.appData.Release = p.release p.appData.mergeMetadata(data) From 5cbf5c617f04a041085e97353afebefba73c1e8a Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 1 Feb 2023 21:25:35 +0000 Subject: [PATCH 42/52] Fix issue where Windows iconifying could crash with stroked rectangle Fixes #3552 --- internal/driver/glfw/canvas.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/driver/glfw/canvas.go b/internal/driver/glfw/canvas.go index abd911ab2b..41019ee330 100644 --- a/internal/driver/glfw/canvas.go +++ b/internal/driver/glfw/canvas.go @@ -281,6 +281,9 @@ func (c *glCanvas) paint(size fyne.Size) { inner := clips.Push(pos, obj.Size()) c.Painter().StartClipping(inner.Rect()) } + if size.Width <= 0 || size.Height <= 0 { // iconifying on Windows can do bad things + return + } c.Painter().Paint(obj, pos, size) } afterPaint := func(node *common.RenderCacheNode) { From 5822d557ec7f72dff2d18093645997718c7b633f Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 7 Feb 2023 09:01:18 +0000 Subject: [PATCH 43/52] Update systray Fixes #3120 --- go.mod | 2 +- go.sum | 4 ++-- vendor/fyne.io/systray/systray_unix.go | 5 +++++ vendor/modules.txt | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 970e4b75bd..6be9f9fd4b 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module fyne.io/fyne/v2 go 1.14 require ( - fyne.io/systray v1.10.1-0.20230126002350-01b67b43df38 + fyne.io/systray v1.10.1-0.20230207085535-4a244dbb9d03 github.com/BurntSushi/toml v1.1.0 github.com/fredbi/uri v0.1.0 github.com/fsnotify/fsnotify v1.5.4 diff --git a/go.sum b/go.sum index 0987c66919..2b19673f01 100644 --- a/go.sum +++ b/go.sum @@ -37,8 +37,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -fyne.io/systray v1.10.1-0.20230126002350-01b67b43df38 h1:GiOT2f0WjANRg0EhEbAZBaGixt26q2nND0m4k4DASvc= -fyne.io/systray v1.10.1-0.20230126002350-01b67b43df38/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE= +fyne.io/systray v1.10.1-0.20230207085535-4a244dbb9d03 h1:zb84y6X6lIi4Aeo8ehu6Qtu2fipBZ6Wzp41Ki/F4qdg= +fyne.io/systray v1.10.1-0.20230207085535-4a244dbb9d03/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= diff --git a/vendor/fyne.io/systray/systray_unix.go b/vendor/fyne.io/systray/systray_unix.go index d0ce2a8270..cd6ecb9bc2 100644 --- a/vendor/fyne.io/systray/systray_unix.go +++ b/vendor/fyne.io/systray/systray_unix.go @@ -165,6 +165,10 @@ func quit() { func nativeStart() { systrayReady() conn, _ := dbus.ConnectSessionBus() + if conn == nil { + log.Printf("systray error: failed to connect to DBus") + return + } err := notifier.ExportStatusNotifierItem(conn, path, ¬ifier.UnimplementedStatusNotifierItem{}) if err != nil { log.Printf("systray error: failed to export status notifier item: %s\n", err) @@ -172,6 +176,7 @@ func nativeStart() { err = menu.ExportDbusmenu(conn, menuPath, instance) if err != nil { log.Printf("systray error: failed to export status notifier item: %s\n", err) + return } name := fmt.Sprintf("org.kde.StatusNotifierItem-%d-1", os.Getpid()) // register id 1 for this process diff --git a/vendor/modules.txt b/vendor/modules.txt index 4376802272..da57c22a99 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ -# fyne.io/systray v1.10.1-0.20230126002350-01b67b43df38 +# fyne.io/systray v1.10.1-0.20230207085535-4a244dbb9d03 ## explicit fyne.io/systray fyne.io/systray/internal/generated/menu From 1c22e47b195b1b59f48f7731304e01cdeceb8db5 Mon Sep 17 00:00:00 2001 From: Edwin Clement Date: Sat, 4 Feb 2023 19:28:38 +0530 Subject: [PATCH 44/52] Restricted exception for tab handling in shortcuts --- internal/driver/glfw/window.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/driver/glfw/window.go b/internal/driver/glfw/window.go index 37e7fd78c2..e052cb7d59 100644 --- a/internal/driver/glfw/window.go +++ b/internal/driver/glfw/window.go @@ -760,7 +760,11 @@ func (w *window) processKeyPressed(keyName fyne.KeyName, keyASCII fyne.KeyName, // key repeat will fall through to TypedKey and TypedShortcut } - if (keyName == fyne.KeyTab && !w.capturesTab(keyDesktopModifier)) || w.triggersShortcut(keyName, keyASCII, keyDesktopModifier) { + modifierOtherThanShift := (keyDesktopModifier & fyne.KeyModifierControl) | + (keyDesktopModifier & fyne.KeyModifierAlt) | + (keyDesktopModifier & fyne.KeyModifierSuper) + if (keyName == fyne.KeyTab && modifierOtherThanShift == 0 && !w.capturesTab(keyDesktopModifier)) || + w.triggersShortcut(keyName, keyASCII, keyDesktopModifier) { return } From 25fe84576cbadbb03c59545e1f482978abd370f1 Mon Sep 17 00:00:00 2001 From: Edwin Clement Date: Wed, 8 Feb 2023 13:12:20 +0530 Subject: [PATCH 45/52] Added Test Cases --- internal/driver/glfw/window_test.go | 68 +++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/internal/driver/glfw/window_test.go b/internal/driver/glfw/window_test.go index f3c89368db..479f9e6234 100644 --- a/internal/driver/glfw/window_test.go +++ b/internal/driver/glfw/window_test.go @@ -1506,6 +1506,64 @@ func TestWindow_CaptureTypedShortcut(t *testing.T) { assert.Equal(t, "CustomDesktop:Control+F", content.capturedShortcuts[0].ShortcutName()) } +func TestWindow_OnlyTabAndShiftTabToCapturesTab(t *testing.T) { + w := createWindow("Test").(*window) + content := &tabbable{} + content.SetMinSize(fyne.NewSize(10, 10)) + w.SetContent(content) + repaintWindow(w) + + w.Canvas().Focus(content) + + // Tab and Shift-Tab are passed to capturesTab + w.keyPressed(nil, glfw.KeyTab, 0, glfw.Press, glfw.ModShift) + w.keyPressed(nil, glfw.KeyTab, 0, glfw.Release, glfw.ModShift) + w.WaitForEvents() + assert.Equal(t, 1, content.acceptTabCallCount) + w.keyPressed(nil, glfw.KeyTab, 0, glfw.Press, 0) + w.keyPressed(nil, glfw.KeyTab, 0, glfw.Release, 0) + w.WaitForEvents() + assert.Equal(t, 2, content.acceptTabCallCount) + + // Tab with ctrl or alt is not passed + w.keyPressed(nil, glfw.KeyTab, 0, glfw.Press, glfw.ModControl) + w.keyPressed(nil, glfw.KeyTab, 0, glfw.Release, glfw.ModControl) + w.keyPressed(nil, glfw.KeyTab, 0, glfw.Press, glfw.ModControl|glfw.ModShift) + w.keyPressed(nil, glfw.KeyTab, 0, glfw.Release, glfw.ModControl|glfw.ModShift) + w.WaitForEvents() + assert.Equal(t, 2, content.acceptTabCallCount) +} + +func TestWindow_TabWithModifierToTriggersShortcut(t *testing.T) { + w := createWindow("Test").(*window) + content := &typedShortcutable{} + content.SetMinSize(fyne.NewSize(10, 10)) + w.SetContent(content) + repaintWindow(w) + + w.Canvas().Focus(content) + + // Tab and Shift-Tab are passed to capturesTab + w.keyPressed(nil, glfw.KeyTab, 0, glfw.Press, glfw.ModShift) + w.keyPressed(nil, glfw.KeyTab, 0, glfw.Release, glfw.ModShift) + + w.keyPressed(nil, glfw.KeyTab, 0, glfw.Press, 0) + w.keyPressed(nil, glfw.KeyTab, 0, glfw.Release, 0) + w.WaitForEvents() + + assert.Equal(t, 0, len(content.capturedShortcuts)) + + // Tab with ctrl or alt is not passed + w.keyPressed(nil, glfw.KeyTab, 0, glfw.Press, glfw.ModControl) + w.keyPressed(nil, glfw.KeyTab, 0, glfw.Release, glfw.ModControl) + w.keyPressed(nil, glfw.KeyTab, 0, glfw.Press, glfw.ModControl|glfw.ModShift) + w.keyPressed(nil, glfw.KeyTab, 0, glfw.Release, glfw.ModControl|glfw.ModShift) + w.WaitForEvents() + assert.Equal(t, 2, len(content.capturedShortcuts)) + assert.Equal(t, "CustomDesktop:Control+Tab", content.capturedShortcuts[0].ShortcutName()) + assert.Equal(t, "CustomDesktop:Shift+Control+Tab", content.capturedShortcuts[1].ShortcutName()) +} + func TestWindow_ManualFocus(t *testing.T) { w := createWindow("Test").(*window) content := &focusable{} @@ -1949,3 +2007,13 @@ func newDoubleTappableButton() *doubleTappableButton { func waitForMain() { runOnMain(func() {}) // this blocks until processed } + +type tabbable struct { + focusable + acceptTabCallCount int +} + +func (t *tabbable) AcceptsTab() bool { + t.acceptTabCallCount += 1 + return true +} From c113bbdb5bbaea3dbe6eb3d369fb3c96e382a5b7 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 8 Feb 2023 12:32:53 +0000 Subject: [PATCH 46/52] Fix typo/miscalculation in padding refactor --- widget/richtext_objects.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/widget/richtext_objects.go b/widget/richtext_objects.go index da32202330..7ed107d849 100644 --- a/widget/richtext_objects.go +++ b/widget/richtext_objects.go @@ -486,13 +486,13 @@ type unpadTextWidgetLayout struct { func (u *unpadTextWidgetLayout) Layout(o []fyne.CanvasObject, s fyne.Size) { pad := theme.InnerPadding() * -1 - pad2 := pad * -1 + pad2 := pad * -2 o[0].Move(fyne.NewPos(pad, pad)) o[0].Resize(s.Add(fyne.NewSize(pad2, pad2))) } func (u *unpadTextWidgetLayout) MinSize(o []fyne.CanvasObject) fyne.Size { - pad4 := theme.InnerPadding() * 2 - return o[0].MinSize().Subtract(fyne.NewSize(pad4, pad4)) + pad := theme.InnerPadding() * 2 + return o[0].MinSize().Subtract(fyne.NewSize(pad, pad)) } From 578b4e35923ef0a0d52aef89b492c5624858907d Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 8 Feb 2023 16:01:10 +0000 Subject: [PATCH 47/52] Prepping for v2.3.1 --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f099462763..c4d6821081 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,31 @@ 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.3.1 - 12 February 2023 + +### Fixed + +* Custom shortcuts with fyne.KeyTab is not working (#3087) +* Running a program with root privileges resulted in panic (#3120) +* Markdown image with no title is not parsed (#3577) +* Systray app on macOS panic when started while machine sleeps (#3609) +* Runtime error with VNC on RaspbianOS (#2972) +* Hovered background in List widget isn't reset when scrolling reuses an existing list item (#3584) +* cmd/fyne package can't find FyneApp.toml when -src option has given (#3459) +* TextWrapWord will cause crash in RichText unverified (#3498) +* crash in widget.(*RichText).lineSizeToColumn (#3292) +* Crash in widget.(*Entry).SelectedText (#3290) +* Crash in widget.(*RichText).updateRowBounds.func1 (#3291) +* window is max size at all times (#3507) +* systray.Quit() is not called consistently when the app is closing (#3597) +* Software rendering would ignore scale for text +* crash when minimize a window which contains a stroked rectangle (#3552) +* Menu item would not appear disabled initially +* Wrong icon colour for danger and warning buttons +* Embedding Fyne apps in iFrame alignment issue +* Generated metadata can be in wrong directory + + ## 2.3.0 - 24 December 2022 ### Added From df835df1cf6bbe264297b2c0ca57250a1addcea5 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sun, 5 Feb 2023 22:11:47 +0000 Subject: [PATCH 48/52] Make the root storage space for any user, not just prefs Fixes #3207 --- app/app.go | 5 +++++ app/storage_test.go | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 app/storage_test.go diff --git a/app/app.go b/app/app.go index dbb647cb72..6031334a8f 100644 --- a/app/app.go +++ b/app/app.go @@ -4,6 +4,7 @@ package app // import "fyne.io/fyne/v2/app" import ( + "os" "strconv" "sync/atomic" "time" @@ -130,6 +131,10 @@ func makeStoreDocs(id string, p fyne.Preferences, s *store) *internal.Docs { if pref, ok := p.(interface{ load() }); ok { pref.load() } + err := os.MkdirAll(s.a.storageRoot(), 0755) // make the space before anyone can use it + if err != nil { + fyne.LogError("Failed to create app storage space", err) + } root, _ := s.docRootURI() return &internal.Docs{RootDocURI: root} diff --git a/app/storage_test.go b/app/storage_test.go new file mode 100644 index 0000000000..5f1b999bcc --- /dev/null +++ b/app/storage_test.go @@ -0,0 +1,22 @@ +package app + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "fyne.io/fyne/v2/internal" +) + +func TestStore_RootURI(t *testing.T) { + id := "io.fyne.test" + a := &fyneApp{uniqueID: id} + d := makeStoreDocs(id, &internal.InMemoryPreferences{}, &store{a: a}) + + w, err := d.Create("test") + assert.Nil(t, err) + err = w.Close() + assert.Nil(t, err) + err = d.Remove("test") + assert.Nil(t, err) +} From 6f4a472e8a7fcdff995048abe569596d87f6efdd Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 8 Feb 2023 16:49:30 +0000 Subject: [PATCH 49/52] CHANGELOG ready for rc1 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4d6821081..260a6d7a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ More detailed release notes can be found on the [releases page](https://github.c * Wrong icon colour for danger and warning buttons * Embedding Fyne apps in iFrame alignment issue * Generated metadata can be in wrong directory +* Android RootURI may not exist when used for storage (#3207) ## 2.3.0 - 24 December 2022 From aa5f0d249b5cb39458d8e4a3c1ce06646c6af028 Mon Sep 17 00:00:00 2001 From: Cedric Bail Date: Tue, 7 Feb 2023 16:27:21 -0500 Subject: [PATCH 50/52] Correct all version used while building application to ensure Windows compatibility. --- cmd/fyne/internal/commands/package.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd/fyne/internal/commands/package.go b/cmd/fyne/internal/commands/package.go index 76fee58742..a21ee41381 100644 --- a/cmd/fyne/internal/commands/package.go +++ b/cmd/fyne/internal/commands/package.go @@ -259,7 +259,13 @@ func (p *Packager) buildPackage(runner runner) ([]string, error) { } func (p *Packager) combinedVersion() string { - return fmt.Sprintf("%s.%d", p.AppVersion, p.AppBuild) + versions := strings.Split(p.AppVersion, ".") + for len(versions) < 3 { + versions = append(versions, "0") + } + appVersion := strings.Join(versions, ".") + + return fmt.Sprintf("%s.%d", appVersion, p.AppBuild) } func (p *Packager) doPackage(runner runner) error { From c22269c6a78427117b553b9f11079476b14980e5 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Tue, 7 Feb 2023 14:40:27 -0700 Subject: [PATCH 51/52] Add tests covering combinedVersion. --- cmd/fyne/internal/commands/package_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cmd/fyne/internal/commands/package_test.go b/cmd/fyne/internal/commands/package_test.go index 3e52cce7ac..55dfd67d38 100644 --- a/cmd/fyne/internal/commands/package_test.go +++ b/cmd/fyne/internal/commands/package_test.go @@ -51,6 +51,24 @@ func Test_isValidVersion(t *testing.T) { assert.False(t, isValidVersion("1..2")) } +func Test_combinedVersion(t *testing.T) { + tests := []struct { + ver string + build int + comb string + }{ + {"1.2.3", 4, "1.2.3.4"}, + {"1.2", 4, "1.2.0.4"}, + {"1", 4, "1.0.0.4"}, + } + + for _, tt := range tests { + p := &Packager{appData: &appData{AppVersion: tt.ver, AppBuild: tt.build}} + comb := p.combinedVersion() + assert.Equal(t, tt.comb, comb) + } +} + func Test_MergeMetata(t *testing.T) { p := &Packager{appData: &appData{}} p.AppVersion = "v0.1" From 30e7cb85036e7ae00f5104532475a812be9d48a2 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 10 Feb 2023 10:47:48 +0000 Subject: [PATCH 52/52] Prep rc2 --- CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 260a6d7a31..0b71e4136a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,16 @@ 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.3.1 - 12 February 2023 +## 2.3.1 - 13 February 2023 + +### Changed + +* Pad app version to ensure Windows packages correctly (#3638) ### Fixed * Custom shortcuts with fyne.KeyTab is not working (#3087) -* Running a program with root privileges resulted in panic (#3120) +* Running a systray app with root privileges resulted in panic (#3120) * Markdown image with no title is not parsed (#3577) * Systray app on macOS panic when started while machine sleeps (#3609) * Runtime error with VNC on RaspbianOS (#2972)