- Renderer computes word wrap via single LayoutString pass with
WrapHeuristically policy; glyphs drawn inline at FlagLineBreak
- Fixed line height (fontSize * 1.2), independent of glyph metrics
- Two-finger trackpad scroll via gesture.Scroll with vertical axis
- Display line feedback: renderer reports last glyph Y after each
Draw; logic uses it to clamp scroll offset so last line stops
at bottom of viewport with lineHeight/2 padding
- ScrollRange fix: {Min: -(1<<30), Max: 1<<30} ensures scroll
delta is consumed (empty range consumes nothing via clampSplit)
- Line counting fix: only FlagLineBreak increments count; buffer
flushes (32-glyph cap) draw but don't count
125 lines
3.0 KiB
Go
125 lines
3.0 KiB
Go
package editor
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"pad/internal/ui"
|
|
)
|
|
|
|
// ConfigEvent represents a window configuration change (resize, orientation).
|
|
// PixelWidth and PixelHeight are the raw pixel dimensions from Gio.
|
|
type ConfigEvent struct {
|
|
PixelWidth int
|
|
PixelHeight int
|
|
}
|
|
|
|
// ScaleEvent represents a metric change (HiDPI scale factor).
|
|
type ScaleEvent struct {
|
|
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.PixelWidth = e.PixelWidth
|
|
s.PixelHeight = e.PixelHeight
|
|
}
|
|
|
|
func (e ScaleEvent) apply(s *State) {
|
|
s.SetScale(e.Scale)
|
|
}
|
|
|
|
// ResultEvent represents a completed async task result.
|
|
type ResultEvent struct {
|
|
// Future: add result fields here
|
|
}
|
|
|
|
// Logic runs the logic goroutine and provides channels for communication.
|
|
type Logic struct {
|
|
state *State
|
|
configChan chan ConfigUpdate
|
|
frameChan chan []ui.Element
|
|
inputChan chan []ui.InputEvent
|
|
lastLineYChan chan int // last line Y (Dp, sent as int) feedback from renderer
|
|
resultChan chan ResultEvent
|
|
mu sync.Mutex
|
|
}
|
|
|
|
// NewLogic creates a new Logic instance.
|
|
func NewLogic() *Logic {
|
|
state := NewState()
|
|
TheState = state
|
|
return &Logic{
|
|
state: state,
|
|
configChan: make(chan ConfigUpdate),
|
|
frameChan: make(chan []ui.Element),
|
|
inputChan: make(chan []ui.InputEvent),
|
|
lastLineYChan: make(chan int),
|
|
resultChan: make(chan ResultEvent),
|
|
}
|
|
}
|
|
|
|
// ConfigChan returns the unified config channel for the logic goroutine.
|
|
// Accepts ConfigEvent (size) and ScaleEvent (scale factor).
|
|
func (l *Logic) ConfigChan() chan<- ConfigUpdate {
|
|
return l.configChan
|
|
}
|
|
|
|
// FrameChan returns the frame channel for the logic goroutine.
|
|
func (l *Logic) FrameChan() <-chan []ui.Element {
|
|
return l.frameChan
|
|
}
|
|
|
|
// InputChan returns the input channel for the logic goroutine.
|
|
func (l *Logic) InputChan() chan<- []ui.InputEvent {
|
|
return l.inputChan
|
|
}
|
|
|
|
// DisplayLineChan returns the last line Y feedback channel.
|
|
func (l *Logic) DisplayLineChan() chan<- int {
|
|
return l.lastLineYChan
|
|
}
|
|
|
|
// ResultChan returns the result channel for the logic goroutine.
|
|
func (l *Logic) ResultChan() chan<- ResultEvent {
|
|
return l.resultChan
|
|
}
|
|
|
|
// TheState is the global editor state, set once at startup.
|
|
var TheState *State
|
|
|
|
// Scale returns the current scale factor (pixels per DP).
|
|
func (l *Logic) Scale() float32 {
|
|
return l.state.Scale()
|
|
}
|
|
|
|
// Run runs the logic goroutine loop.
|
|
func (l *Logic) Run() {
|
|
for {
|
|
select {
|
|
case update := <-l.configChan:
|
|
update.apply(l.state)
|
|
l.frameChan <- l.state.layout()
|
|
case y := <-l.lastLineYChan:
|
|
l.state.LastLineY = ui.Dp(y)
|
|
l.frameChan <- l.state.layout()
|
|
case events := <-l.inputChan:
|
|
for _, evt := range events {
|
|
evt.Handler(evt.Data)
|
|
}
|
|
l.frameChan <- l.state.layout()
|
|
case <-l.resultChan:
|
|
l.frameChan <- l.state.layout()
|
|
}
|
|
}
|
|
}
|
|
|
|
// State returns the current state.
|
|
func (l *Logic) State() *State {
|
|
return l.state
|
|
}
|