client io architecture
This commit is contained in:
+190
-11
@@ -1,24 +1,203 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"image"
|
||||
"sync"
|
||||
|
||||
"galaxy/client/world"
|
||||
"galaxy/connector"
|
||||
mc "galaxy/model/client"
|
||||
"galaxy/model/report"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/app"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
type ui struct {
|
||||
type client struct {
|
||||
conn connector.UIConnector
|
||||
app fyne.App
|
||||
window fyne.Window
|
||||
|
||||
world *world.World
|
||||
drawer *world.GGDrawer
|
||||
raster *canvas.Raster
|
||||
co *RasterCoalescer[world.RenderParams]
|
||||
pan *PanController
|
||||
|
||||
// Protected camera/options state (UI-facing). This is the "base" params snapshot.
|
||||
// Viewport/margins are NOT stored here; they come from raster draw callback.
|
||||
mu sync.RWMutex
|
||||
wp *world.RenderParams
|
||||
canvasScale float32
|
||||
|
||||
// Latest raster geometry metadata for correct event->pixel conversion:
|
||||
// - logical size: raster.Size() (Fyne units)
|
||||
// - pixel size: last (wPx,hPx) passed to draw callback
|
||||
metaMu sync.RWMutex
|
||||
lastRasterLogicW float32
|
||||
lastRasterLogicH float32
|
||||
lastRasterPxW int
|
||||
lastRasterPxH int
|
||||
lastCanvasScale float32 // optional, useful for debugging
|
||||
|
||||
// Snapshot of params actually used for the last render (includes viewport/margins).
|
||||
// Used for HitTest and to keep UI interactions consistent with what the user sees.
|
||||
lastRenderedMu sync.RWMutex
|
||||
lastRenderedParams world.RenderParams
|
||||
|
||||
// Indexing / backing-canvas caches (owned by client because it depends on UI geometry)
|
||||
lastIndexedViewportW int
|
||||
lastIndexedViewportH int
|
||||
lastIndexedZoomFp int
|
||||
|
||||
lastCanvasW int
|
||||
lastCanvasH int
|
||||
|
||||
viewportImg *image.RGBA
|
||||
viewportW int
|
||||
viewportH int
|
||||
|
||||
hits []world.Hit
|
||||
}
|
||||
|
||||
func NewUI() *ui {
|
||||
c := &ui{}
|
||||
c.app = app.New()
|
||||
c.window = c.app.NewWindow("Galaxy Plus")
|
||||
client := NewClient()
|
||||
client.BuildUI(c.window)
|
||||
return c
|
||||
func NewClient(conn connector.UIConnector, app fyne.App, settings mc.Settings) (mc.Client, error) {
|
||||
e := &client{
|
||||
conn: conn,
|
||||
app: app,
|
||||
window: app.NewWindow("Galaxy Plus"),
|
||||
world: nil,
|
||||
wp: &world.RenderParams{
|
||||
CameraZoom: 1.0,
|
||||
Options: &world.RenderOptions{DisableWrapScroll: false},
|
||||
},
|
||||
lastCanvasScale: 1.0,
|
||||
hits: make([]world.Hit, 5),
|
||||
}
|
||||
|
||||
e.drawer = &world.GGDrawer{DC: nil}
|
||||
|
||||
e.raster = canvas.NewRaster(func(wPx, hPx int) image.Image {
|
||||
return e.draw(wPx, hPx)
|
||||
})
|
||||
|
||||
e.pan = NewPanController(e)
|
||||
|
||||
e.co = NewRasterCoalescer(
|
||||
FyneExecutor{},
|
||||
e.raster,
|
||||
func(wPx, hPx int, p world.RenderParams) image.Image {
|
||||
return e.renderRasterImage(wPx, hPx, p)
|
||||
},
|
||||
)
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func (c *ui) Run() {
|
||||
c.window.ShowAndRun()
|
||||
func (e *client) loadReport(t uint) {
|
||||
e.conn.FetchReport("GAME_ID", t, func(r report.Report, err error) {
|
||||
if err != nil {
|
||||
e.handlerError(err)
|
||||
} else {
|
||||
e.setReport(r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (e *client) setReport(r report.Report) {
|
||||
w := world.NewWorld(int(r.Width), int(r.Height))
|
||||
for i := range r.LocalPlanet {
|
||||
p := r.LocalPlanet[i]
|
||||
w.AddCircle(p.X.F(), p.Y.F(), p.Size.F())
|
||||
}
|
||||
for i := range r.UnidentifiedPlanet {
|
||||
p := r.UnidentifiedPlanet[i]
|
||||
w.AddPoint(p.X.F(), p.Y.F())
|
||||
}
|
||||
e.loadWorld(w)
|
||||
}
|
||||
|
||||
func (e *client) handlerError(err error) {
|
||||
|
||||
}
|
||||
|
||||
func (e *client) BuildUI(w fyne.Window) {
|
||||
mapCanvas := newInteractiveRaster(e, e.raster, e.onRasterWidgetLayout, e.onScrolled, e.onDragged, e.onDradEnd, e.onTapped)
|
||||
mapCanvas.SetMinSize(fyne.NewSize(292, 292))
|
||||
|
||||
toolbar := widget.NewToolbar(
|
||||
widget.NewToolbarAction(
|
||||
theme.FolderIcon(),
|
||||
func() { e.loadWorld(mockWorld()) }),
|
||||
widget.NewToolbarSeparator(),
|
||||
widget.NewToolbarAction(
|
||||
theme.NavigateBackIcon(),
|
||||
func() {}),
|
||||
widget.NewToolbarAction(
|
||||
theme.NavigateNextIcon(),
|
||||
func() {}),
|
||||
)
|
||||
|
||||
tabs := container.NewAppTabs(
|
||||
container.NewTabItemWithIcon(
|
||||
"Map",
|
||||
theme.GridIcon(),
|
||||
mapCanvas),
|
||||
container.NewTabItemWithIcon(
|
||||
"Calculator",
|
||||
theme.ComputerIcon(),
|
||||
container.NewStack(widget.NewButton("Calc", func() {})),
|
||||
),
|
||||
)
|
||||
|
||||
content := container.NewBorder(
|
||||
toolbar, // top
|
||||
nil, // bottom
|
||||
nil, // left
|
||||
nil, // right
|
||||
tabs, // center
|
||||
)
|
||||
|
||||
w.CenterOnScreen()
|
||||
w.SetContent(content)
|
||||
}
|
||||
|
||||
func (e *client) loadWorld(w *world.World) {
|
||||
w.SetCircleRadiusScaleFp(world.SCALE / 4)
|
||||
e.world = w
|
||||
// TODO: store camera position in user settings
|
||||
e.wp.CameraXWorldFp = w.W / 2
|
||||
e.wp.CameraYWorldFp = w.H / 2
|
||||
e.world.SetTheme(world.ThemeDark)
|
||||
|
||||
// if e.world == nil {
|
||||
// w.SetCircleRadiusScaleFp(world.SCALE / 4)
|
||||
// e.world = w
|
||||
// e.wp.CameraXWorldFp = w.W / 2
|
||||
// e.wp.CameraYWorldFp = w.H / 2
|
||||
// e.world.SetTheme(world.ThemeDark)
|
||||
// } else {
|
||||
// if e.world.Theme().ID() == "theme.light.v1" {
|
||||
// e.world.SetTheme(world.ThemeDark)
|
||||
// } else {
|
||||
// e.world.SetTheme(world.ThemeLight)
|
||||
// }
|
||||
// }
|
||||
|
||||
e.RequestRefresh()
|
||||
}
|
||||
|
||||
func (e *client) Run() error {
|
||||
e.BuildUI(e.window)
|
||||
e.window.ShowAndRun()
|
||||
e.RequestRefresh()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *client) Shutdown() {
|
||||
e.window.Close()
|
||||
}
|
||||
|
||||
func (e *client) OnConnection(bool) {}
|
||||
|
||||
Reference in New Issue
Block a user