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())) logic := editor.NewLogic() renderer := ui.New(ui.Theme{FontSize: 14}, shaper, logic.State()) var mu sync.Mutex var elems []ui.Element go frameReceiver(w, &mu, &elems, logic.FrameChan()) go logic.Run() for { switch e := w.Event().(type) { case app.DestroyEvent: return e.Err case app.ConfigEvent: // ConfigEvent: raw pixel dimensions only. logic.ConfigChan() <- editor.ConfigEvent{ PixelWidth: e.Config.Size.X, PixelHeight: e.Config.Size.Y, } case app.FrameEvent: gtx := app.NewContext(&ops, e) newScale := gtx.Metric.PxPerDp curScale := logic.State().Scale() if newScale != curScale { logic.ConfigChan() <- editor.ScaleEvent{newScale} } mu.Lock() currentElems := elems renderer.Draw(gtx, currentElems) logic.DisplayLineChan() <- int(renderer.LastLineY()) // Send search query update to the logic goroutine when it changes. // The logic goroutine handles filtering and triggers a new frame. newQuery := logic.State().Browser.SearchEditor.Text() if newQuery != logic.State().Browser.Query { logic.SearchQueryChan() <- newQuery } if events := renderer.CheckGestures(e.Source, gtx.Metric); len(events) > 0 { logic.InputChan() <- events } e.Frame(&ops) mu.Unlock() } } } 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() } }