fix: drag speed on pan
This commit is contained in:
+15
-1
@@ -82,6 +82,10 @@ func (e *client) renderRasterImage(viewportW, viewportH int, p world.RenderParam
|
||||
return image.NewRGBA(image.Rect(0, 0, 0, 0))
|
||||
}
|
||||
|
||||
// Keep the incoming zoom snapshot so we can safely sync corrected zoom back
|
||||
// to base params only when no newer zoom was written concurrently.
|
||||
inputZoom := p.CameraZoom
|
||||
|
||||
// Record current raster pixel size (used for event coordinate conversion).
|
||||
e.metaMu.Lock()
|
||||
e.lastRasterPxW = viewportW
|
||||
@@ -95,7 +99,17 @@ func (e *client) renderRasterImage(viewportW, viewportH int, p world.RenderParam
|
||||
p.MarginYPx = viewportH / 4
|
||||
|
||||
// Correct zoom for viewport/world constraints, and clamp camera for no-wrap.
|
||||
p.CameraZoom = e.world.CorrectCameraZoom(p.CameraZoom, viewportW, viewportH)
|
||||
correctedZoom := e.world.CorrectCameraZoom(inputZoom, viewportW, viewportH)
|
||||
p.CameraZoom = correctedZoom
|
||||
|
||||
// Sync corrected zoom to the canonical UI-facing params snapshot.
|
||||
// Guard prevents stale render snapshots from overwriting a newer zoom value
|
||||
// that may have been set by another UI event.
|
||||
e.mu.Lock()
|
||||
if e.wp.CameraZoom == inputZoom {
|
||||
e.wp.CameraZoom = correctedZoom
|
||||
}
|
||||
e.mu.Unlock()
|
||||
|
||||
// Ensure indexing is up-to-date when viewport size or zoom changes.
|
||||
zoomFp, err := p.CameraZoomFp()
|
||||
|
||||
+3
-4
@@ -11,9 +11,8 @@ import (
|
||||
/*
|
||||
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.
|
||||
- DragEvent.Dragged provides per-event delta in Fyne logical units.
|
||||
- Client knows canvasScale (pixels per Fyne unit) and converts to pixels.
|
||||
- We update camera center in world-fixed (CameraXWorldFp/YWorldFp).
|
||||
|
||||
Sign convention (map follows pointer):
|
||||
@@ -24,7 +23,7 @@ Sign convention (map follows pointer):
|
||||
// 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 returns pixels per Fyne logical unit.
|
||||
CanvasScale() float32
|
||||
|
||||
// UpdateParams applies a mutation and schedules refresh through your coalescer.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"image"
|
||||
"testing"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
@@ -107,3 +108,94 @@ func TestFyneTestDriverIsUsable(t *testing.T) {
|
||||
t.Parallel()
|
||||
_ = test.NewApp()
|
||||
}
|
||||
|
||||
type immediateExecutor struct{}
|
||||
|
||||
func (immediateExecutor) Post(fn func()) {
|
||||
if fn != nil {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
|
||||
type noopRefresher struct{}
|
||||
|
||||
func (noopRefresher) Refresh() {}
|
||||
|
||||
func newZoomSyncTestClient(t *testing.T, worldW, worldH int, cameraZoom float64) *client {
|
||||
t.Helper()
|
||||
|
||||
w := world.NewWorld(worldW, worldH)
|
||||
e := &client{
|
||||
world: w,
|
||||
drawer: &world.GGDrawer{},
|
||||
wp: &world.RenderParams{
|
||||
CameraZoom: cameraZoom,
|
||||
CameraXWorldFp: w.W / 2,
|
||||
CameraYWorldFp: w.H / 2,
|
||||
Options: &world.RenderOptions{DisableWrapScroll: false},
|
||||
},
|
||||
hits: make([]world.Hit, 5),
|
||||
}
|
||||
|
||||
e.co = NewRasterCoalescer(
|
||||
immediateExecutor{},
|
||||
noopRefresher{},
|
||||
func(wPx, hPx int, _ world.RenderParams) image.Image {
|
||||
return image.NewRGBA(image.Rect(0, 0, wPx, hPx))
|
||||
},
|
||||
)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func TestRenderRasterImage_SyncsCorrectedZoomToBaseParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := newZoomSyncTestClient(t, 10, 10, 1.0)
|
||||
p := *e.wp
|
||||
|
||||
correctedZoom := e.world.CorrectCameraZoom(p.CameraZoom, 100, 100)
|
||||
require.NotEqual(t, p.CameraZoom, correctedZoom)
|
||||
|
||||
_ = e.renderRasterImage(100, 100, p)
|
||||
|
||||
require.Equal(t, correctedZoom, e.wp.CameraZoom)
|
||||
}
|
||||
|
||||
func TestRenderRasterImage_DoesNotOverrideNewerBaseZoom(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := newZoomSyncTestClient(t, 10, 10, 1.0)
|
||||
p := *e.wp
|
||||
|
||||
// Simulate a newer UI update that happened after this render snapshot was taken.
|
||||
e.wp.CameraZoom = 3.0
|
||||
|
||||
_ = e.renderRasterImage(100, 100, p)
|
||||
|
||||
require.Equal(t, 3.0, e.wp.CameraZoom)
|
||||
}
|
||||
|
||||
func TestPanController_Dragged_AfterRenderZoomCorrection_UsesSyncedZoom(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := newZoomSyncTestClient(t, 10, 10, 1.0)
|
||||
|
||||
// Initial render corrects zoom and syncs it into base params.
|
||||
_ = e.renderRasterImage(100, 100, *e.wp)
|
||||
|
||||
syncedZoom := e.wp.CameraZoom
|
||||
require.NotEqual(t, 1.0, syncedZoom)
|
||||
|
||||
zoomFp, err := world.CameraZoomToWorldFixed(syncedZoom)
|
||||
require.NoError(t, err)
|
||||
|
||||
startX := e.wp.CameraXWorldFp
|
||||
pan := NewPanController(e)
|
||||
pan.Dragged(&fyne.DragEvent{
|
||||
Dragged: fyne.Delta{DX: 1, DY: 0},
|
||||
})
|
||||
|
||||
expectedShift := world.PixelSpanToWorldFixed(1, zoomFp)
|
||||
require.Equal(t, startX-expectedShift, e.wp.CameraXWorldFp)
|
||||
}
|
||||
Reference in New Issue
Block a user