Unify ConfigEvent and ScaleEvent on single channel with type switch

- ConfigUpdate interface with apply(*State) method
- ConfigEvent and ScaleEvent both implement ConfigUpdate
- Logic.ConfigChan() accepts both types
- Type switch in Run() differentiates ConfigEvent vs ScaleEvent
- main.go sends both on same ConfigChan()
This commit is contained in:
Greg Pomerantz 2026-05-09 20:10:06 -04:00
parent fbf6cfac95
commit ef31701d87
2 changed files with 39 additions and 31 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
// Track previous scale to detect changes // Track previous scale to detect changes
prevScale := float32(1.0) 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)
@ -78,9 +78,9 @@ func run(w *app.Window) error {
newScale := gtx.Metric.PxPerDp newScale := gtx.Metric.PxPerDp
// If scale changed, send scale event to logic goroutine // If scale changed, send scale event to logic goroutine
if newScale != prevScale { if newScale != scale {
logic.ScaleChan() <- editor.ScaleEvent{Scale: newScale} logic.ConfigChan() <- editor.ScaleEvent{Scale: newScale}
prevScale = newScale scale = newScale
} }
// Convert pixel dimensions to DP using actual scale // Convert pixel dimensions to DP using actual scale

View File

@ -18,6 +18,24 @@ type ScaleEvent struct {
Scale float32 Scale float32
} }
// ConfigUpdate is a common interface for all configuration updates.
// Both ConfigEvent and ScaleEvent implement this interface.
type ConfigUpdate interface {
apply(*State)
}
func (e ConfigEvent) apply(s *State) {
s.ScreenWidth = e.Width
s.ScreenHeight = e.Height
s.Elems = EditorLayout(e.Width, e.Height)
}
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
}
// 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
@ -33,8 +51,7 @@ type ResultEvent struct {
// Logic runs the logic goroutine and provides channels for communication. // Logic runs the logic goroutine and provides channels for communication.
type Logic struct { type Logic struct {
state *State state *State
configChan chan ConfigEvent configChan chan ConfigUpdate
scaleChan chan ScaleEvent
frameChan chan []ui.Element frameChan chan []ui.Element
inputChan chan []InputEvent inputChan chan []InputEvent
resultChan chan ResultEvent resultChan chan ResultEvent
@ -46,24 +63,19 @@ type Logic struct {
func NewLogic(screenWidth, screenHeight ui.Dp) *Logic { 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 ConfigUpdate),
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),
} }
} }
// ConfigChan returns the config channel for the logic goroutine. // ConfigChan returns the unified config channel for the logic goroutine.
func (l *Logic) ConfigChan() chan<- ConfigEvent { // Accepts ConfigEvent (size) and ScaleEvent (scale factor).
func (l *Logic) ConfigChan() chan<- ConfigUpdate {
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
@ -86,25 +98,21 @@ func (l *Logic) ScreenSize() (ui.Dp, ui.Dp) {
return l.state.ScreenWidth, l.state.ScreenHeight 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 {
select { select {
case cfg := <-l.configChan: case update := <-l.configChan:
// Update screen size and recompute layout // Type switch to differentiate ConfigEvent vs ScaleEvent
l.state.ScreenWidth = cfg.Width switch e := update.(type) {
l.state.ScreenHeight = cfg.Height case ConfigEvent:
l.state.Elems = EditorLayout(cfg.Width, cfg.Height) e.apply(l.state)
l.frameChan <- l.state.Elems 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.
l.frameChan <- l.state.Elems
}
case <-l.inputChan: case <-l.inputChan:
// Process input (not implemented in mockup) // Process input (not implemented in mockup)
l.frameChan <- l.state.Elems l.frameChan <- l.state.Elems