Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App freezes when rapidly modifying content of container #4799

Open
2 tasks done
roffe opened this issue Apr 20, 2024 · 5 comments
Open
2 tasks done

App freezes when rapidly modifying content of container #4799

roffe opened this issue Apr 20, 2024 · 5 comments
Labels
unverified A bug that has been reported but not verified

Comments

@roffe
Copy link
Contributor

roffe commented Apr 20, 2024

Checklist

  • I have searched the issue tracker for open issues that relate to the same problem, before opening a new one.
  • This issue only relates to a single bug. I will open new issues for any other problems.

Describe the bug

I've for quiet some time been trying to chase down a application freeze when users are switching between preferences in my app it sometimes randomly just freezes.

I've managed to create a program that replicates the behaviour that makes the main window freeze and become unresponsive.

How to reproduce

Run attached program code. window should freeze within 3-4 seconds

Screenshots

image

Example code

package main

import (
	"encoding/json"
	"fmt"
	"image/color"
	"log"
	"math/rand/v2"
	"strconv"
	"sync"
	"time"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/canvas"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
	symbol "github.com/roffe/ecusymbol"
	"github.com/roffe/txlogger/pkg/layout"
)

var Map = make(map[string]string)

func init() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	Map["T5 Dash"] = `[{"Name":"Rpm","Number":86,"SramOffset":4194,"Address":0,"Length":2,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1},{"Name":"Medeltrot","Number":80,"SramOffset":4150,"Address":0,"Length":1,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1},{"Name":"Ign_angle","Number":168,"SramOffset":4228,"Address":0,"Length":2,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1},{"Name":"Lufttemp","Number":75,"SramOffset":4145,"Address":0,"Length":1,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1},{"Name":"P_medel","Number":320,"SramOffset":10751,"Address":0,"Length":1,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1},{"Name":"Max_tryck","Number":312,"SramOffset":10747,"Address":0,"Length":1,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1},{"Name":"Regl_tryck","Number":315,"SramOffset":10748,"Address":0,"Length":1,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":0.01},{"Name":"PWM_ut10","Number":318,"SramOffset":10754,"Address":0,"Length":1,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1},{"Name":"P_fak","Number":313,"SramOffset":11046,"Address":0,"Length":2,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1},{"Name":"I_fak","Number":314,"SramOffset":11044,"Address":0,"Length":2,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1},{"Name":"D_fak","Number":311,"SramOffset":11042,"Address":0,"Length":2,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1},{"Name":"AD_EGR","Number":9,"SramOffset":4118,"Address":0,"Length":1,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1},{"Name":"Kyl_temp","Number":72,"SramOffset":4141,"Address":0,"Length":1,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1},{"Name":"Bil_hast","Number":60,"SramOffset":4123,"Address":0,"Length":1,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1},{"Name":"Knock_offset1234","Number":131,"SramOffset":4236,"Address":0,"Length":2,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1},{"Name":"Batt_volt","Number":61,"SramOffset":4122,"Address":0,"Length":1,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1},{"Name":"Insptid_ms10","Number":64,"SramOffset":4190,"Address":0,"Length":2,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1},{"Name":"Lambdaint","Number":73,"SramOffset":4143,"Address":0,"Length":1,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1}]`
	Map["T7 Dash"] = `[{"Name":"ActualIn.n_Engine","Number":3462,"SramOffset":0,"Address":15788900,"Length":2,"Mask":0,"Type":33,"ExtendedType":0,"Correctionfactor":1},{"Name":"Out.X_AccPedal","Number":3672,"SramOffset":0,"Address":15789336,"Length":2,"Mask":0,"Type":33,"ExtendedType":0,"Correctionfactor":0.1,"Unit":"%"},{"Name":"In.v_Vehicle","Number":3409,"SramOffset":0,"Address":15788816,"Length":2,"Mask":0,"Type":33,"ExtendedType":0,"Correctionfactor":0.1,"Unit":"Km/h"},{"Name":"ActualIn.T_Engine","Number":3469,"SramOffset":0,"Address":15788916,"Length":2,"Mask":0,"Type":33,"ExtendedType":0,"Correctionfactor":1},{"Name":"ActualIn.T_AirInlet","Number":3470,"SramOffset":0,"Address":15788918,"Length":2,"Mask":0,"Type":33,"ExtendedType":0,"Correctionfactor":1},{"Name":"IgnProt.fi_Offset","Number":3045,"SramOffset":0,"Address":15787464,"Length":2,"Mask":0,"Type":33,"ExtendedType":0,"Correctionfactor":0.1,"Unit":"Degrees"},{"Name":"Out.fi_Ignition","Number":3686,"SramOffset":0,"Address":15789366,"Length":2,"Mask":0,"Type":33,"ExtendedType":0,"Correctionfactor":0.1,"Unit":"° BTDC"},{"Name":"Out.PWM_BoostCntrl","Number":3645,"SramOffset":0,"Address":15789300,"Length":2,"Mask":0,"Type":33,"ExtendedType":0,"Correctionfactor":0.1,"Unit":"%"},{"Name":"ActualIn.p_AirInlet","Number":3472,"SramOffset":0,"Address":15788922,"Length":2,"Mask":0,"Type":33,"ExtendedType":0,"Correctionfactor":0.001},{"Name":"In.p_AirBefThrottle","Number":3395,"SramOffset":0,"Address":15788788,"Length":2,"Mask":0,"Type":33,"ExtendedType":0,"Correctionfactor":0.001,"Unit":"Bar"},{"Name":"ECMStat.p_Diff","Number":3759,"SramOffset":0,"Address":15789466,"Length":2,"Mask":0,"Type":33,"ExtendedType":0,"Correctionfactor":0.001,"Unit":"Bar"},{"Name":"MAF.m_AirInlet","Number":452,"SramOffset":0,"Address":15775882,"Length":2,"Mask":0,"Type":32,"ExtendedType":0,"Correctionfactor":1,"Unit":"Mg/c"},{"Name":"m_Request","Number":59,"SramOffset":0,"Address":15775190,"Length":2,"Mask":0,"Type":0,"ExtendedType":0,"Correctionfactor":1,"Unit":"Mg/c"},{"Name":"ECMStat.ST_ActiveAirDem","Number":3754,"SramOffset":0,"Address":15789448,"Length":1,"Mask":0,"Type":36,"ExtendedType":0,"Correctionfactor":1},{"Name":"DisplProt.LambdaScanner","Number":3316,"SramOffset":0,"Address":15788686,"Length":2,"Mask":0,"Type":33,"ExtendedType":0,"Correctionfactor":0.01},{"Name":"Lambda.LambdaInt","Number":2606,"SramOffset":0,"Address":15787098,"Length":2,"Mask":0,"Type":33,"ExtendedType":0,"Correctionfactor":0.01}]`
	Map["T8 Dash"] = `[{"Name":"ActualIn.n_Engine","Number":4181,"SramOffset":0,"Address":1066236,"Length":2,"Mask":0,"Type":1,"ExtendedType":0,"Correctionfactor":1},{"Name":"Out.X_AccPos","Number":4772,"SramOffset":0,"Address":1066522,"Length":2,"Mask":0,"Type":1,"ExtendedType":0,"Correctionfactor":0.1},{"Name":"In.v_Vehicle","Number":4024,"SramOffset":0,"Address":1065980,"Length":2,"Mask":0,"Type":1,"ExtendedType":0,"Correctionfactor":0.1,"Unit":"Km/h"},{"Name":"ActualIn.T_Engine","Number":4155,"SramOffset":0,"Address":1066180,"Length":2,"Mask":0,"Type":1,"ExtendedType":0,"Correctionfactor":1},{"Name":"ActualIn.T_AirInlet","Number":4171,"SramOffset":0,"Address":1066214,"Length":2,"Mask":0,"Type":1,"ExtendedType":0,"Correctionfactor":1},{"Name":"IgnMastProt.fi_Offset","Number":3235,"SramOffset":0,"Address":1065164,"Length":2,"Mask":0,"Type":1,"ExtendedType":0,"Correctionfactor":0.1},{"Name":"Out.fi_Ignition","Number":4870,"SramOffset":0,"Address":1066662,"Length":2,"Mask":0,"Type":1,"ExtendedType":0,"Correctionfactor":0.1,"Unit":"° BTDC"},{"Name":"Out.PWM_BoostCntrl","Number":4843,"SramOffset":0,"Address":1066622,"Length":2,"Mask":0,"Type":1,"ExtendedType":0,"Correctionfactor":0.1,"Unit":"%"},{"Name":"In.p_AirInlet","Number":4004,"SramOffset":0,"Address":1065938,"Length":2,"Mask":0,"Type":1,"ExtendedType":0,"Correctionfactor":0.001},{"Name":"ActualIn.p_AirBefThrottle","Number":4159,"SramOffset":0,"Address":1066188,"Length":2,"Mask":0,"Type":1,"ExtendedType":0,"Correctionfactor":0.001},{"Name":"MAF.m_AirInlet","Number":383,"SramOffset":0,"Address":1056888,"Length":2,"Mask":0,"Type":1,"ExtendedType":0,"Correctionfactor":1,"Unit":"Mg/c"},{"Name":"AirMassMast.m_Request","Number":260,"SramOffset":0,"Address":1056830,"Length":2,"Mask":0,"Type":1,"ExtendedType":0,"Correctionfactor":1},{"Name":"ECMStat.ST_ActiveAirDem","Number":4977,"SramOffset":0,"Address":1067070,"Length":1,"Mask":0,"Type":4,"ExtendedType":0,"Correctionfactor":1},{"Name":"Lambda.LambdaInt","Number":2729,"SramOffset":0,"Address":1064772,"Length":2,"Mask":0,"Type":1,"ExtendedType":0,"Correctionfactor":0.01}]`
}

