diff --git a/cmd/pad/main.go b/cmd/pad/main.go index bea9fd6..db1e1aa 100644 --- a/cmd/pad/main.go +++ b/cmd/pad/main.go @@ -40,8 +40,6 @@ func run(w *app.Window) error { // Track pixel dimensions from ConfigEvent (for resize handling) var pixelW, pixelH int - // Track previous scale to detect changes - scale := float32(1.0) // Create logic instance with initial DP size and scale=1.0 logic := editor.NewLogic(initialDPW, initialDPH) @@ -75,13 +73,9 @@ func run(w *app.Window) error { } case app.FrameEvent: gtx := app.NewContext(&ops, e) - newScale := gtx.Metric.PxPerDp - // If scale changed, send scale event to logic goroutine - if newScale != scale { - logic.ConfigChan() <- editor.ScaleEvent{Scale: newScale} - scale = newScale - } + // 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) @@ -93,6 +87,9 @@ func run(w *app.Window) error { Height: dpH, } + // Update renderer with current scale from State + renderer.SetScale(newScale) + // Acquire mutex, read frame, draw, release mutex mu.Lock() currentElems := elems diff --git a/internal/editor/logic.go b/internal/editor/logic.go index 2fe12bd..f334bf0 100644 --- a/internal/editor/logic.go +++ b/internal/editor/logic.go @@ -31,9 +31,7 @@ func (e ConfigEvent) apply(s *State) { } func (e ScaleEvent) apply(s *State) { - // Scale is used by the renderer, not stored in state. - // Layout is already computed in DP, so no recomputation needed. - _ = s.Elems // force use of state + s.SetScale(e.Scale) } // InputEvent represents a user input event routed to an element. @@ -98,6 +96,13 @@ func (l *Logic) ScreenSize() (ui.Dp, ui.Dp) { return l.state.ScreenWidth, l.state.ScreenHeight } +// Scale returns the current scale factor (pixels per DP). +func (l *Logic) Scale() float32 { + l.mu.Lock() + defer l.mu.Unlock() + return l.state.Scale +} + // Run runs the logic goroutine loop. func (l *Logic) Run() { for { @@ -109,8 +114,7 @@ func (l *Logic) Run() { e.apply(l.state) l.frameChan <- l.state.Elems case ScaleEvent: - // Scale is used by the renderer via gtx.Metric.PxPerDp. - // No state change needed — layout is already in DP. + e.apply(l.state) l.frameChan <- l.state.Elems } case <-l.inputChan: diff --git a/internal/editor/state.go b/internal/editor/state.go index ac68ada..e9783e0 100644 --- a/internal/editor/state.go +++ b/internal/editor/state.go @@ -9,19 +9,28 @@ import ( type State struct { ScreenWidth ui.Dp ScreenHeight ui.Dp + Scale float32 // pixels per DP, default 1.0 Elems []ui.Element } // NewState creates a new State with initial editor layout. // screenWidth and screenHeight are in device-independent pixels (Dp). +// scale defaults to 1.0 until updated by ScaleEvent. func NewState(screenWidth, screenHeight ui.Dp) *State { return &State{ ScreenWidth: screenWidth, ScreenHeight: screenHeight, + Scale: 1.0, Elems: EditorLayout(screenWidth, screenHeight), } } +// SetScale updates the scale factor and recomputes layout. +func (s *State) SetScale(scale float32) { + s.Scale = scale + s.Elems = EditorLayout(s.ScreenWidth, s.ScreenHeight) +} + // EditorLayout computes regions for the editor page. // It takes screen dimensions in Dp and returns []Element with regions in Dp. // This function works exclusively in Dp — no pixel conversions. diff --git a/internal/ui/render.go b/internal/ui/render.go index beaf971..cf34263 100644 --- a/internal/ui/render.go +++ b/internal/ui/render.go @@ -19,7 +19,7 @@ import ( type Renderer struct { theme Theme shp *text.Shaper - scale float32 // pixels per DP, from gtx.Metric.PxPerDp + scale float32 // pixels per DP, from State.Scale } // New creates a new Renderer. @@ -27,9 +27,9 @@ func New(th Theme, shp *text.Shaper) *Renderer { return &Renderer{theme: th, shp: shp, scale: 1.0} } -// setScale updates the renderer's scale factor from a layout.Context. -func (r *Renderer) setScale(gtx layout.Context) { - r.scale = gtx.Metric.PxPerDp +// SetScale updates the renderer's scale factor from State.Scale. +func (r *Renderer) SetScale(scale float32) { + r.scale = scale } // toPx converts Dp to physical pixels using the renderer's scale. @@ -45,10 +45,8 @@ 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. func (r *Renderer) Draw(gtx layout.Context, elems []Element) { - // Update scale from the frame context - r.setScale(gtx) - // Capture initial constraints once — before any clips modify them. // These are in physical pixels and serve as the window bounds for all positioning. initialConstraints := WindowConstraints{