fyne
fyne copied to clipboard
Widgets extend beyond the dialog window
Checklist
- [X] I have searched the issue tracker for open issues that relate to the same problem, before opening a new one.
- [X] This issue only relates to a single bug. I will open new issues for any other problems.
Describe the bug
Malfunction of dialog.NewCustomConfirm. For some reason, the widgets extend beyond the dialog window
but once you change the size of the parent window, they begin to display correctly
How to reproduce
Run example code and press Free crop button. Sometime it works correctly and in such case close dialog and open it ones or twice more.
Screenshots
See bug description
Example code
Example code consists of:
- main.go
package main
import (
"image"
"image/color"
"log"
"os"
_ "image/jpeg"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
const downscaleFactor float32 = 0.6
var (
ScreenWidth = 3840
ScreenHeight = 2160
)
func main() {
reader, err := os.Open("test.jpg")
if err != nil {
log.Fatal(err)
}
defer reader.Close()
m, _, err := image.Decode(reader)
if err != nil {
log.Fatal(err)
}
a := app.New()
w := a.NewWindow("Photo")
img := canvas.NewImageFromImage(m)
top := container.NewHBox(widget.NewButton("Free crop", func() { doCropDialog(m, w) }))
content := container.NewBorder(top, nil, nil, nil, img)
w.SetMaster()
w.SetContent(content)
w.CenterOnScreen()
w.Resize(fyne.NewSize(float32(ScreenWidth)*downscaleFactor, float32(ScreenHeight)*downscaleFactor))
w.Show()
a.Run()
}
func doCropDialog(m image.Image, w fyne.Window) {
scale := fyne.CurrentApp().Settings().Scale()
img := canvas.NewImageFromImage(m)
img.FillMode = canvas.ImageFillOriginal
img.ScaleMode = canvas.ImageScaleFastest
border := canvas.NewRectangle(color.Transparent)
border.StrokeColor = theme.PrimaryColor()
border.StrokeWidth = 5
center := container.NewWithoutLayout(img, border)
imgSize := fyne.NewSize(float32(img.Image.Bounds().Dx())/scale, float32(img.Image.Bounds().Dy())/scale)
img.Move(fyne.NewPos(0, 0))
img.Resize(imgSize)
var content fyne.CanvasObject
var posTL, posBR fyne.Position
posTL = fyne.NewPos(0, 0)
posBR = fyne.NewPos(imgSize.Width, imgSize.Height)
border.Move(posTL)
border.Resize(fyne.NewSize(posBR.X-posTL.X, posBR.Y-posTL.Y))
topEdge := widget.NewSlider(0, float64(imgSize.Height))
topEdge.Orientation = widget.Vertical
topEdge.Value = float64(imgSize.Height)
leftEdge := widget.NewSlider(0, float64(imgSize.Width))
leftEdge.Orientation = widget.Horizontal
leftEdge.Value = 0
bottomEdge := widget.NewSlider(0, float64(imgSize.Height))
bottomEdge.Orientation = widget.Vertical
bottomEdge.Value = 0
rightEdge := widget.NewSlider(0, float64(imgSize.Width))
rightEdge.Orientation = widget.Horizontal
rightEdge.Value = float64(imgSize.Width)
topEdge.OnChanged = func(v float64) {
if v <= bottomEdge.Value {
return
}
posTL.Y = imgSize.Height - float32(v)
border.Move(posTL)
border.Resize(fyne.NewSize(posBR.X-posTL.X, posBR.Y-posTL.Y))
}
leftEdge.OnChanged = func(v float64) {
if v >= rightEdge.Value {
return
}
posTL.X = float32(v)
border.Move(posTL)
border.Resize(fyne.NewSize(posBR.X-posTL.X, posBR.Y-posTL.Y))
}
bottomEdge.OnChanged = func(v float64) {
if v >= topEdge.Value {
return
}
posBR.Y = imgSize.Height - float32(v)
border.Move(posTL)
border.Resize(fyne.NewSize(posBR.X-posTL.X, posBR.Y-posTL.Y))
}
rightEdge.OnChanged = func(v float64) {
if v <= leftEdge.Value {
return
}
posBR.X = float32(v)
border.Move(posTL)
border.Resize(fyne.NewSize(posBR.X-posTL.X, posBR.Y-posTL.Y))
}
content = container.NewBorder(rightEdge, leftEdge, bottomEdge, topEdge, center)
dlg := dialog.NewCustomConfirm("Crop image", "Crop", "Cancel", content, func(b bool) {}, w)
// jolt(w) // Uncomment this line to see workaround for right functioning
dlg.Show()
}
func jolt(w fyne.Window) {
s := w.Content().Size().AddWidthHeight(1., 1.)
w.Resize(s)
s = w.Content().Size().AddWidthHeight(-1., -1.)
w.Resize(s)
}
- test.jpg
Fyne version
v2.4.1, v2.4.3
Go compiler version
go version go1.21.4 linux/arm64
Operating system and version
Ubuntu Release 22.04.3 LTS 64-bit Kernel Linux 4.9.337-35 aarch64 MATE 1.26.0
Additional Information
No response
Significantly simplified the test example. It doesn't need a test sample. But this error occurs much less frequently with it. You have to start the dialog multiple times.
package main
import (
"image/color"
_ "image/jpeg"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
func main() {
a := app.New()
w := a.NewWindow("Photo")
top := container.NewHBox(widget.NewButton("Crop", func() { doCropDialog(w) }))
content := container.NewBorder(top, nil, nil, nil)
w.SetMaster()
w.SetContent(content)
w.CenterOnScreen()
w.Resize(fyne.NewSize(1280, 720))
w.Show()
a.Run()
}
func doCropDialog(w fyne.Window) {
imgWidth := float32(200)
imgHeight := float32(400)
border := canvas.NewRectangle(color.Transparent)
border.StrokeColor = theme.PrimaryColor()
border.StrokeWidth = 5
var posTL, posBR fyne.Position
posTL = fyne.NewPos(0, 0)
posBR = fyne.NewPos(imgWidth, imgHeight)
border.Move(posTL)
border.Resize(fyne.NewSize(posBR.X-posTL.X, posBR.Y-posTL.Y))
rect := canvas.NewRectangle(color.White)
rect.SetMinSize(fyne.NewSize(imgWidth, imgHeight))
leftEdge := widget.NewSlider(0, float64(imgWidth))
leftEdge.Orientation = widget.Horizontal
leftEdge.Value = 0
leftEdge.OnChanged = func(v float64) {
posTL.X = float32(v)
border.Move(posTL)
border.Resize(fyne.NewSize(posBR.X-posTL.X, posBR.Y-posTL.Y))
}
center := container.NewStack(rect, border)
content := container.NewBorder(nil, leftEdge, nil, nil, center)
dlg := dialog.NewCustomConfirm("Crop image", "Crop", "Cancel", content, func(b bool) {}, w)
dlg.Show()
}
Malfunction screenshot
Right functioning
I don’t even know if it’s worth releasing a new app feature with such a bug :(
Thanks for the simplified test. Somehow this crept into v2.4.0 on an existing feature and we have been trying to track it down.
Maybe the dialogue just doesn't have enough time to render. If after dialog.Show() you put some code with a delay,
s := w.Content().Size()
w.Resize(s.AddWidthHeight(-1, -1))
time.Sleep(10 * time.Millisecond)
w.Resize(s)
then dialog rerenders normally
This does not depend on the OS and hardware. I have the same bug on my Intel NUC with Windows 10
Maybe the dialogue just doesn't have enough time to render.
If after dialog.Show() you put some code with a delay,
s := w.Content().Size() w.Resize(s.AddWidthHeight(-1, -1)) time.Sleep(10 * time.Millisecond) w.Resize(s)
then dialog rerenders normally
Adding workarounds like this should never be needed. The render loop only paints what is ready, this seems more likely an order issue potentially race related whereby the dialog thinks it is laid out when render happens.
Maybe this issue is induced by high CPU load. Screenshot for above code load
It is like #4574
@andydotxyz is there progress on this issue ? it's a blocking item for our app. can I at least get some reason / workarounds. @vinser adding delay after dialog.Show() doesn't seem to work for me.
Suffering the same issues on my app as well.
//create album button
titleEntry := widget.NewEntry()
titleEntry.PlaceHolder = "Enter title:"
createButton := cwidget.NewButtonWithIcon("", theme.FolderNewIcon(), func() {
dialog.ShowCustomConfirm(
resource.KAlbumTitleDialogTitle,
resource.KAlbumTitleConfirmTitle,
resource.KAlbumTitleDismissTitle,
titleEntry,
func(confirm bool) {
if confirm {
v.client.AddAlbum(titleEntry.Text)
}
},
getWindow(),
)
})
It is very very random. Sometimes the text entry is missing, sometimes the confirm button doesn't light up.