func main() {
	a := app.NewWithID("com.roffe.freezebug")
	w := a.NewWindow("freezebug")

	updatefunc := func(s []*symbol.Symbol) {
	}
	lst := NewSymbolListWidget(w, updatefunc)
	cnt := container.NewStack(lst)
	w.SetContent(cnt)
	w.Resize(fyne.NewSize(800, 600))

	go func() {
		for {
			time.Sleep(300 * time.Millisecond)
			syms := loadpref()
			lst.LoadSymbols(syms...)
			cnt.Refresh()
		}
	}()

	w.ShowAndRun()
}

func loadpref() []*symbol.Symbol {
	var data string
	switch rand.IntN(3) {
	case 0:
		log.Println("T5 Dash")
		data = Map["T5 Dash"]
	case 1:
		log.Println("T7 Dash")
		data = Map["T7 Dash"]
	case 2:
		log.Println("T8 Dash")
		data = Map["T8 Dash"]
	default:
		log.Println("oops")
	}
	var symbols []*symbol.Symbol
	if err := json.Unmarshal([]byte(data), &symbols); err != nil {
		panic(err)
	}
	return symbols
}

type SymbolListWidget struct {
	widget.BaseWidget
	symbols    []*symbol.Symbol
	entryMap   map[string]*SymbolWidgetEntry
	entries    []*SymbolWidgetEntry
	container  *fyne.Container
	border     *fyne.Container
	scroll     *container.Scroll
	mu         sync.Mutex
	updateBars bool
	onUpdate   func([]*symbol.Symbol)
	w          fyne.Window
}

