Store scale in State, use it everywhere instead of gtx.Metric.PxPerDp

- State.Scale stores the scale factor (default 1.0)
- ScaleEvent applies SetScale() which updates State.Scale
- Logic.Scale() accessor returns current scale
- Renderer.SetScale() updates renderer's scale from State
- main.go: uses logic.Scale() to get scale, calls renderer.SetScale()
- No more references to gtx.Metric.PxPerDp in render.go or main.go
This commit is contained in:
Greg Pomerantz 2026-05-09 20:23:02 -04:00
parent ef31701d87
commit 68437c32a7
4 changed files with 28 additions and 20 deletions

View File

@ -40,8 +40,6 @@ func run(w *app.Window) error {
// Track pixel dimensions from ConfigEvent (for resize handling) // Track pixel dimensions from ConfigEvent (for resize handling)
var pixelW, pixelH int 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 // Create logic instance with initial DP size and scale=1.0
logic := editor.NewLogic(initialDPW, initialDPH) logic := editor.NewLogic(initialDPW, initialDPH)
@ -75,13 +73,9 @@ func run(w *app.Window) error {
} }
case app.FrameEvent: case app.FrameEvent:
gtx := app.NewContext(&ops, e) gtx := app.NewContext(&ops, e)
newScale := gtx.Metric.PxPerDp
// If scale changed, send scale event to logic goroutine // Get scale from State — this is updated by ScaleEvent
if newScale != scale { newScale := logic.Scale()
logic.ConfigChan() <- editor.ScaleEvent{Scale: newScale}
scale = newScale
}
// Convert pixel dimensions to DP using actual scale // Convert pixel dimensions to DP using actual scale
dpW := ui.ToDp(ui.Px(pixelW), newScale) dpW := ui.ToDp(ui.Px(pixelW), newScale)
@ -93,6 +87,9 @@ func run(w *app.Window) error {
Height: dpH, Height: dpH,
} }
// Update renderer with current scale from State
renderer.SetScale(newScale)
// Acquire mutex, read frame, draw, release mutex // Acquire mutex, read frame, draw, release mutex
mu.Lock() mu.Lock()
currentElems := elems currentElems := elems

View File

@ -31,9 +31,7 @@ func (e ConfigEvent) apply(s *State) {
} }
func (e ScaleEvent) apply(s *State) { func (e ScaleEvent) apply(s *State) {
// Scale is used by the renderer, not stored in state. s.SetScale(e.Scale)
// Layout is already computed in DP, so no recomputation needed.
_ = s.Elems // force use of state
} }
// InputEvent represents a user input event routed to an element. // 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 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. // Run runs the logic goroutine loop.
func (l *Logic) Run() { func (l *Logic) Run() {
for { for {
@ -109,8 +114,7 @@ func (l *Logic) Run() {
e.apply(l.state) e.apply(l.state)
l.frameChan <- l.state.Elems l.frameChan <- l.state.Elems
case ScaleEvent: case ScaleEvent:
// Scale is used by the renderer via gtx.Metric.PxPerDp. e.apply(l.state)
// No state change needed — layout is already in DP.
l.frameChan <- l.state.Elems l.frameChan <- l.state.Elems
} }
case <-l.inputChan: case <-l.inputChan:

View File

@ -9,19 +9,28 @@ import (
type State struct { type State struct {
ScreenWidth ui.Dp ScreenWidth ui.Dp
ScreenHeight ui.Dp ScreenHeight ui.Dp
Scale float32 // pixels per DP, default 1.0
Elems []ui.Element Elems []ui.Element
} }
// NewState creates a new State with initial editor layout. // NewState creates a new State with initial editor layout.
// screenWidth and screenHeight are in device-independent pixels (Dp). // 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 { func NewState(screenWidth, screenHeight ui.Dp) *State {
return &State{ return &State{
ScreenWidth: screenWidth, ScreenWidth: screenWidth,
ScreenHeight: screenHeight, ScreenHeight: screenHeight,
Scale: 1.0,
Elems: EditorLayout(screenWidth, screenHeight), 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. // EditorLayout computes regions for the editor page.
// It takes screen dimensions in Dp and returns []Element with regions in Dp. // It takes screen dimensions in Dp and returns []Element with regions in Dp.
// This function works exclusively in Dp — no pixel conversions. // This function works exclusively in Dp — no pixel conversions.

View File

@ -19,7 +19,7 @@ import (
type Renderer struct { type Renderer struct {
theme Theme theme Theme
shp *text.Shaper 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. // 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} return &Renderer{theme: th, shp: shp, scale: 1.0}
} }
// setScale updates the renderer's scale factor from a layout.Context. // SetScale updates the renderer's scale factor from State.Scale.
func (r *Renderer) setScale(gtx layout.Context) { func (r *Renderer) SetScale(scale float32) {
r.scale = gtx.Metric.PxPerDp r.scale = scale
} }
// toPx converts Dp to physical pixels using the renderer's 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). // Draw iterates elements and draws each in slice order (back-to-front).
// It captures the initial constraints once at the start, then passes them // It captures the initial constraints once at the start, then passes them
// to each render function for consistent positioning. // 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) { 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. // Capture initial constraints once — before any clips modify them.
// These are in physical pixels and serve as the window bounds for all positioning. // These are in physical pixels and serve as the window bounds for all positioning.
initialConstraints := WindowConstraints{ initialConstraints := WindowConstraints{