Fix event flow: ConfigEvent sets size, FrameEvent sets scale only

- ConfigEvent: ONLY from app.ConfigEvent, sets width/height in State
- FrameEvent: gets scale from gtx.Metric.PxPerDp, sends ScaleEvent only if changed
- ScaleEvent: updates State.Scale
- Renderer reads windowW/windowH from State (SetWindowSize), not gtx.Constraints
- Renderer reads scale from State (SetScale), not gtx.Metric.PxPerDp
- main.go: ConfigEvent calls SetWindowSize, FrameEvent calls SetScale
This commit is contained in:
Greg Pomerantz 2026-05-09 20:26:10 -04:00
parent 68437c32a7
commit 21b4df59a3
2 changed files with 24 additions and 19 deletions

View File

@ -40,6 +40,8 @@ func run(w *app.Window) error {
// Track pixel dimensions from ConfigEvent (for resize handling)
var pixelW, pixelH int
// Track previous scale to detect changes
prevScale := float32(1.0)
// Create logic instance with initial DP size and scale=1.0
logic := editor.NewLogic(initialDPW, initialDPH)
@ -71,24 +73,20 @@ func run(w *app.Window) error {
Width: dpW,
Height: dpH,
}
// Update renderer with window size from State
renderer.SetWindowSize(pixelW, pixelH)
case app.FrameEvent:
gtx := app.NewContext(&ops, e)
newScale := gtx.Metric.PxPerDp
// Get scale from State — this is updated by ScaleEvent
newScale := logic.Scale()
// Convert pixel dimensions to DP using actual scale
dpW := ui.ToDp(ui.Px(pixelW), newScale)
dpH := ui.ToDp(ui.Px(pixelH), newScale)
// Send updated config to logic goroutine
logic.ConfigChan() <- editor.ConfigEvent{
Width: dpW,
Height: dpH,
// If scale changed, send scale event to logic goroutine
if newScale != prevScale {
logic.ConfigChan() <- editor.ScaleEvent{Scale: newScale}
prevScale = newScale
}
// Update renderer with current scale from State
renderer.SetScale(newScale)
renderer.SetScale(logic.Scale())
// Acquire mutex, read frame, draw, release mutex
mu.Lock()

View File

@ -20,6 +20,9 @@ type Renderer struct {
theme Theme
shp *text.Shaper
scale float32 // pixels per DP, from State.Scale
// Window dimensions in physical pixels, from State
windowW int
windowH int
}
// New creates a new Renderer.
@ -32,6 +35,12 @@ func (r *Renderer) SetScale(scale float32) {
r.scale = scale
}
// SetWindowSize updates the window dimensions in physical pixels from State.
func (r *Renderer) SetWindowSize(w, h int) {
r.windowW = w
r.windowH = h
}
// toPx converts Dp to physical pixels using the renderer's scale.
func (r *Renderer) toPx(dp Dp) Px {
return ToPx(dp, r.scale)
@ -43,15 +52,13 @@ func (r *Renderer) toDp(px Px) Dp {
}
// Draw iterates elements and draws each in slice order (back-to-front).
// It captures the initial constraints once at the start, then passes them
// to each render function for consistent positioning.
// The scale is set externally via SetScale() from State.Scale.
// Window constraints come from State (set via SetWindowSize), not from gtx.
// Scale comes from State (set via SetScale), not from gtx.Metric.PxPerDp.
func (r *Renderer) Draw(gtx layout.Context, elems []Element) {
// Capture initial constraints once — before any clips modify them.
// These are in physical pixels and serve as the window bounds for all positioning.
// Use window dimensions from State — never from gtx.Constraints
initialConstraints := WindowConstraints{
Min: gtx.Constraints.Min,
Max: gtx.Constraints.Max,
Min: image.Point{X: 0, Y: 0},
Max: image.Point{X: r.windowW, Y: r.windowH},
}
for _, e := range elems {