func NewSymbolListWidget(w fyne.Window, updateFunc func([]*symbol.Symbol), symbols ...*symbol.Symbol) *SymbolListWidget {
	sl := &SymbolListWidget{
		entryMap: make(map[string]*SymbolWidgetEntry),
		onUpdate: updateFunc,
		w:        w,
	}
	sl.ExtendBaseWidget(sl)
	sl.render()
	sl.LoadSymbols(symbols...)
	return sl
}

func (s *SymbolListWidget) render() {
	s.container = container.NewVBox()
	s.scroll = container.NewVScroll(s.container)

	name := widget.NewLabel("Name")
	name.TextStyle = fyne.TextStyle{Bold: true}

	value := widget.NewLabel("Value")
	value.TextStyle = fyne.TextStyle{Bold: true}

	num := widget.NewLabel("#")
	num.TextStyle = fyne.TextStyle{Bold: true}

	typ := widget.NewLabel("Type")
	typ.TextStyle = fyne.TextStyle{Bold: true}

	factor := widget.NewLabel("Factor")
	factor.TextStyle = fyne.TextStyle{Bold: true}

	s.border = container.NewBorder(
		container.New(&layout.RatioContainer{Widths: sz},
			widget.NewLabel(""),
			name,
			value,
			num,
			typ,
			factor,
		),
		nil,
		nil,
		nil,
		s.scroll,
	)
}

func (s *SymbolListWidget) UpdateBars(enabled bool) {
	s.updateBars = enabled
}

