diff --git a/CHANGELOG.md b/CHANGELOG.md index bcbe770323..d22207b4e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,28 @@ 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.5 - 6 June 2023 + +### Fixed + +* Panic with unsupported font (#3646) +* Temporary manifest file not closed after building on Windows +* Panic when using autogenerated quit menu and having unshown windows (#3870) +* Using `canvas.ImageScaleFastest` not working on arm64 (#3891) +* Disabled password Entry should also disable the ActionItem (#3908) +* Disabled RadioGroup does not display status (#3882) +* Negative TableCellID Row (#2857) +* Make sure we have sufficient space for the bar as well if content is tiny (#3898) +* Leak in image painter when replacing image.Image source regularly +* Links in Markdown/Rich Text lists breaks formatting (#2911) +* Crash when reducing window to taskbar with popup opened (#3877) +* RichText vertical scroll will truncate long content with horizontal lines (#3929) +* Custom metadata would not apply with `fyne release` command +* Horizontal CheckGroup overlap when having long text (#3005) +* Fix focused colour of coloured buttons (#3462) +* Menu separator not visible with light theme (#3814) + + ## 2.3.4 - 3 May 2023 ### Fixed diff --git a/cmd/fyne/internal/commands/package-windows.go b/cmd/fyne/internal/commands/package-windows.go index 84a5651bad..be48db82ee 100644 --- a/cmd/fyne/internal/commands/package-windows.go +++ b/cmd/fyne/internal/commands/package-windows.go @@ -66,6 +66,7 @@ func (p *Packager) packageWindows() error { if err != nil { return fmt.Errorf("failed to write manifest template: %w", err) } + manifestFile.Close() } // launch rsrc to generate the object file diff --git a/cmd/fyne/internal/commands/release.go b/cmd/fyne/internal/commands/release.go index 1544d6038d..1723ffddbc 100644 --- a/cmd/fyne/internal/commands/release.go +++ b/cmd/fyne/internal/commands/release.go @@ -121,6 +121,11 @@ func Release() *cli.Command { Value: "", Destination: &r.icon, }, + &cli.GenericFlag{ + Name: "metadata", + Usage: "Specify custom metadata key value pair that you do not want to store in your FyneApp.toml (key=value)", + Value: &r.customMetadata, + }, }, Action: r.releaseAction, } diff --git a/container/tabs.go b/container/tabs.go index 49377edcac..0ec2610dab 100644 --- a/container/tabs.go +++ b/container/tabs.go @@ -365,7 +365,23 @@ func (r *baseTabsRenderer) layout(t baseTabs, size fyne.Size) { } func (r *baseTabsRenderer) minSize(t baseTabs) fyne.Size { + pad := theme.Padding() + buttonPad := pad barMin := r.bar.MinSize() + tabsMin := r.bar.Objects[0].MinSize() + accessory := r.bar.Objects[1] + accessoryMin := accessory.MinSize() + if scroll, ok := r.bar.Objects[0].(*Scroll); ok && len(scroll.Content.(*fyne.Container).Objects) == 0 { + tabsMin = fyne.Size{} // scroller forces 32 where we don't need any space + buttonPad = 0 + } else if group, ok := r.bar.Objects[0].(*fyne.Container); ok && len(group.Objects) > 0 { + tabsMin = group.Objects[0].MinSize() + buttonPad = 0 + } + if !accessory.Visible() || accessoryMin.Width == 0 { + buttonPad = 0 + accessoryMin = fyne.Size{} + } contentMin := fyne.NewSize(0, 0) for _, content := range t.items() { @@ -374,9 +390,11 @@ func (r *baseTabsRenderer) minSize(t baseTabs) fyne.Size { switch t.tabLocation() { case TabLocationLeading, TabLocationTrailing: - return fyne.NewSize(barMin.Width+contentMin.Width+theme.Padding(), contentMin.Height) + return fyne.NewSize(barMin.Width+contentMin.Width+pad, + fyne.Max(contentMin.Height, accessoryMin.Height+buttonPad+tabsMin.Height)) default: - return fyne.NewSize(contentMin.Width, barMin.Height+contentMin.Height+theme.Padding()) + return fyne.NewSize(fyne.Max(contentMin.Width, accessoryMin.Width+buttonPad+tabsMin.Width), + barMin.Height+contentMin.Height+pad) } } diff --git a/container/testdata/apptabs/desktop/tab_location_bottom.xml b/container/testdata/apptabs/desktop/tab_location_bottom.xml index 04fd3a3cef..0489a64917 100644 --- a/container/testdata/apptabs/desktop/tab_location_bottom.xml +++ b/container/testdata/apptabs/desktop/tab_location_bottom.xml @@ -1,21 +1,21 @@ - + - - - + + + Test1 - + - + - + Text 1 diff --git a/container/testdata/apptabs/desktop/tab_location_leading.xml b/container/testdata/apptabs/desktop/tab_location_leading.xml index 1a2de7fb8a..e8a2736535 100644 --- a/container/testdata/apptabs/desktop/tab_location_leading.xml +++ b/container/testdata/apptabs/desktop/tab_location_leading.xml @@ -1,21 +1,21 @@ - + - - - + + + Test1 - + - + - + Text 1 diff --git a/container/testdata/apptabs/desktop/tab_location_top.xml b/container/testdata/apptabs/desktop/tab_location_top.xml index ec5ca20290..c116129dd4 100644 --- a/container/testdata/apptabs/desktop/tab_location_top.xml +++ b/container/testdata/apptabs/desktop/tab_location_top.xml @@ -1,21 +1,21 @@ - + - - - + + + Test1 - + - + - + Text 1 diff --git a/container/testdata/apptabs/desktop/tab_location_trailing.xml b/container/testdata/apptabs/desktop/tab_location_trailing.xml index 273f7238d9..6dd3bd61fd 100644 --- a/container/testdata/apptabs/desktop/tab_location_trailing.xml +++ b/container/testdata/apptabs/desktop/tab_location_trailing.xml @@ -1,21 +1,21 @@ - + - - - + + + Test1 - + - + - + Text 1 diff --git a/container/testdata/apptabs/mobile/tab_location_bottom.xml b/container/testdata/apptabs/mobile/tab_location_bottom.xml index 887daffd9b..6108a8d05c 100644 --- a/container/testdata/apptabs/mobile/tab_location_bottom.xml +++ b/container/testdata/apptabs/mobile/tab_location_bottom.xml @@ -1,21 +1,21 @@ - + - - - - - Test1 + + + + + Test1 - + - - - + + + Text 1 diff --git a/container/testdata/apptabs/mobile/tab_location_top.xml b/container/testdata/apptabs/mobile/tab_location_top.xml index 899a28e368..98e9ce422c 100644 --- a/container/testdata/apptabs/mobile/tab_location_top.xml +++ b/container/testdata/apptabs/mobile/tab_location_top.xml @@ -1,21 +1,21 @@ - + - - - - - Test1 + + + + + Test1 - + - - - + + + Text 1 diff --git a/container/testdata/doctabs/desktop/tab_location_bottom.xml b/container/testdata/doctabs/desktop/tab_location_bottom.xml index 0e84dc90de..06035bd8d1 100644 --- a/container/testdata/doctabs/desktop/tab_location_bottom.xml +++ b/container/testdata/doctabs/desktop/tab_location_bottom.xml @@ -1,8 +1,8 @@ - + - - - + + + Test1 @@ -14,16 +14,16 @@ Test3 - + - + - + @@ -31,9 +31,9 @@ - - - + + + Text 1 diff --git a/container/testdata/doctabs/desktop/tab_location_leading.xml b/container/testdata/doctabs/desktop/tab_location_leading.xml index 04d966d20b..785d7f0ad6 100644 --- a/container/testdata/doctabs/desktop/tab_location_leading.xml +++ b/container/testdata/doctabs/desktop/tab_location_leading.xml @@ -1,8 +1,8 @@ - + - - - + + + Test1 @@ -14,16 +14,16 @@ Test3 - + - + - + @@ -31,9 +31,9 @@ - - - + + + Text 1 diff --git a/container/testdata/doctabs/desktop/tab_location_top.xml b/container/testdata/doctabs/desktop/tab_location_top.xml index 0d4ef77450..ca0cb17a95 100644 --- a/container/testdata/doctabs/desktop/tab_location_top.xml +++ b/container/testdata/doctabs/desktop/tab_location_top.xml @@ -1,8 +1,8 @@ - + - - - + + + Test1 @@ -14,16 +14,16 @@ Test3 - + - + - + @@ -31,9 +31,9 @@ - - - + + + Text 1 diff --git a/container/testdata/doctabs/desktop/tab_location_trailing.xml b/container/testdata/doctabs/desktop/tab_location_trailing.xml index 6dffc9a823..4a60f55f4e 100644 --- a/container/testdata/doctabs/desktop/tab_location_trailing.xml +++ b/container/testdata/doctabs/desktop/tab_location_trailing.xml @@ -1,8 +1,8 @@ - + - - - + + + Test1 @@ -14,16 +14,16 @@ Test3 - + - + - + @@ -31,9 +31,9 @@ - - - + + + Text 1 diff --git a/container/testdata/doctabs/mobile/tab_location_bottom.xml b/container/testdata/doctabs/mobile/tab_location_bottom.xml index a6edccacfa..7d62591676 100644 --- a/container/testdata/doctabs/mobile/tab_location_bottom.xml +++ b/container/testdata/doctabs/mobile/tab_location_bottom.xml @@ -1,8 +1,8 @@ - + - - - + + + Test1 @@ -23,16 +23,16 @@ - + - + - + @@ -40,9 +40,9 @@ - - - + + + Text 1 diff --git a/container/testdata/doctabs/mobile/tab_location_top.xml b/container/testdata/doctabs/mobile/tab_location_top.xml index 9fb6b80179..51608743ed 100644 --- a/container/testdata/doctabs/mobile/tab_location_top.xml +++ b/container/testdata/doctabs/mobile/tab_location_top.xml @@ -1,8 +1,8 @@ - + - - - + + + Test1 @@ -23,16 +23,16 @@ - + - + - + @@ -40,9 +40,9 @@ - - - + + + Text 1 diff --git a/go.mod b/go.mod index c3ddeb2c3f..a08249c6bd 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.20230403195833-7dc3c09283d6 + fyne.io/systray v1.10.1-0.20230602210930-b6a2d6ca2a7b 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 98a6c0a22d..13f533a899 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.20230403195833-7dc3c09283d6 h1:lHt8dm97Uy9ggtnt9N6XOlsp76wXmRAh3SjReWm1e2Q= -fyne.io/systray v1.10.1-0.20230403195833-7dc3c09283d6/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE= +fyne.io/systray v1.10.1-0.20230602210930-b6a2d6ca2a7b h1:MP1cUnIdF1cxrMhK9iw9H0JP3zopyD1zi84BqU6WTsE= +fyne.io/systray v1.10.1-0.20230602210930-b6a2d6ca2a7b/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/internal/driver/glfw/driver_desktop.go b/internal/driver/glfw/driver_desktop.go index 21923618f8..862dcc0709 100644 --- a/internal/driver/glfw/driver_desktop.go +++ b/internal/driver/glfw/driver_desktop.go @@ -121,12 +121,7 @@ func (d *gLDriver) refreshSystray(m *fyne.Menu) { systray.ResetMenu() d.refreshSystrayMenu(m, nil) - systray.AddSeparator() - quit := systray.AddMenuItem("Quit", "Quit application") - go func() { - <-quit.ClickedCh - d.Quit() - }() + addMissingQuitForMenu(m, d) } func (d *gLDriver) refreshSystrayMenu(m *fyne.Menu, parent *systray.MenuItem) { @@ -165,3 +160,25 @@ func (d *gLDriver) SetSystemTrayIcon(resource fyne.Resource) { func (d *gLDriver) SystemTrayMenu() *fyne.Menu { return d.systrayMenu } + +func addMissingQuitForMenu(menu *fyne.Menu, d *gLDriver) { + var lastItem *fyne.MenuItem + if len(menu.Items) > 0 { + lastItem = menu.Items[len(menu.Items)-1] + if lastItem.Label == "Quit" { + lastItem.IsQuit = true + } + } + if lastItem == nil || !lastItem.IsQuit { // make sure the menu always has a quit option + quitItem := fyne.NewMenuItem("Quit", nil) + quitItem.IsQuit = true + menu.Items = append(menu.Items, fyne.NewMenuItemSeparator(), quitItem) + } + for _, item := range menu.Items { + if item.IsQuit && item.Action == nil { + item.Action = func() { + d.Quit() + } + } + } +} diff --git a/internal/driver/glfw/menu.go b/internal/driver/glfw/menu.go index dd0a93a789..f469451750 100644 --- a/internal/driver/glfw/menu.go +++ b/internal/driver/glfw/menu.go @@ -10,11 +10,11 @@ func buildMenuOverlay(menus *fyne.MainMenu, w *window) fyne.CanvasObject { return nil } - menus = addMissingQuit(menus, w) + menus = addMissingQuitForMainMenu(menus, w) return NewMenuBar(menus, w.canvas) } -func addMissingQuit(menus *fyne.MainMenu, w *window) *fyne.MainMenu { +func addMissingQuitForMainMenu(menus *fyne.MainMenu, w *window) *fyne.MainMenu { var lastItem *fyne.MenuItem if len(menus.Items[0].Items) > 0 { lastItem = menus.Items[0].Items[len(menus.Items[0].Items)-1] diff --git a/internal/driver/glfw/window_desktop.go b/internal/driver/glfw/window_desktop.go index 4be8d1ab27..1d21f8be22 100644 --- a/internal/driver/glfw/window_desktop.go +++ b/internal/driver/glfw/window_desktop.go @@ -317,7 +317,9 @@ func (w *window) refresh(_ *glfw.Window) { } func (w *window) closed(viewport *glfw.Window) { - viewport.SetShouldClose(false) // reset the closed flag until we check the veto in processClosed + if viewport != nil { + viewport.SetShouldClose(false) // reset the closed flag until we check the veto in processClosed + } w.processClosed() } diff --git a/internal/driver/glfw/window_test.go b/internal/driver/glfw/window_test.go index 479f9e6234..dcadaa0835 100644 --- a/internal/driver/glfw/window_test.go +++ b/internal/driver/glfw/window_test.go @@ -1700,6 +1700,12 @@ func TestWindow_CloseInterception(t *testing.T) { assert.True(t, onClosed) // Close is called if the interceptor is not set. } +func TestWindow_ClosedBeforeShow(t *testing.T) { + w := createWindow("Test").(*window) + // viewport will be nil if window is closed before show + assert.NotPanics(t, func() { w.closed(nil) }) +} + func TestWindow_SetContent_Twice(t *testing.T) { w := createWindow("Test").(*window) diff --git a/internal/driver/mobile/driver.go b/internal/driver/mobile/driver.go index 9602d683f0..0d48c41461 100644 --- a/internal/driver/mobile/driver.go +++ b/internal/driver/mobile/driver.go @@ -288,6 +288,10 @@ func (d *mobileDriver) paintWindow(window fyne.Window, 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) } afterDraw := func(node *common.RenderCacheNode) { diff --git a/internal/painter/font.go b/internal/painter/font.go index 8afbda226d..5804140ecf 100644 --- a/internal/painter/font.go +++ b/internal/painter/font.go @@ -84,18 +84,33 @@ func CachedFontFace(style fyne.TextStyle, fontDP float32, texScale float32) (fon switch { case style.Monospace: measureFace = loadMeasureFont(theme.TextMonospaceFont()) + if measureFace == nil { + measureFace = loadMeasureFont(theme.DefaultTextMonospaceFont()) + } case style.Bold: if style.Italic { measureFace = loadMeasureFont(theme.TextBoldItalicFont()) + if measureFace == nil { + measureFace = loadMeasureFont(theme.DefaultTextBoldItalicFont()) + } } else { measureFace = loadMeasureFont(theme.TextBoldFont()) + if measureFace == nil { + measureFace = loadMeasureFont(theme.DefaultTextBoldFont()) + } } case style.Italic: measureFace = loadMeasureFont(theme.TextItalicFont()) + if measureFace == nil { + measureFace = loadMeasureFont(theme.DefaultTextItalicFont()) + } case style.Symbol: measureFace = loadMeasureFont(theme.DefaultSymbolFont()) default: measureFace = loadMeasureFont(theme.TextFont()) + if measureFace == nil { + measureFace = loadMeasureFont(theme.DefaultTextFont()) + } } comp.facesMutex.Lock() @@ -148,6 +163,7 @@ func loadMeasureFont(data fyne.Resource) gotext.Face { loaded, err := gotext.ParseTTF(bytes.NewReader(data.Content())) if err != nil { fyne.LogError("font load error", err) + return nil } return loaded diff --git a/internal/painter/gl/gl_es.go b/internal/painter/gl/gl_es.go index 98d62f436e..9ba564eb49 100644 --- a/internal/painter/gl/gl_es.go +++ b/internal/painter/gl/gl_es.go @@ -67,7 +67,7 @@ type ( Uniform int32 ) -var textureFilterToGL = []int32{gl.LINEAR, gl.NEAREST} +var textureFilterToGL = []int32{gl.LINEAR, gl.NEAREST, gl.LINEAR} func (p *painter) Init() { p.ctx = &esContext{} diff --git a/internal/painter/image.go b/internal/painter/image.go index 4e82fe07ed..4ab588558f 100644 --- a/internal/painter/image.go +++ b/internal/painter/image.go @@ -29,6 +29,11 @@ func GetAspect(img *canvas.Image) float32 { aspect = aspects[img.Resource.Name()] } else if img.File != "" { aspect = aspects[img.File] + } else if img.Image != nil { + // HOTFIX until Fyne 2.4 proper fix: + // we are not storing the aspect ratio in the map for the image.Image case + size := img.Image.Bounds().Size() + return float32(size.X) / float32(size.Y) } if aspect == 0 { @@ -132,7 +137,9 @@ func paintImage(img *canvas.Image, width, height int, wantOrigSize bool, wantOri case img.Image != nil: origSize := img.Image.Bounds().Size() origW, origH = origSize.X, origSize.Y - if checkSize(origSize.X, origSize.Y) { + // HOTFIX until Fyne 2.4: don't store aspect ratio in map, as checkSize(x, y) does. + // Doing so leaks a reference to the image.Image data + if !wantOrigSize || (wantOrigW == origW && wantOrigH == origH) { dst = scaleImage(img.Image, width, height, img.ScaleMode) } default: diff --git a/theme/theme.go b/theme/theme.go index cabffeda32..c94f67d153 100644 --- a/theme/theme.go +++ b/theme/theme.go @@ -760,7 +760,7 @@ func lightPaletColorNamed(name fyne.ThemeColorName) color.Color { case ColorNameScrollBar: return color.NRGBA{A: 0x99} case ColorNameSeparator: - return color.NRGBA{R: 0xf5, G: 0xf5, B: 0xf5, A: 0xff} + return color.NRGBA{R: 0xe3, G: 0xe3, B: 0xe3, A: 0xff} case ColorNameShadow: return color.NRGBA{A: 0x33} case ColorNameSuccess: diff --git a/vendor/fyne.io/systray/systray_menu_unix.go b/vendor/fyne.io/systray/systray_menu_unix.go index 1c23a1ab7f..2d6ed207dc 100644 --- a/vendor/fyne.io/systray/systray_menu_unix.go +++ b/vendor/fyne.io/systray/systray_menu_unix.go @@ -319,7 +319,7 @@ func refresh() { dbusErr := instance.menuProps.Set("com.canonical.dbusmenu", "Version", dbus.MakeVariant(instance.menuVersion)) if dbusErr != nil { - log.Printf("systray error: failed to update menu version: %s\n", dbusErr) + log.Printf("systray error: failed to update menu version: %v\n", dbusErr) return } err := menu.Emit(instance.conn, &menu.Dbusmenu_LayoutUpdatedSignal{ @@ -329,7 +329,7 @@ func refresh() { }, }) if err != nil { - log.Printf("systray error: failed to emit layout updated signal: %s\n", err) + log.Printf("systray error: failed to emit layout updated signal: %v\n", err) } } diff --git a/vendor/fyne.io/systray/systray_unix.go b/vendor/fyne.io/systray/systray_unix.go index cd6ecb9bc2..842633ce3e 100644 --- a/vendor/fyne.io/systray/systray_unix.go +++ b/vendor/fyne.io/systray/systray_unix.go @@ -60,12 +60,8 @@ func SetIcon(iconBytes []byte) { return } - dbusErr := props.Set("org.kde.StatusNotifierItem", "IconPixmap", - dbus.MakeVariant([]PX{convertToPixels(iconBytes)})) - if dbusErr != nil { - log.Printf("systray error: failed to set IconPixmap prop: %s\n", dbusErr) - return - } + props.SetMust("org.kde.StatusNotifierItem", "IconPixmap", + []PX{convertToPixels(iconBytes)}) if conn == nil { return } @@ -164,18 +160,18 @@ func quit() { func nativeStart() { systrayReady() - conn, _ := dbus.ConnectSessionBus() - if conn == nil { - log.Printf("systray error: failed to connect to DBus") + conn, err := dbus.SessionBus() + if err != nil { + log.Printf("systray error: failed to connect to DBus: %v\n", err) return } - err := notifier.ExportStatusNotifierItem(conn, path, ¬ifier.UnimplementedStatusNotifierItem{}) + err = notifier.ExportStatusNotifierItem(conn, path, ¬ifier.UnimplementedStatusNotifierItem{}) if err != nil { - log.Printf("systray error: failed to export status notifier item: %s\n", err) + log.Printf("systray error: failed to export status notifier item: %v\n", err) } err = menu.ExportDbusmenu(conn, menuPath, instance) if err != nil { - log.Printf("systray error: failed to export status notifier item: %s\n", err) + log.Printf("systray error: failed to export status notifier menu: %v\n", err) return } diff --git a/vendor/modules.txt b/vendor/modules.txt index b2dab53c50..6bd50e7309 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ -# fyne.io/systray v1.10.1-0.20230403195833-7dc3c09283d6 +# fyne.io/systray v1.10.1-0.20230602210930-b6a2d6ca2a7b ## explicit fyne.io/systray fyne.io/systray/internal/generated/menu diff --git a/widget/button.go b/widget/button.go index 04b7ff1664..5f7e5204fa 100644 --- a/widget/button.go +++ b/widget/button.go @@ -231,7 +231,16 @@ func (b *Button) buttonColor() color.Color { } return theme.DisabledButtonColor() case b.focused: - return blendColor(theme.ButtonColor(), theme.FocusColor()) + bg := theme.ButtonColor() + if b.Importance == HighImportance { + bg = theme.PrimaryColor() + } else if b.Importance == DangerImportance { + bg = theme.ErrorColor() + } else if b.Importance == WarningImportance { + bg = theme.WarningColor() + } + + return blendColor(bg, theme.FocusColor()) case b.hovered: bg := theme.ButtonColor() if b.Importance == HighImportance { @@ -356,7 +365,11 @@ func (r *buttonRenderer) applyTheme() { case r.button.disabled: r.label.Segments[0].(*TextSegment).Style.ColorName = theme.ColorNameDisabled case r.button.Importance == HighImportance || r.button.Importance == DangerImportance || r.button.Importance == WarningImportance: - r.label.Segments[0].(*TextSegment).Style.ColorName = theme.ColorNameBackground + if r.button.focused { + r.label.Segments[0].(*TextSegment).Style.ColorName = theme.ColorNameForeground + } else { + r.label.Segments[0].(*TextSegment).Style.ColorName = theme.ColorNameBackground + } } r.label.Refresh() if r.icon != nil && r.icon.Resource != nil { diff --git a/widget/check_group.go b/widget/check_group.go index d468e1fde0..ad1f28e5b2 100644 --- a/widget/check_group.go +++ b/widget/check_group.go @@ -217,13 +217,15 @@ func (r *checkGroupRenderer) MinSize() fyne.Size { height := float32(0) for _, item := range r.items { itemMin := item.MinSize() - if r.checks.Horizontal { - height = fyne.Max(height, itemMin.Height) - width += itemMin.Width - } else { - width = fyne.Max(width, itemMin.Width) - height += itemMin.Height - } + + width = fyne.Max(width, itemMin.Width) + height = fyne.Max(height, itemMin.Height) + } + + if r.checks.Horizontal { + width = width * float32(len(r.items)) + } else { + height = height * float32(len(r.items)) } return fyne.NewSize(width, height) diff --git a/widget/check_group_test.go b/widget/check_group_test.go index e8efc9a457..8605ccb806 100644 --- a/widget/check_group_test.go +++ b/widget/check_group_test.go @@ -116,12 +116,12 @@ func TestCheckGroup_Layout(t *testing.T) { }, "multiple_horizontal": { horizontal: true, - options: []string{"Foo", "Bar"}, + options: []string{"Foo", "Barley"}, }, "multiple_horizontal_disabled": { disabled: true, horizontal: true, - options: []string{"Foo", "Bar"}, + options: []string{"Foo", "Barley"}, }, "multiple_selected": { options: []string{"Foo", "Bar"}, diff --git a/widget/entry_password.go b/widget/entry_password.go index d88eb7a53a..501925667c 100644 --- a/widget/entry_password.go +++ b/widget/entry_password.go @@ -40,6 +40,10 @@ func (r *passwordRevealer) Cursor() desktop.Cursor { } func (r *passwordRevealer) Tapped(*fyne.PointEvent) { + if r.entry.Disabled() { + return + } + r.entry.setFieldsAndRefresh(func() { r.entry.Password = !r.entry.Password }) @@ -71,5 +75,9 @@ func (r *passwordRevealerRenderer) Refresh() { } else { r.icon.Resource = theme.VisibilityOffIcon() } + + if r.entry.disabled { + r.icon.Resource = theme.NewDisabledResource(r.icon.Resource) + } canvas.Refresh(r.icon) } diff --git a/widget/entry_test.go b/widget/entry_test.go index 8ea6955023..f68aaa7094 100644 --- a/widget/entry_test.go +++ b/widget/entry_test.go @@ -1694,6 +1694,19 @@ func TestPasswordEntry_ActionItemSizeAndPlacement(t *testing.T) { assert.Equal(t, fyne.NewPos(e.MinSize().Width-2*theme.Padding()-b.Size().Width, 2*theme.Padding()), b.Position()) } +func TestPasswordEntry_Disabled(t *testing.T) { + entry, window := setupPasswordTest(t) + defer teardownImageTest(window) + entry.Disable() + + test.Tap(entry.ActionItem.(fyne.Tappable)) + assert.True(t, entry.Password) + + entry.Enable() + test.Tap(entry.ActionItem.(fyne.Tappable)) + assert.False(t, entry.Password) +} + func TestPasswordEntry_NewlineIgnored(t *testing.T) { entry := widget.NewPasswordEntry() entry.SetText("test") diff --git a/widget/radio_group_internal_test.go b/widget/radio_group_internal_test.go index eb8770fd4a..ac5bffee72 100644 --- a/widget/radio_group_internal_test.go +++ b/widget/radio_group_internal_test.go @@ -70,7 +70,7 @@ func TestRadioGroup_DisableWhenSelected(t *testing.T) { assert.Equal(t, "primary_"+icon.Name(), render.icon.Resource.Name()) radio.Disable() - assert.Equal(t, fmt.Sprintf("background_%v", icon.Name()), render.icon.Resource.Name()) + assert.Equal(t, fmt.Sprintf("disabled_%v", icon.Name()), render.icon.Resource.Name()) } func TestRadioGroup_DisableWhenNotSelected(t *testing.T) { diff --git a/widget/radio_item.go b/widget/radio_item.go index 611ea18b6f..915c9cdfc9 100644 --- a/widget/radio_item.go +++ b/widget/radio_item.go @@ -205,7 +205,11 @@ func (r *radioItemRenderer) update() { out.ColorName = theme.ColorNameForeground } if r.item.Disabled() { - in.ColorName = theme.ColorNameBackground + if r.item.Selected { + in.ColorName = theme.ColorNameDisabled + } else { + in.ColorName = theme.ColorNameBackground + } out.ColorName = theme.ColorNameDisabled } r.icon.Resource = in diff --git a/widget/richtext.go b/widget/richtext.go index ad0b0709e2..4025763a73 100644 --- a/widget/richtext.go +++ b/widget/richtext.go @@ -352,14 +352,16 @@ func (t *RichText) updateRowBounds() { maxWidth := t.size.Width - 2*innerPadding + 2*t.inset.Width wrapWidth := maxWidth + var currentBound *rowBoundary var iterateSegments func(segList []RichTextSegment) iterateSegments = func(segList []RichTextSegment) { - var currentBound *rowBoundary for _, seg := range segList { if parent, ok := seg.(RichTextBlock); ok { - iterateSegments(parent.Segments()) - if !seg.Inline() { + segs := parent.Segments() + iterateSegments(segs) + if len(segs) > 0 && !segs[len(segs)-1].Inline() { wrapWidth = maxWidth + currentBound = nil } continue } @@ -464,7 +466,8 @@ func (r *textRenderer) Layout(size fyne.Size) { innerPadding := theme.InnerPadding() lineSpacing := theme.LineSpacing() - left := innerPadding - r.obj.inset.Width + xInset := innerPadding - r.obj.inset.Width + left := xInset yPos := innerPadding - r.obj.inset.Height lineWidth := size.Width - left*2 var rowItems []fyne.CanvasObject @@ -483,12 +486,14 @@ func (r *textRenderer) Layout(size fyne.Size) { if len(rowItems) != 0 { width, _ := r.layoutRow(rowItems, rowAlign, left, yPos, lineWidth) left += width + rowItems = nil } height := obj.MinSize().Height obj.Move(fyne.NewPos(left, yPos)) obj.Resize(fyne.NewSize(lineWidth, height)) - yPos += height + lineSpacing + yPos += height + left = xInset continue } rowItems = append(rowItems, obj) diff --git a/widget/richtext_objects.go b/widget/richtext_objects.go index 7ed107d849..b285c24836 100644 --- a/widget/richtext_objects.go +++ b/widget/richtext_objects.go @@ -226,16 +226,10 @@ func (l *ListSegment) Segments() []RichTextSegment { txt = strconv.Itoa(i+1) + "." } bullet := &TextSegment{Text: txt + " ", Style: RichTextStyleStrong} - if para, ok := in.(*ParagraphSegment); ok { - seg := &ParagraphSegment{Texts: []RichTextSegment{bullet}} - seg.Texts = append(seg.Texts, para.Texts...) - out[i] = seg - } else { - out[i] = &ParagraphSegment{Texts: []RichTextSegment{ - bullet, - in, - }} - } + out[i] = &ParagraphSegment{Texts: []RichTextSegment{ + bullet, + in, + }} } return out } diff --git a/widget/table.go b/widget/table.go index dde9efc512..3453062e60 100644 --- a/widget/table.go +++ b/widget/table.go @@ -222,6 +222,9 @@ func (t *Table) ScrollToBottom() { rows, _ := t.Length() cellY, cellHeight := t.findY(rows - 1) y := cellY + cellHeight - t.scroll.Size().Height + if y <= 0 { + return + } t.scroll.Offset.Y = y t.offset.Y = y @@ -265,6 +268,9 @@ func (t *Table) ScrollToTrailing() { _, cols := t.Length() cellX, cellWidth := t.findX(cols - 1) scrollX := cellX + cellWidth - t.scroll.Size().Width + if scrollX <= 0 { + return + } t.scroll.Offset.X = scrollX t.offset.X = scrollX diff --git a/widget/testdata/check_group/layout_multiple_horizontal.xml b/widget/testdata/check_group/layout_multiple_horizontal.xml index f9ffc07fdf..2bfd74f2a6 100644 --- a/widget/testdata/check_group/layout_multiple_horizontal.xml +++ b/widget/testdata/check_group/layout_multiple_horizontal.xml @@ -1,17 +1,17 @@ - + - - + + - Foo + Foo - + - Bar + Barley diff --git a/widget/testdata/check_group/layout_multiple_horizontal_disabled.xml b/widget/testdata/check_group/layout_multiple_horizontal_disabled.xml index e332b2a4ee..e88a895960 100644 --- a/widget/testdata/check_group/layout_multiple_horizontal_disabled.xml +++ b/widget/testdata/check_group/layout_multiple_horizontal_disabled.xml @@ -1,17 +1,17 @@ - + - - + + - Foo + Foo - + - Bar + Barley diff --git a/widget/testdata/check_group/layout_multiple_selected_horizontal.xml b/widget/testdata/check_group/layout_multiple_selected_horizontal.xml index 48954a9dcb..f1ec2b0716 100644 --- a/widget/testdata/check_group/layout_multiple_selected_horizontal.xml +++ b/widget/testdata/check_group/layout_multiple_selected_horizontal.xml @@ -1,17 +1,17 @@ - + - Foo + Foo - + - Bar + Bar diff --git a/widget/testdata/check_group/layout_multiple_selected_horizontal_disabled.xml b/widget/testdata/check_group/layout_multiple_selected_horizontal_disabled.xml index a71193af03..879d0fd6eb 100644 --- a/widget/testdata/check_group/layout_multiple_selected_horizontal_disabled.xml +++ b/widget/testdata/check_group/layout_multiple_selected_horizontal_disabled.xml @@ -1,17 +1,17 @@ - + - Foo + Foo - + - Bar + Bar diff --git a/widget/testdata/radio_group/layout_multiple_selected_disabled.xml b/widget/testdata/radio_group/layout_multiple_selected_disabled.xml index 14c7f43950..cc0905f405 100644 --- a/widget/testdata/radio_group/layout_multiple_selected_disabled.xml +++ b/widget/testdata/radio_group/layout_multiple_selected_disabled.xml @@ -4,7 +4,7 @@ - + Foo diff --git a/widget/testdata/radio_group/layout_multiple_selected_horizontal_disabled.xml b/widget/testdata/radio_group/layout_multiple_selected_horizontal_disabled.xml index 379351e431..28a6728356 100644 --- a/widget/testdata/radio_group/layout_multiple_selected_horizontal_disabled.xml +++ b/widget/testdata/radio_group/layout_multiple_selected_horizontal_disabled.xml @@ -4,7 +4,7 @@ - + Foo diff --git a/widget/testdata/radio_group/layout_single_selected_disabled.xml b/widget/testdata/radio_group/layout_single_selected_disabled.xml index 48de7eddbe..a17de0ab5b 100644 --- a/widget/testdata/radio_group/layout_single_selected_disabled.xml +++ b/widget/testdata/radio_group/layout_single_selected_disabled.xml @@ -4,7 +4,7 @@ - + Test diff --git a/widget/testdata/radio_group/layout_single_selected_horizontal_disabled.xml b/widget/testdata/radio_group/layout_single_selected_horizontal_disabled.xml index 48de7eddbe..a17de0ab5b 100644 --- a/widget/testdata/radio_group/layout_single_selected_horizontal_disabled.xml +++ b/widget/testdata/radio_group/layout_single_selected_horizontal_disabled.xml @@ -4,7 +4,7 @@ - + Test diff --git a/widget/testdata/table/col_size.png b/widget/testdata/table/col_size.png index 840df6c948..09ec90ebbf 100644 Binary files a/widget/testdata/table/col_size.png and b/widget/testdata/table/col_size.png differ diff --git a/widget/testdata/table/filled.png b/widget/testdata/table/filled.png index a259b549c4..5683f03780 100644 Binary files a/widget/testdata/table/filled.png and b/widget/testdata/table/filled.png differ diff --git a/widget/testdata/table/row_size.png b/widget/testdata/table/row_size.png index 660c9510b5..cb6ca8a345 100644 Binary files a/widget/testdata/table/row_size.png and b/widget/testdata/table/row_size.png differ diff --git a/widget/testdata/tree/refresh_initial.png b/widget/testdata/tree/refresh_initial.png index 50926c8e16..61f9236a4e 100644 Binary files a/widget/testdata/tree/refresh_initial.png and b/widget/testdata/tree/refresh_initial.png differ diff --git a/widget/testdata/tree/refresh_replaced.png b/widget/testdata/tree/refresh_replaced.png index 87ab9c752c..8838908b19 100644 Binary files a/widget/testdata/tree/refresh_replaced.png and b/widget/testdata/tree/refresh_replaced.png differ