ui: basic map scroller
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
|
||||
"github.com/iliadenisov/galaxy/client/world"
|
||||
)
|
||||
|
||||
/*
|
||||
Editor pan integration for Fyne Draggable:
|
||||
|
||||
- DragEvent provides absolute coordinates in "Fyne units".
|
||||
- Editor 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
|
||||
*/
|
||||
|
||||
// draggableEditor is the minimal interface we need from your editor implementation.
|
||||
// If your Editor already has these methods/fields, you can fold the code directly into it.
|
||||
type draggableEditor 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 draggableEditor
|
||||
|
||||
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 draggableEditor) *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()
|
||||
}
|
||||
Reference in New Issue
Block a user