func (s *SymbolListWidget) SetValue(name string, value float64) {
	val, found := s.entryMap[name]
	if found {
		val.value = value
		if value < val.min {
			val.min = value
		} else if value > val.max {
			val.max = value
		}
		if s.updateBars {
			factor := float32((value - val.min) / (val.max - val.min))
			col := GetColorInterpolation(val.min, val.max, value)
			col.A = 30
			val.valueBar.FillColor = col
			val.valueBar.Resize(fyne.NewSize(factor*100, 26))
		}
		switch val.symbol.Correctionfactor {
		case 1:
			val.symbolValue.SetText(strconv.FormatFloat(value, 'f', 0, 64))
			return
		case 0.1:
			val.symbolValue.SetText(strconv.FormatFloat(value, 'f', 1, 64))
			return
		case 0.01:
			val.symbolValue.SetText(strconv.FormatFloat(value, 'f', 2, 64))
			return
		case 0.001:
			val.symbolValue.SetText(strconv.FormatFloat(value, 'f', 3, 64))
			return
		default:
			val.symbolValue.SetText(strconv.FormatFloat(value, 'f', 2, 64))
			return
		}
	}
}

func (s *SymbolListWidget) Disable() {
	for _, e := range s.entries {
		e.symbolCorrectionfactor.Disable()
		e.deleteBTN.Disable()
	}
}

func (s *SymbolListWidget) Enable() {
	for _, e := range s.entries {
		e.symbolCorrectionfactor.Enable()
		e.deleteBTN.Enable()
	}
}

func (s *SymbolListWidget) Add(symbols ...*symbol.Symbol) {
	s.mu.Lock()
	defer s.mu.Unlock()
	for _, sym := range symbols {
		if _, found := s.entryMap[sym.Name]; found {
			continue
		}
		deleteFunc := func(sw *SymbolWidgetEntry) {
			for i, e := range s.entries {
				if e == sw {
					s.mu.Lock()
					defer s.mu.Unlock()
					s.symbols = append(s.symbols[:i], s.symbols[i+1:]...)
					s.entries = append(s.entries[:i], s.entries[i+1:]...)
					delete(s.entryMap, sw.symbol.Name)
					s.container.Remove(sw)
					s.scroll.Refresh()
					s.onUpdate(s.symbols)
					break
				}
			}
		}
		entry := NewSymbolWidgetEntry(s.w, sym, deleteFunc)
		s.symbols = append(s.symbols, sym)
		s.entries = append(s.entries, entry)
		s.container.Objects = append(s.container.Objects, entry)
		s.entryMap[sym.Name] = entry
	}
	s.onUpdate(s.symbols)
}

func (s *SymbolListWidget) Clear() {
	for _, e := range s.entries {
		e.symbolValue.SetText("---")
	}
}

func (s *SymbolListWidget) clear() {
	s.mu.Lock()
	defer s.mu.Unlock()
	//s.container.Refresh()
	//s.border.Refresh()
	s.container.RemoveAll()
	s.symbols = []*symbol.Symbol{}
	s.entries = []*SymbolWidgetEntry{}
	s.entryMap = make(map[string]*SymbolWidgetEntry)
	s.onUpdate(s.symbols)
}

func (s *SymbolListWidget) LoadSymbols(symbols ...*symbol.Symbol) {
	s.clear()
	s.Add(symbols...)
}

func (s *SymbolListWidget) Count() int {
	s.mu.Lock()
	defer s.mu.Unlock()
	return len(s.symbols)
}

func (s *SymbolListWidget) Symbols() []*symbol.Symbol {
	s.mu.Lock()
	defer s.mu.Unlock()
	out := make([]*symbol.Symbol, len(s.symbols))
	copy(out, s.symbols)
	return out
}

func (s *SymbolListWidget) CreateRenderer() fyne.WidgetRenderer {
	swr := &SymbolListWidgetRenderer{
		sl: s,
	}
	return swr
}

type SymbolListWidgetRenderer struct {
	sl *SymbolListWidget
}

func (sr *SymbolListWidgetRenderer) Layout(size fyne.Size) {
	sr.sl.border.Resize(size)
}

func (sr *SymbolListWidgetRenderer) MinSize() fyne.Size {
	var width float32
	var height float32
	for _, en := range sr.sl.entries {
		sz := en.MinSize()
		if sz.Width > width {
			width = sz.Width
		}
		height += sz.Height
	}
	return fyne.NewSize(width, min(height, 200))
}

func (sr *SymbolListWidgetRenderer) Refresh() {
	for _, e := range sr.sl.entries {
		e.Refresh()
	}
}

func (sr *SymbolListWidgetRenderer) Destroy() {
}

func (sr *SymbolListWidgetRenderer) Objects() []fyne.CanvasObject {
	return []fyne.CanvasObject{sr.sl.border}
}

