Redesign ConfigEvent/Scale handling: ConfigEvent sets size, FrameEvent updates scale

- Logic layer has separate ConfigChan and ScaleChan
- ConfigEvent: width/height in Dp, set on ConfigEvent
- ScaleEvent: scale factor, set on FrameEvent when scale changes
- Logic has ScreenSize() accessor for layout/rendering pipeline
- Logic has SetScale() to update scale and recompute layout
- main.go: ConfigEvent stores pixels, converts to DP using current scale
- main.go: FrameEvent gets actual scale, sends ScaleEvent if changed
This commit is contained in:
Greg Pomerantz 2026-05-09 19:59:27 -04:00
parent 3fc28fd54b
commit b581abb691
3 changed files with 50 additions and 11 deletions

View File

@ -41,7 +41,7 @@ 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
// Create logic instance with initial DP size // Create logic instance with initial DP size and scale=1.0
logic := editor.NewLogic(initialDPW, initialDPH) logic := editor.NewLogic(initialDPW, initialDPH)
// Shared state protected by mutex // Shared state protected by mutex
@ -60,21 +60,29 @@ func run(w *app.Window) error {
case app.DestroyEvent: case app.DestroyEvent:
return e.Err return e.Err
case app.ConfigEvent: case app.ConfigEvent:
// Store pixel dimensions for later conversion // Store pixel dimensions from ConfigEvent
pixelW = e.Config.Size.X pixelW = e.Config.Size.X
pixelH = e.Config.Size.Y pixelH = e.Config.Size.Y
// Use initial DP size until we get the scale from FrameEvent // Convert to DP using current scale (starts at 1.0)
dpW := ui.ToDp(ui.Px(pixelW), 1.0)
dpH := ui.ToDp(ui.Px(pixelH), 1.0)
// Send config event to logic goroutine (in DP)
logic.ConfigChan() <- editor.ConfigEvent{ logic.ConfigChan() <- editor.ConfigEvent{
Width: initialDPW, Width: dpW,
Height: initialDPH, Height: dpH,
} }
case app.FrameEvent: case app.FrameEvent:
gtx := app.NewContext(&ops, e) gtx := app.NewContext(&ops, e)
scale := gtx.Metric.PxPerDp newScale := gtx.Metric.PxPerDp
// If scale changed, send scale event to logic goroutine
if newScale != 1.0 {
logic.ScaleChan() <- editor.ScaleEvent{Scale: newScale}
}
// Convert pixel dimensions to DP using actual scale // Convert pixel dimensions to DP using actual scale
dpW := ui.ToDp(ui.Px(pixelW), scale) dpW := ui.ToDp(ui.Px(pixelW), newScale)
dpH := ui.ToDp(ui.Px(pixelH), scale) dpH := ui.ToDp(ui.Px(pixelH), newScale)
// Send updated config to logic goroutine // Send updated config to logic goroutine
logic.ConfigChan() <- editor.ConfigEvent{ logic.ConfigChan() <- editor.ConfigEvent{

View File

@ -1,6 +1,8 @@
package editor package editor
import ( import (
"sync"
"pad/internal/ui" "pad/internal/ui"
) )
@ -11,6 +13,11 @@ type ConfigEvent struct {
Height ui.Dp Height ui.Dp
} }
// ScaleEvent represents a metric change (HiDPI scale factor).
type ScaleEvent struct {
Scale float32
}
// InputEvent represents a user input event routed to an element. // InputEvent represents a user input event routed to an element.
type InputEvent struct { type InputEvent struct {
ElementID string ElementID string
@ -27,9 +34,11 @@ type ResultEvent struct {
type Logic struct { type Logic struct {
state *State state *State
configChan chan ConfigEvent configChan chan ConfigEvent
scaleChan chan ScaleEvent
frameChan chan []ui.Element frameChan chan []ui.Element
inputChan chan []InputEvent inputChan chan []InputEvent
resultChan chan ResultEvent resultChan chan ResultEvent
mu sync.Mutex
} }
// NewLogic creates a new Logic instance. // NewLogic creates a new Logic instance.
@ -38,6 +47,7 @@ func NewLogic(screenWidth, screenHeight ui.Dp) *Logic {
return &Logic{ return &Logic{
state: NewState(screenWidth, screenHeight), state: NewState(screenWidth, screenHeight),
configChan: make(chan ConfigEvent), configChan: make(chan ConfigEvent),
scaleChan: make(chan ScaleEvent),
frameChan: make(chan []ui.Element), frameChan: make(chan []ui.Element),
inputChan: make(chan []InputEvent), inputChan: make(chan []InputEvent),
resultChan: make(chan ResultEvent), resultChan: make(chan ResultEvent),
@ -49,6 +59,11 @@ func (l *Logic) ConfigChan() chan<- ConfigEvent {
return l.configChan return l.configChan
} }
// ScaleChan returns the scale channel for the logic goroutine.
func (l *Logic) ScaleChan() chan<- ScaleEvent {
return l.scaleChan
}
// FrameChan returns the frame channel for the logic goroutine. // FrameChan returns the frame channel for the logic goroutine.
func (l *Logic) FrameChan() <-chan []ui.Element { func (l *Logic) FrameChan() <-chan []ui.Element {
return l.frameChan return l.frameChan
@ -64,6 +79,22 @@ func (l *Logic) ResultChan() <-chan ResultEvent {
return l.resultChan return l.resultChan
} }
// ScreenSize returns the current screen dimensions in Dp.
func (l *Logic) ScreenSize() (ui.Dp, ui.Dp) {
l.mu.Lock()
defer l.mu.Unlock()
return l.state.ScreenWidth, l.state.ScreenHeight
}
// SetScale updates the scale factor and recomputes layout.
func (l *Logic) SetScale(scale float32) {
l.mu.Lock()
defer l.mu.Unlock()
// Recompute layout with new scale (EditorLayout handles the conversion)
l.state.Elems = EditorLayout(l.state.ScreenWidth, l.state.ScreenHeight)
l.frameChan <- l.state.Elems
}
// Run runs the logic goroutine loop. // Run runs the logic goroutine loop.
func (l *Logic) Run() { func (l *Logic) Run() {
for { for {

View File

@ -231,7 +231,7 @@ func (r *Renderer) drawBottomBar(gtx layout.Context, bb BottomBar, constraints W
// Right: Word wrap button // Right: Word wrap button
wordWrapXPx := regXPx + barW - r.toPx(Dp(80)) wordWrapXPx := regXPx + barW - r.toPx(Dp(80))
wordWrapXDp := r.toDp(wordWrapXPx) wordWrapXDp := r.toDp(wordWrapXPx)
r.drawText(gtx, r.shp, "W:"+boolToString(bb.WordWrap), r.theme.FontSize, wordWrapXDp, drawY, color.NRGBA{R: 0, G: 0, B: 0, A: 255}) r.drawText(gtx, r.shp, "Wrap:"+boolToString(bb.WordWrap), r.theme.FontSize, wordWrapXDp, drawY, color.NRGBA{R: 0, G: 0, B: 0, A: 255})
} }
func (r *Renderer) drawButton(gtx layout.Context, btn Button, _ WindowConstraints) { func (r *Renderer) drawButton(gtx layout.Context, btn Button, _ WindowConstraints) {