diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index a3a21f725a..006afafe82 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1 +1 @@
-github: [fyne-io, andydotxyz, toaster, okratitan]
+github: [fyne-io, andydotxyz, toaster, okratitan, Jacalz]
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b083d6cf66..5f6ca0cecd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,13 +7,42 @@ More detailed release notes can be found on the [releases page](https://github.c
### Added
-### Updated
+### Changed
* Focusable widgets are no longer focused on tap, add canvas.Focus(obj) in Tapped handler if required
### Fixed
+## 2.0.4 - 6 August 2021
+
+### Changed
+
+* Disable Form labels when the element it applys to is disabled (#1530)
+* Entry popup menu now fires shortcuts so extended widgets can intercept
+* Update Android builds to SDK 30
+
+### Fixed
+
+* sendnotification show appID for name on windows (#1940)
+* Fix accidental removal of windows builds during cross-compile
+* Removing an item from a container did not update layout
+* Update title bar on Windows 10 to match OS theme (#2184)
+* Tapped triggered after Drag (#2235)
+* Improved documentation and example code for file dialog (#2156)
+* Preferences file gets unexpectedly cleared (#2241)
+* Extra row dividers rendered on using SetColumnWidth to update a table (#2266)
+* Fix resizing fullscreen issue
+* Fullscreen changes my display resolution when showing a dialog (#1832)
+* Entry validation does not work for empty field (#2179)
+* Tab support for focus handling missing on mobile
+* ScrollToBottom not always scrolling all the way when items added to container.Scroller
+* Fixed scrollbar disappearing after changing content (#2303)
+* Calling SetContent a second time with the same content will not show
+* Drawing text can panic when Color is nil (#2347)
+* Optimisations when drawing transparent rectangle or whitespace strings
+
+
## 2.0.3 - 30 April 2021
### Fixed
diff --git a/README.md b/README.md
index e23bafe647..ad3985419a 100644
--- a/README.md
+++ b/README.md
@@ -154,3 +154,23 @@ More documentation is available at the [Fyne developer website](https://develope
You can find many example applications in the [examples repository](https://github.com/fyne-io/examples/).
Alternatively a list of applications using fyne can be found at [our website](https://apps.fyne.io/).
+
+# Shipping the Fyne Toolkit
+
+All Fyne apps will work without pre-installed libraries, this is one reason the apps are so portable.
+However, if looking to support Fyne in a bigger way on your operating system then you can install some utilities that help to make a more complete experience.
+
+## Additional apps
+
+It is recommended that you install the following additional apps:
+
+| app | go get | description |
+| --- | ------ | ----------- |
+| fyne_settings | `fyne.io/fyne/v2/cmd/fyne_settings` | A GUI for managing your global Fyne settings like theme and scaling |
+| apps | `github.com/fyne-io/apps` | A graphical installer for the Fyne apps listed at https://apps.fyne.io |
+
+These are optional applications but can help to create a more complete desktop experience.
+
+## FyneDesk (Linux / BSD)
+
+To go all the way with Fyne on your desktop / laptop computer you could install [FyneDesk](https://github.com/fyne-io/fynedesk) as well :)
diff --git a/canvas/image.go b/canvas/image.go
index fed8b1d9d8..f2aa8efaa5 100644
--- a/canvas/image.go
+++ b/canvas/image.go
@@ -74,6 +74,9 @@ func (i *Image) Resize(s fyne.Size) {
if s == i.Size() {
return
}
+ if i.FillMode == ImageFillOriginal && i.size.Height > 2 { // don't refresh original scale images after first draw
+ return
+ }
i.baseObject.Resize(s)
diff --git a/canvas/text.go b/canvas/text.go
index 81c05a5f98..a172733133 100644
--- a/canvas/text.go
+++ b/canvas/text.go
@@ -40,9 +40,13 @@ func (t *Text) Refresh() {
// NewText returns a new Text implementation
func NewText(text string, color color.Color) *Text {
+ size := float32(0)
+ if fyne.CurrentApp() != nil { // nil app possible if app not started
+ size = fyne.CurrentApp().Settings().Theme().Size("text") // manually name the size to avoid import loop
+ }
return &Text{
Color: color,
Text: text,
- TextSize: fyne.CurrentApp().Settings().Theme().Size("text"), // manually name the size to avoid import loop
+ TextSize: size,
}
}
diff --git a/cmd/fyne/internal/mobile/binres/binres.go b/cmd/fyne/internal/mobile/binres/binres.go
index ec67ea0405..e855385c1a 100644
--- a/cmd/fyne/internal/mobile/binres/binres.go
+++ b/cmd/fyne/internal/mobile/binres/binres.go
@@ -210,7 +210,7 @@ type ltoken struct {
// UnmarshalXML decodes an AndroidManifest.xml document returning type XML
// containing decoded resources.
-func UnmarshalXML(r io.Reader, withIcon bool) (*XML, error) {
+func UnmarshalXML(r io.Reader, withIcon bool, targetSDK int) (*XML, error) {
lr := &lineReader{r: r}
dec := xml.NewDecoder(lr)
@@ -273,7 +273,7 @@ func UnmarshalXML(r io.Reader, withIcon bool) (*XML, error) {
Space: androidSchema,
Local: "targetSdkVersion",
},
- Value: "29",
+ Value: strconv.Itoa(targetSDK),
},
},
}
diff --git a/cmd/fyne/internal/mobile/binres/binres_test.go b/cmd/fyne/internal/mobile/binres/binres_test.go
index b56877a6da..feab9ed73c 100644
--- a/cmd/fyne/internal/mobile/binres/binres_test.go
+++ b/cmd/fyne/internal/mobile/binres/binres_test.go
@@ -155,7 +155,7 @@ func TestEncode(t *testing.T) {
f, err := os.Open("testdata/bootstrap.xml")
r.NoError(err)
- bx, err := UnmarshalXML(f, false)
+ bx, err := UnmarshalXML(f, false, 30)
r.NoError(err)
bin, err := ioutil.ReadFile("testdata/bootstrap.bin")
@@ -183,7 +183,7 @@ func TestRawValueByName(t *testing.T) {
f, err := os.Open("testdata/bootstrap.xml")
r.NoError(err)
- bx, err := UnmarshalXML(f, false)
+ bx, err := UnmarshalXML(f, false, 30)
r.NoError(err)
pkgname, err := bx.RawValueByName("manifest", xml.Name{Local: "package"})
diff --git a/cmd/fyne/internal/mobile/build.go b/cmd/fyne/internal/mobile/build.go
index 76b11d28ea..392eb0585c 100644
--- a/cmd/fyne/internal/mobile/build.go
+++ b/cmd/fyne/internal/mobile/build.go
@@ -143,7 +143,11 @@ func runBuildImpl(cmd *command) (*packages.Package, error) {
}
return pkg, nil
}
- nmpkgs, err = goAndroidBuild(pkg, buildBundleID, targetArchs, cmd.IconPath, cmd.AppName, cmd.Version, cmd.Build)
+ target := 30
+ if !buildRelease {
+ target = 29 // TODO once we have gomobile debug signing working for v2 android signs
+ }
+ nmpkgs, err = goAndroidBuild(pkg, buildBundleID, targetArchs, cmd.IconPath, cmd.AppName, cmd.Version, cmd.Build, target, buildRelease)
if err != nil {
return nil, err
}
diff --git a/cmd/fyne/internal/mobile/build_androidapp.go b/cmd/fyne/internal/mobile/build_androidapp.go
index dc2298ac40..16c3945595 100644
--- a/cmd/fyne/internal/mobile/build_androidapp.go
+++ b/cmd/fyne/internal/mobile/build_androidapp.go
@@ -24,7 +24,7 @@ import (
)
func goAndroidBuild(pkg *packages.Package, bundleID string, androidArchs []string,
- iconPath, appName, version string, build int) (map[string]bool, error) {
+ iconPath, appName, version string, build, target int, release bool) (map[string]bool, error) {
ndkRoot, err := ndkRoot()
if err != nil {
return nil, err
@@ -117,7 +117,7 @@ func goAndroidBuild(pkg *packages.Package, bundleID string, androidArchs []strin
if err != nil {
return nil, err
}
- err = addAssets(apkw, manifestData, dir, iconPath)
+ err = addAssets(apkw, manifestData, dir, iconPath, target)
if err != nil {
return nil, err
}
@@ -134,7 +134,7 @@ func goAndroidBuild(pkg *packages.Package, bundleID string, androidArchs []strin
return nmpkgs[androidArchs[0]], nil
}
-func addAssets(apkw *Writer, manifestData []byte, dir, iconPath string) error {
+func addAssets(apkw *Writer, manifestData []byte, dir, iconPath string, target int) error {
// Add any assets.
var arsc struct {
iconPath string
@@ -186,7 +186,7 @@ func addAssets(apkw *Writer, manifestData []byte, dir, iconPath string) error {
}
}
- bxml, err := binres.UnmarshalXML(bytes.NewReader(manifestData), arsc.iconPath != "")
+ bxml, err := binres.UnmarshalXML(bytes.NewReader(manifestData), arsc.iconPath != "", target)
if err != nil {
return err
}
diff --git a/cmd/fyne_demo/tutorials/widget.go b/cmd/fyne_demo/tutorials/widget.go
index 4c429c7a26..58c06f9cb8 100644
--- a/cmd/fyne_demo/tutorials/widget.go
+++ b/cmd/fyne_demo/tutorials/widget.go
@@ -325,6 +325,9 @@ func makeFormTab(_ fyne.Window) fyne.CanvasObject {
password := widget.NewPasswordEntry()
password.SetPlaceHolder("Password")
+ disabled := widget.NewRadioGroup([]string{"Option 1", "Option 2"}, func(string) {})
+ disabled.Horizontal = true
+ disabled.Disable()
largeText := widget.NewMultiLineEntry()
form := &widget.Form{
@@ -344,6 +347,7 @@ func makeFormTab(_ fyne.Window) fyne.CanvasObject {
},
}
form.Append("Password", password)
+ form.Append("Disabled", disabled)
form.Append("Message", largeText)
return form
}
diff --git a/internal/driver/glfw/window.go b/internal/driver/glfw/window.go
index f7df9284ba..7279ff76e5 100644
--- a/internal/driver/glfw/window.go
+++ b/internal/driver/glfw/window.go
@@ -484,6 +484,10 @@ func (w *window) SetContent(content fyne.CanvasObject) {
}
w.canvas.SetContent(content)
+ // show new canvas element
+ if content != nil {
+ content.Show()
+ }
w.RescaleContext()
}
@@ -596,7 +600,6 @@ func (w *window) mouseMoved(viewport *glfw.Window, xpos float64, ypos float64) {
w.mousePos = fyne.NewPos(internal.UnscaleInt(w.canvas, int(xpos)), internal.UnscaleInt(w.canvas, int(ypos)))
mousePos := w.mousePos
mouseButton := w.mouseButton
- mouseDragStarted := w.mouseDragStarted
mouseOver := w.mouseOver
w.mouseLock.Unlock()
@@ -631,7 +634,7 @@ func (w *window) mouseMoved(viewport *glfw.Window, xpos float64, ypos float64) {
}
}
- if mouseButton != 0 && mouseButton != desktop.MouseButtonSecondary && !mouseDragStarted {
+ if w.mouseButton != 0 && w.mouseButton != desktop.MouseButtonSecondary && !w.mouseDragStarted {
obj, pos, _ := w.findObjectAtPositionMatching(w.canvas, previousPos, func(object fyne.CanvasObject) bool {
_, ok := object.(fyne.Draggable)
return ok
@@ -690,14 +693,16 @@ func (w *window) mouseMoved(viewport *glfw.Window, xpos float64, ypos float64) {
mouseDraggedOffset := w.mouseDraggedOffset
mouseDragPos := w.mouseDragPos
w.mouseLock.RUnlock()
- if mouseDragged != nil && mouseButton > 0 && mouseButton != desktop.MouseButtonSecondary {
- draggedObjDelta := mouseDraggedObjStart.Subtract(mouseDragged.(fyne.CanvasObject).Position())
- ev := new(fyne.DragEvent)
- ev.AbsolutePosition = mousePos
- ev.Position = mousePos.Subtract(mouseDraggedOffset).Add(draggedObjDelta)
- ev.Dragged = fyne.NewDelta(mousePos.X-mouseDragPos.X, mousePos.Y-mouseDragPos.Y)
- wd := mouseDragged
- w.QueueEvent(func() { wd.Dragged(ev) })
+ if w.mouseDragged != nil && w.mouseButton != desktop.MouseButtonSecondary {
+ if w.mouseButton > 0 {
+ draggedObjDelta := mouseDraggedObjStart.Subtract(mouseDragged.(fyne.CanvasObject).Position())
+ ev := new(fyne.DragEvent)
+ ev.AbsolutePosition = mousePos
+ ev.Position = mousePos.Subtract(mouseDraggedOffset).Add(draggedObjDelta)
+ ev.Dragged = fyne.NewDelta(mousePos.X-mouseDragPos.X, mousePos.Y-mouseDragPos.Y)
+ wd := mouseDragged
+ w.QueueEvent(func() { wd.Dragged(ev) })
+ }
w.mouseLock.Lock()
w.mouseDragStarted = true
diff --git a/internal/driver/glfw/window_test.go b/internal/driver/glfw/window_test.go
index 26f745190b..b3cfe61a0d 100644
--- a/internal/driver/glfw/window_test.go
+++ b/internal/driver/glfw/window_test.go
@@ -349,6 +349,30 @@ func TestWindow_HandleDragging(t *testing.T) {
assert.Nil(t, d1.popDragEvent())
assert.Nil(t, d2.popDragEvent())
+ // no drag event on secondary mouseDown
+ w.mouseClicked(w.viewport, glfw.MouseButton2, glfw.Press, 0)
+ w.WaitForEvents()
+ assert.Nil(t, d1.popDragEvent())
+ assert.Nil(t, d2.popDragEvent())
+
+ // no drag start and no drag event with pressed secondary mouse button
+ w.mouseMoved(w.viewport, 8, 8)
+ w.WaitForEvents()
+ assert.Nil(t, d1.popDragEvent())
+ assert.Nil(t, d2.popDragEvent())
+
+ // no drag end event on secondary mouseUp
+ w.mouseClicked(w.viewport, glfw.MouseButton2, glfw.Release, 0)
+ w.WaitForEvents()
+ assert.Nil(t, d1.popDragEndEvent())
+ assert.Nil(t, d2.popDragEndEvent())
+
+ // no drag event in simple move
+ w.mouseMoved(w.viewport, 9, 9)
+ w.WaitForEvents()
+ assert.Nil(t, d1.popDragEvent())
+ assert.Nil(t, d2.popDragEvent())
+
// no drag event on mouseDown
w.mouseClicked(w.viewport, glfw.MouseButton1, glfw.Press, 0)
w.WaitForEvents()
@@ -1594,6 +1618,20 @@ func TestWindow_CloseInterception(t *testing.T) {
assert.True(t, onClosed) // Close is called if the interceptor is not set.
}
+func TestWindow_SetContent_Twice(t *testing.T) {
+ w := createWindow("Test").(*window)
+
+ e1 := widget.NewLabel("1")
+ e2 := widget.NewLabel("2")
+
+ w.SetContent(e1)
+ assert.True(t, e1.Visible())
+ w.SetContent(e2)
+ assert.True(t, e2.Visible())
+ w.SetContent(e1)
+ assert.True(t, e1.Visible())
+}
+
// This test makes our developer screens flash, let's not run it regularly...
//func TestWindow_Shortcut(t *testing.T) {
// w := createWindow("Test")
diff --git a/internal/driver/gomobile/driver.go b/internal/driver/gomobile/driver.go
index f1aa31b926..77c63c752b 100644
--- a/internal/driver/gomobile/driver.go
+++ b/internal/driver/gomobile/driver.go
@@ -197,9 +197,9 @@ func (d *mobileDriver) Run() {
}
case key.Event:
if e.Direction == key.DirPress {
- d.typeDownCanvas(c, e.Rune, e.Code)
+ d.typeDownCanvas(c, e.Rune, e.Code, e.Modifiers)
} else if e.Direction == key.DirRelease {
- d.typeUpCanvas(c, e.Rune, e.Code)
+ d.typeUpCanvas(c, e.Rune, e.Code, e.Modifiers)
}
}
}
@@ -471,8 +471,26 @@ func runeToPrintable(r rune) rune {
return 0
}
-func (d *mobileDriver) typeDownCanvas(canvas *mobileCanvas, r rune, code key.Code) {
+func (d *mobileDriver) typeDownCanvas(canvas *mobileCanvas, r rune, code key.Code, mod key.Modifiers) {
keyName := keyToName(code)
+ switch keyName {
+ case fyne.KeyTab:
+ capture := false
+ if ent, ok := canvas.Focused().(fyne.Tabbable); ok {
+ capture = ent.AcceptsTab()
+ }
+ if !capture {
+ switch mod {
+ case 0:
+ canvas.FocusNext()
+ return
+ case key.ModShift:
+ canvas.FocusPrevious()
+ return
+ }
+ }
+ }
+
r = runeToPrintable(r)
keyEvent := &fyne.KeyEvent{Name: keyName}
@@ -501,8 +519,7 @@ func (d *mobileDriver) typeDownCanvas(canvas *mobileCanvas, r rune, code key.Cod
}
}
-func (d *mobileDriver) typeUpCanvas(canvas *mobileCanvas, r rune, code key.Code) {
-
+func (d *mobileDriver) typeUpCanvas(_ *mobileCanvas, _ rune, _ key.Code, _ key.Modifiers) {
}
func (d *mobileDriver) Device() fyne.Device {
diff --git a/internal/painter/gl/draw.go b/internal/painter/gl/draw.go
index 6a4be0c7ad..77487b63d6 100644
--- a/internal/painter/gl/draw.go
+++ b/internal/painter/gl/draw.go
@@ -1,6 +1,7 @@
package gl
import (
+ "image/color"
"math"
"fyne.io/fyne/v2"
@@ -55,12 +56,15 @@ func (p *glPainter) drawGradient(o fyne.CanvasObject, texCreator func(fyne.Canva
}
func (p *glPainter) drawRectangle(rect *canvas.Rectangle, pos fyne.Position, frame fyne.Size) {
+ if (rect.FillColor == color.Transparent || rect.FillColor == nil) && (rect.StrokeColor == color.Transparent || rect.FillColor == nil || rect.StrokeWidth == 0) {
+ return
+ }
p.drawTextureWithDetails(rect, p.newGlRectTexture, pos, rect.Size(), frame, canvas.ImageFillStretch,
1.0, painter.VectorPad(rect))
}
func (p *glPainter) drawText(text *canvas.Text, pos fyne.Position, frame fyne.Size) {
- if text.Text == "" {
+ if text.Text == "" || text.Text == " " {
return
}
diff --git a/internal/painter/gl/gl_common.go b/internal/painter/gl/gl_common.go
index 63981802f3..ecd54817cc 100644
--- a/internal/painter/gl/gl_common.go
+++ b/internal/painter/gl/gl_common.go
@@ -5,6 +5,7 @@ import (
"log"
"runtime"
+ "fyne.io/fyne/v2/theme"
"github.com/goki/freetype"
"github.com/goki/freetype/truetype"
@@ -63,6 +64,10 @@ func (p *glPainter) newGlStrokedRectTexture(obj fyne.CanvasObject) Texture {
func (p *glPainter) newGlTextTexture(obj fyne.CanvasObject) Texture {
text := obj.(*canvas.Text)
+ color := text.Color
+ if color == nil {
+ color = theme.ForegroundColor()
+ }
bounds := text.MinSize()
width := int(p.textureScale(bounds.Width))
@@ -77,7 +82,7 @@ func (p *glPainter) newGlTextTexture(obj fyne.CanvasObject) Texture {
d := painter.FontDrawer{}
d.Dst = img
- d.Src = &image.Uniform{C: text.Color}
+ d.Src = &image.Uniform{C: color}
d.Face = face
d.Dot = freetype.Pt(0, height-face.Metrics().Descent.Ceil())
d.DrawString(text.Text, text.TextStyle.TabWidth)
diff --git a/internal/painter/software/draw.go b/internal/painter/software/draw.go
index c22fa5b3ba..7a1130eaa3 100644
--- a/internal/painter/software/draw.go
+++ b/internal/painter/software/draw.go
@@ -9,6 +9,7 @@ import (
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/internal"
"fyne.io/fyne/v2/internal/painter"
+ "fyne.io/fyne/v2/theme"
"github.com/goki/freetype"
"github.com/goki/freetype/truetype"
@@ -138,6 +139,11 @@ func drawText(c fyne.Canvas, text *canvas.Text, pos fyne.Position, base *image.N
height := internal.ScaleInt(c, bounds.Height)
txtImg := image.NewRGBA(image.Rect(0, 0, width, height))
+ color := text.Color
+ if color == nil {
+ color = theme.ForegroundColor()
+ }
+
var opts truetype.Options
fontSize := text.TextSize * c.Scale()
opts.Size = float64(fontSize)
@@ -146,7 +152,7 @@ func drawText(c fyne.Canvas, text *canvas.Text, pos fyne.Position, base *image.N
d := painter.FontDrawer{}
d.Dst = txtImg
- d.Src = &image.Uniform{C: text.Color}
+ d.Src = &image.Uniform{C: color}
d.Face = face
d.Dot = freetype.Pt(0, height-face.Metrics().Descent.Ceil())
d.DrawString(text.Text, text.TextStyle.TabWidth)
diff --git a/internal/widget/scroller.go b/internal/widget/scroller.go
index 38249ce745..ecc377f850 100644
--- a/internal/widget/scroller.go
+++ b/internal/widget/scroller.go
@@ -390,7 +390,7 @@ func (s *Scroll) CreateRenderer() fyne.WidgetRenderer {
//ScrollToBottom will scroll content to container bottom - to show latest info which end user just added
func (s *Scroll) ScrollToBottom() {
- s.Offset.Y = s.Content.Size().Height - s.Size().Height
+ s.Offset.Y = s.Content.MinSize().Height - s.Size().Height
s.Refresh()
}
diff --git a/layout/formlayout.go b/layout/formlayout.go
index 7695dcf7b6..46c75b2c0d 100644
--- a/layout/formlayout.go
+++ b/layout/formlayout.go
@@ -2,6 +2,7 @@ package layout
import (
"fyne.io/fyne/v2"
+ "fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/theme"
)
@@ -53,6 +54,9 @@ func (f *formLayout) tableCellsSize(objects []fyne.CanvasObject, containerWidth
}
labelCell := currentRow[0].MinSize()
+ if _, ok := currentRow[0].(*canvas.Text); ok {
+ labelCell.Width += theme.Padding() * 4
+ }
labelCellMaxWidth = fyne.Max(labelCellMaxWidth, labelCell.Width)
contentCell := currentRow[1].MinSize()
@@ -92,8 +96,13 @@ func (f *formLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) {
}
tableRow := table[row]
- objects[i].Move(fyne.NewPos(0, y))
- objects[i].Resize(fyne.NewSize(tableRow[0].Width, tableRow[0].Height))
+ if _, ok := objects[i].(*canvas.Text); ok {
+ objects[i].Move(fyne.NewPos(theme.Padding()*2, y+theme.Padding()*2))
+ objects[i].Resize(fyne.NewSize(tableRow[0].Width-theme.Padding()*4, objects[i].MinSize().Height))
+ } else {
+ objects[i].Move(fyne.NewPos(0, y))
+ objects[i].Resize(fyne.NewSize(tableRow[0].Width, tableRow[0].Height))
+ }
if i+1 < len(objects) {
objects[i+1].Move(fyne.NewPos(theme.Padding()+tableRow[0].Width, y))
diff --git a/widget/form.go b/widget/form.go
index ff184788cc..77b2920cec 100644
--- a/widget/form.go
+++ b/widget/form.go
@@ -137,8 +137,12 @@ func (f *Form) createInput(item *FormItem) fyne.CanvasObject {
return fyne.NewContainerWithLayout(layout.NewVBoxLayout(), item.Widget, fyne.NewContainerWithoutLayout(text))
}
-func (f *Form) createLabel(text string) *Label {
- return NewLabelWithStyle(text, fyne.TextAlignTrailing, fyne.TextStyle{Bold: true})
+func (f *Form) createLabel(text string) *canvas.Text {
+ return &canvas.Text{Text: text,
+ Alignment: fyne.TextAlignTrailing,
+ Color: theme.ForegroundColor(),
+ TextSize: theme.TextSize(),
+ TextStyle: fyne.TextStyle{Bold: true}}
}
func (f *Form) updateButtons() {
@@ -234,12 +238,18 @@ func (f *Form) updateHelperText(item *FormItem) {
func (f *Form) updateLabels() {
for i, item := range f.Items {
- l := f.itemGrid.Objects[i*2].(*Label)
- if l.Text == item.Text {
- continue
+ l := f.itemGrid.Objects[i*2].(*canvas.Text)
+ l.TextSize = theme.TextSize()
+ if dis, ok := item.Widget.(fyne.Disableable); ok {
+ if dis.Disabled() {
+ l.Color = theme.DisabledColor()
+ } else {
+ l.Color = theme.ForegroundColor()
+ }
}
- l.SetText(item.Text)
+ l.Text = item.Text
+ l.Refresh()
f.updateHelperText(item)
}
}
@@ -263,8 +273,9 @@ func (f *Form) CreateRenderer() fyne.WidgetRenderer {
f.itemGrid = fyne.NewContainerWithLayout(layout.NewFormLayout(), objects...)
renderer := NewSimpleRenderer(fyne.NewContainerWithLayout(layout.NewVBoxLayout(), f.itemGrid, f.buttonBox))
- f.updateButtons() // will set correct visibility on the submit/cancel btns
- f.checkValidation(nil) // will trigger a validation check for correct initial validation status
+ f.updateButtons()
+ f.updateLabels()
+ f.checkValidation(nil) // will trigger a validation check for correct intial validation status
return renderer
}
diff --git a/widget/form_test.go b/widget/form_test.go
index d441865a62..c2012d4e77 100644
--- a/widget/form_test.go
+++ b/widget/form_test.go
@@ -5,6 +5,7 @@ import (
"testing"
"fyne.io/fyne/v2"
+ "fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/data/validation"
"fyne.io/fyne/v2/test"
"fyne.io/fyne/v2/theme"
@@ -104,11 +105,11 @@ func TestForm_ChangeText(t *testing.T) {
renderer := test.WidgetRenderer(form)
c := renderer.Objects()[0].(*fyne.Container).Objects[0].(*fyne.Container)
- assert.Equal(t, "Test", c.Objects[0].(*Label).Text)
+ assert.Equal(t, "Test", c.Objects[0].(*canvas.Text).Text)
item.Text = "Changed"
form.Refresh()
- assert.Equal(t, "Changed", c.Objects[0].(*Label).Text)
+ assert.Equal(t, "Changed", c.Objects[0].(*canvas.Text).Text)
}
func TestForm_ChangeTheme(t *testing.T) {
@@ -133,6 +134,23 @@ func TestForm_ChangeTheme(t *testing.T) {
})
}
+func TestForm_Disabled(t *testing.T) {
+ app := test.NewApp()
+ defer test.NewApp()
+ app.Settings().SetTheme(theme.LightTheme())
+
+ disabled := NewEntry()
+ disabled.Disable()
+ f := NewForm(
+ NewFormItem("Form Item 1", NewEntry()),
+ NewFormItem("Form Item 2", disabled))
+
+ w := test.NewWindow(f)
+ defer w.Close()
+
+ test.AssertImageMatches(t, "form/disabled.png", w.Canvas().Capture())
+}
+
func TestForm_Hints(t *testing.T) {
test.NewApp()
defer test.NewApp()
diff --git a/widget/testdata/form/disabled.png b/widget/testdata/form/disabled.png
new file mode 100644
index 0000000000..dd8dde2456
Binary files /dev/null and b/widget/testdata/form/disabled.png differ
diff --git a/widget/testdata/form/layout.xml b/widget/testdata/form/layout.xml
index da4116d911..1cb5f9e9ff 100644
--- a/widget/testdata/form/layout.xml
+++ b/widget/testdata/form/layout.xml
@@ -3,9 +3,7 @@
-
- test1
-
+ test1
@@ -20,9 +18,7 @@
-
- test2
-
+ test2