type SymbolWidgetEntry struct {
	widget.BaseWidget
	symbol                 *symbol.Symbol
	copyName               *widget.Button
	symbolName             *widget.Label
	symbolValue            *widget.Label
	symbolNumber           *widget.Label
	symbolType             *widget.Label
	symbolCorrectionfactor *widget.Entry
	deleteBTN              *widget.Button
	valueBar               *canvas.Rectangle

	container *fyne.Container

	deleteFunc func(*SymbolWidgetEntry)

	//valueSet bool
	value    float64
	min, max float64
}

func NewSymbolWidgetEntry(w fyne.Window, sym *symbol.Symbol, deleteFunc func(*SymbolWidgetEntry)) *SymbolWidgetEntry {
	sw := &SymbolWidgetEntry{
		symbol:     sym,
		deleteFunc: deleteFunc,
	}
	sw.ExtendBaseWidget(sw)
	sw.copyName = widget.NewButtonWithIcon("", theme.ContentCopyIcon(), func() {
		w.Clipboard().SetContent(sym.Name)
	})
	sw.symbolName = widget.NewLabel(sw.symbol.Name)
	sw.symbolValue = widget.NewLabel("---")
	sw.symbolNumber = widget.NewLabel(strconv.Itoa(sw.symbol.Number))
	sw.symbolType = widget.NewLabel(fmt.Sprintf("%02X", sw.symbol.Type))
	sw.symbolCorrectionfactor = widget.NewEntry()

	sw.symbolCorrectionfactor.OnChanged = func(s string) {
		f, err := strconv.ParseFloat(s, 64)
		if err != nil {
			return
		}
		sw.symbol.Correctionfactor = f
	}

	sw.SetCorrectionFactor(sym.Correctionfactor)

	sw.deleteBTN = widget.NewButtonWithIcon("", theme.DeleteIcon(), func() {
		if sw.deleteFunc != nil {
			sw.deleteFunc(sw)
		}
	})

	sw.valueBar = canvas.NewRectangle(color.RGBA{255, 0, 0, 255})

	sw.container = container.NewWithoutLayout(
		sw.copyName,
		sw.valueBar,
		sw.symbolName,
		sw.symbolValue,
		sw.symbolNumber,
		sw.symbolType,
		sw.symbolCorrectionfactor,
		sw.deleteBTN,
	)
	return sw
}

func (sw *SymbolWidgetEntry) SetCorrectionFactor(f float64) {
	sw.symbol.Correctionfactor = f
	switch f {
	case 1:
		sw.symbolCorrectionfactor.SetText(strconv.FormatFloat(f, 'f', 0, 64))
	case 0.1:
		sw.symbolCorrectionfactor.SetText(strconv.FormatFloat(f, 'f', 1, 64))
	case 0.01:
		sw.symbolCorrectionfactor.SetText(strconv.FormatFloat(f, 'f', 2, 64))
	case 0.001:
		sw.symbolCorrectionfactor.SetText(strconv.FormatFloat(f, 'f', 3, 64))
	default:
		sw.symbolCorrectionfactor.SetText(strconv.FormatFloat(f, 'f', 4, 64))
	}
}

func (sw *SymbolWidgetEntry) CreateRenderer() fyne.WidgetRenderer {
	swr := &SymbolWidgetEntryRenderer{
		sw: sw,
	}
	return swr
}

type SymbolWidgetEntryRenderer struct {
	sw *SymbolWidgetEntry
}

var sz = []float32{
	.04, // copy
	.32, // name
	.18, // value
	.12, // number
	.14, // correctionfactor
	.08, // type
	.04, // deletebtn
}

func sumFloat32(a []float32) float32 {
	var sum float32
	for _, v := range a {
		sum += v
	}
	return sum
}

