Files
galaxy-game/client/ui_drag.go
T
2026-03-09 14:26:17 +03:00

112 lines
2.9 KiB
Go

package client
import (
"math"
"fyne.io/fyne/v2"
"github.com/iliadenisov/galaxy/client/world"
)
/*
Client pan integration for Fyne Draggable:
- DragEvent provides absolute coordinates in "Fyne units".
- Client knows canvasScale (Fyne units per pixel) and converts to pixels.
- We keep last drag position and compute dx/dy ourselves.
- We update camera center in world-fixed (CameraXWorldFp/YWorldFp).
Sign convention (map follows pointer):
- Drag right (dxPx > 0): move world content right => move camera left => CameraXWorldFp -= dxWorldFp
- Drag down (dyPx > 0): move world content down => move camera up => CameraYWorldFp -= dyWorldFp
*/
// draggableClient is the minimal interface we need from your client implementation.
// If your Client already has these methods/fields, you can fold the code directly into it.
type draggableClient interface {
// CanvasScale returns the fyne-units-per-pixel scale factor.
CanvasScale() float32
// UpdateParams applies a mutation and schedules refresh through your coalescer.
UpdateParams(fn func(p *world.RenderParams))
// RequestRefresh schedules a refresh with current params (no mutation).
RequestRefresh()
// ForceFullRedraw forces a full redraw on next Render (used on DragEnd).
ForceFullRedraw()
}
// PanController holds per-drag transient state.
type PanController struct {
ed draggableClient
dragging bool
lastFx float32 // last absolute position in Fyne units
lastFy float32
// Remainders to keep subpixel fyne->px conversion stable across many events.
remPxX float32
remPxY float32
}
func NewPanController(ed draggableClient) *PanController {
return &PanController{ed: ed}
}
// Dragged processes one drag event, updates camera center by delta, and schedules redraw.
func (p *PanController) Dragged(ev *fyne.DragEvent) {
if ev == nil {
return
}
scale := p.ed.CanvasScale()
if scale <= 0 {
return
}
// DragEvent.Dragged is delta in Fyne logical units (device independent).
// Convert to pixels by multiplying by canvas scale.
dxPxF := ev.Dragged.DX * scale
dyPxF := ev.Dragged.DY * scale
// accumulate subpixel remainder in pixels
dxPxF += p.remPxX
dyPxF += p.remPxY
dxPx := int(math.Round(float64(dxPxF)))
dyPx := int(math.Round(float64(dyPxF)))
p.remPxX = dxPxF - float32(dxPx)
p.remPxY = dyPxF - float32(dyPx)
if dxPx == 0 && dyPx == 0 {
return
}
p.ed.UpdateParams(func(rp *world.RenderParams) {
zoomFp, err := rp.CameraZoomFp()
if err != nil || zoomFp <= 0 {
return
}
dxWorldFp := world.PixelSpanToWorldFixed(dxPx, zoomFp)
dyWorldFp := world.PixelSpanToWorldFixed(dyPx, zoomFp)
// Map follows pointer
rp.CameraXWorldFp -= dxWorldFp
rp.CameraYWorldFp -= dyWorldFp
})
}
// DragEnd ends the drag gesture. We force a full redraw next to eliminate any
// possible artifacts from incremental shifting and to "settle" the final state.
func (p *PanController) DragEnd() {
p.dragging = false
p.remPxX = 0
p.remPxY = 0
p.ed.ForceFullRedraw()
p.ed.RequestRefresh()
}