- Add ui.Dp and ui.Px as distinct named types - Logic layer works exclusively in Dp (positions, sizes, regions) - Renderer converts Dp→Px at Gio interop boundaries - Capture gtx.Constraints once at start of Draw() for consistent positioning - main.go converts app.ConfigEvent pixels to Dp before passing to logic layer - Update documentation with new coordinate system architecture
108 lines
2.4 KiB
Go
108 lines
2.4 KiB
Go
package main
|
|
|
|
import (
|
|
"log"
|
|
"os"
|
|
"sync"
|
|
|
|
"gioui.org/app"
|
|
"gioui.org/op"
|
|
"gioui.org/text"
|
|
"gioui.org/unit"
|
|
"gioui.org/font/gofont"
|
|
|
|
"pad/internal/editor"
|
|
"pad/internal/ui"
|
|
)
|
|
|
|
func main() {
|
|
go func() {
|
|
w := new(app.Window)
|
|
w.Option(app.Title("Pad"))
|
|
w.Option(app.Size(unit.Dp(390), unit.Dp(844)))
|
|
if err := run(w); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
os.Exit(0)
|
|
}()
|
|
app.Main()
|
|
}
|
|
|
|
func run(w *app.Window) error {
|
|
var ops op.Ops
|
|
|
|
shaper := text.NewShaper(text.WithCollection(gofont.Collection()))
|
|
renderer := ui.New(ui.Theme{FontSize: 14}, shaper)
|
|
|
|
// Track window dimensions in pixels (from ConfigEvent) and DP (for logic layer)
|
|
var pixelW, pixelH int
|
|
var dpW, dpH ui.Dp
|
|
var scale float32 = 1.0 // updated on first FrameEvent
|
|
|
|
// Create logic instance with initial DP size
|
|
logic := editor.NewLogic(dpW, dpH)
|
|
|
|
// Shared state protected by mutex
|
|
var mu sync.Mutex
|
|
var elems []ui.Element
|
|
|
|
// Start logic goroutine
|
|
go logic.Run()
|
|
|
|
// Start frame receiver goroutine
|
|
go frameReceiver(w, &mu, &elems, logic.FrameChan())
|
|
|
|
// Main event loop
|
|
for {
|
|
switch e := w.Event().(type) {
|
|
case app.DestroyEvent:
|
|
return e.Err
|
|
case app.ConfigEvent:
|
|
// Store pixel dimensions from ConfigEvent
|
|
pixelW = e.Config.Size.X
|
|
pixelH = e.Config.Size.Y
|
|
// Convert to DP using current scale (default 1.0 until first FrameEvent)
|
|
dpW = ui.ToDp(ui.Px(pixelW), scale)
|
|
dpH = ui.ToDp(ui.Px(pixelH), scale)
|
|
// Send config event to logic goroutine (in DP)
|
|
logic.ConfigChan() <- editor.ConfigEvent{
|
|
Width: dpW,
|
|
Height: dpH,
|
|
}
|
|
case app.FrameEvent:
|
|
// Update scale from the frame context
|
|
gtx := app.NewContext(&ops, e)
|
|
scale = gtx.Metric.PxPerDp
|
|
|
|
// Reconvert pixel dimensions to DP with actual scale
|
|
dpW = ui.ToDp(ui.Px(pixelW), scale)
|
|
dpH = ui.ToDp(ui.Px(pixelH), scale)
|
|
|
|
// Send updated config to logic goroutine
|
|
logic.ConfigChan() <- editor.ConfigEvent{
|
|
Width: dpW,
|
|
Height: dpH,
|
|
}
|
|
|
|
// Acquire mutex, read frame, draw, release mutex
|
|
mu.Lock()
|
|
currentElems := elems
|
|
mu.Unlock()
|
|
|
|
renderer.Draw(gtx, currentElems)
|
|
e.Frame(&ops)
|
|
}
|
|
}
|
|
}
|
|
|
|
// frameReceiver runs in a separate goroutine and bridges frameChan to the main loop.
|
|
func frameReceiver(w *app.Window, mu *sync.Mutex, elems *[]ui.Element, frameChan <-chan []ui.Element) {
|
|
for {
|
|
frame := <-frameChan
|
|
mu.Lock()
|
|
*elems = frame
|
|
w.Invalidate()
|
|
mu.Unlock()
|
|
}
|
|
}
|