func (sr *SymbolWidgetEntryRenderer) Layout(size fyne.Size) {
	sw := sr.sw
	padd := size.Width * ((1.0 - sumFloat32(sz)) / float32(len(sz)))
	sw.copyName.Resize(fyne.NewSize(size.Width*sz[0], size.Height))
	sw.symbolName.Resize(fyne.NewSize(size.Width*sz[1], size.Height))
	sw.symbolValue.Resize(fyne.NewSize(size.Width*sz[2], size.Height))
	sw.symbolNumber.Resize(fyne.NewSize(size.Width*sz[3], size.Height))
	sw.symbolType.Resize(fyne.NewSize(size.Width*sz[4], size.Height))
	sw.symbolCorrectionfactor.Resize(fyne.NewSize(size.Width*sz[5], size.Height))
	sw.deleteBTN.Resize(fyne.NewSize(size.Width*sz[6], size.Height))

	var x float32

	sw.copyName.Move(fyne.NewPos(x, 0))
	x += sw.copyName.Size().Width + padd

	sw.symbolName.Move(fyne.NewPos(x, 0))
	x += sw.symbolName.Size().Width + padd

	sw.symbolValue.Move(fyne.NewPos(x, 0))
	sw.valueBar.Move(fyne.NewPos(x, 6))
	x += sw.symbolValue.Size().Width + padd

	sw.symbolNumber.Move(fyne.NewPos(x, 0))
	x += sw.symbolNumber.Size().Width + padd

	sw.symbolType.Move(fyne.NewPos(x, 0))
	x += sw.symbolType.Size().Width + padd

	sw.symbolCorrectionfactor.Move(fyne.NewPos(x, 0))
	x += sw.symbolCorrectionfactor.Size().Width + padd

	sw.deleteBTN.Move(fyne.NewPos(x, 0))
}

func (sr *SymbolWidgetEntryRenderer) MinSize() fyne.Size {
	sw := sr.sw
	var width float32
	var height float32 = sw.symbolName.MinSize().Height
	width += sw.copyName.MinSize().Width
	width += sw.symbolName.MinSize().Width
	width += sw.symbolValue.MinSize().Width
	width += sw.symbolNumber.MinSize().Width
	width += sw.symbolCorrectionfactor.MinSize().Width
	width += sw.deleteBTN.MinSize().Width
	return fyne.NewSize(width, height)
}

func (sr *SymbolWidgetEntryRenderer) Refresh() {
	sr.sw.symbolName.Refresh()
	sr.sw.symbolValue.Refresh()
	sr.sw.symbolNumber.Refresh()
	sr.sw.symbolType.Refresh()
	sr.sw.symbolCorrectionfactor.Refresh()
}

func (sr *SymbolWidgetEntryRenderer) Destroy() {
}

func (sr *SymbolWidgetEntryRenderer) Objects() []fyne.CanvasObject {
	return []fyne.CanvasObject{sr.sw.container}
}

func GetColorInterpolation(min, max, value float64) color.RGBA {
	//log.Println("getColorInterpolation", min, max, value)
	// Normalize the value to a 0-1 range
	t := (value - min) / (max - min)
	divider := .5
	var r, g, b float64
	if t < divider { // Green to Yellow interpolation
		r = lerp(0, 1, t/divider)
		g = 1
	} else { // Yellow to Red interpolation
		r = 1
		g = lerp(1, 0, (t-divider)/(1-divider))
	}
	b = 0
	// Convert from 0-1 range to 0-255 for color.RGBA
	return color.RGBA{
		R: uint8(r * 255),
		G: uint8(g * 255),
		B: uint8(b * 255),
		A: 255,
	}
}

func lerp(a, b, t float64) float64 {
	return a + (b-a)*t
}

Fyne version

fyne.io/fyne/v2 v2.4.6-0.20240418153625-66b892df8f5e

Go compiler version

go version go1.22.0 windows/amd64

Operating system and version

Windows 11

Additional Information

No response

@roffe roffe added the unverified A bug that has been reported but not verified label Apr 20, 2024
@andydotxyz
Copy link
Member

Can you replicate this without your custom widget code?

@roffe
Copy link
Contributor Author

roffe commented Apr 21, 2024

i've managed to make some more discoveries., i added a bunch of debug output and it's never inside the widget code it freezes.

however! it seems related to mouse somehow... if i start the test program and don't put the mouse pointer inside the window it will run forever. but as soon as i select the window and move the mouse inside it then it freezes within a few seconds

@roffe
Copy link
Contributor Author

roffe commented Apr 21, 2024

also the go thread to swap content keeps running the whole time so the go runtime is not dead when this happends

@andydotxyz
Copy link
Member

also the go thread to swap content keeps running the whole time so the go runtime is not dead when this happends

That makes sense - it would be a guess that the event handling thread is deadlocked.
I'm not sure where the debug was added, but the best step forward is to check which goroutines are hung and on what lock (either it's a deadlock or some other kind of wait I think...)

@Jacalz
Copy link
Member

Jacalz commented Apr 22, 2024

Send a SIGQUIT to the app and you should get a nice clean stack trace

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
unverified A bug that has been reported but not verified
Projects
None yet
Development

No branches or pull requests

3 participants