112 lines
2.9 KiB
Go
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()
|
|
}
|