fix(zoom): split ZoomAndPanMode into per-gesture flags + honor NoFit in manual Zoom - #2177
Conversation
Adds PanX, ZoomX, PanY, ZoomY so users can enable zoom while disabling pan (or vice versa). The existing X, Y and Both names are preserved as composites (X = PanX | ZoomX, Y = PanY | ZoomY), so code or XAML that references them by name keeps the same pan+zoom semantics. The CartesianChartEngine now checks the granular flag for each gesture: Zoom and zoom-by-section consult ZoomX/ZoomY, Pan and the pointer-up bounce-back consult PanX/PanY, and the post-zoom fit consults ZoomX/ZoomY. Closes #2175 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verifies that ZoomX/PanX (and Y counterparts) only act on their gesture, that mismatched calls are no-ops (Pan with ZoomX-only does nothing, Zoom with PanX-only does nothing), and that the composite X flag still pans and zooms — covering the back-compat guarantee. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Updates the axes guide, the lines/zoom sample template and llms-full.txt to list the new per-gesture flags alongside the existing X/Y/Both composites, and shows the "zoom on X, no pan" combination from the discussion that motivated #2175. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR refines the chart interaction API by splitting ZoomAndPanMode into per-gesture, per-axis flags so consumers can enable/disable pan vs. zoom independently (e.g., allow wheel/pinch zoom on X while disallowing panning).
Changes:
- Extended
ZoomAndPanModewithPanX,ZoomX,PanY,ZoomY, while keepingX,Y, andBothas backward-compatible composites. - Updated
CartesianChartEngineto use the new flags so zoom-related and pan-related gestures consult the appropriate bits. - Added unit tests validating zoom/pan isolation per axis and confirming composite
Xpreserves existing behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/LiveChartsCore/Measure/ZoomAndPanMode.cs |
Introduces new per-gesture flags and redefines X/Y/Both as composites; updates docs. |
src/LiveChartsCore/CartesianChartEngine.cs |
Updates zoom/pan/zoom-by-section and related fit/bounce logic to check ZoomX/ZoomY vs PanX/PanY. |
tests/CoreTests/OtherTests/ChartInteractiveApiTests.cs |
Adds test coverage for the new flags and for backward compatibility of X. |
Thanks for your contribution!Benchmark delta — ✅ 1 improvement, no regressionsShow detailsBase: master — Head: ec7217a (run)
Build summary for run #353. Packing ✅Download the NuGet packages for this build here (dev-353). Available for 30 days — you can either use them directly or wait for this PR to be merged to have them published to NuGet.org. Tests ✅ |
FitAllOnZoom checked the chart view's ZoomMode for the NoFit flag instead of the flags passed to the public Zoom(...) call. Manual callers that built their own input scheme (view ZoomMode = None) had their NoFit ignored, so the post-zoom debounced fit snapped axis limits back to the data bounds. Now the fit consults the flags argument, matching the other NoFit checks in ZoomAxis / PanAxis / InvokePointerUp. Closes #2119 (zoom decoupling part is in the prior commit; this covers the "manual Zoom must honor NoFit" part). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Avalonia and Eto cartesian views were marking PointerWheel events as Handled whenever ZoomMode was non-None. With pan-only modes (PanX/PanY) now possible, that swallowed the wheel even though the underlying Zoom() call is a no-op — blocking parent ScrollViewers in the same way issue #1864 originally fixed for ZoomMode = None. The guard now checks for any zoom flag (ZoomX/ZoomY) before suppressing the event. Also fixes two pre-existing doc issues touched by this PR: - StartZoomingSection's `point` param doc said "panning operation" instead of describing the zoom-by-section start point. - InvertPanningPointerTrigger doc said "triggered to the left click" (should read "triggered by the left click"). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- "forces the the chart" → "forces the chart" (lines/zoom template). - "Fit top Bounds" → "Fit to Bounds" (lines/zoom template). - InvertPanningPointerTrigger doc claimed both pan and zoom-by-section were triggered by "touch-and-hold" on touch devices, which can't be true since they're different actions. Aligned with the secondary- action terminology used elsewhere in the codebase: pan via right click / double tap, zoom-by-section via left click / single tap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ranular-zoom-pan-flags fix(zoom): split ZoomAndPanMode into per-gesture flags + honor NoFit in manual Zoom
Summary
PanX,ZoomX,PanY,ZoomYtoZoomAndPanModeso each gesture can be enabled independently (e.g.ZoomMode = ZoomAndPanMode.ZoomXto allow wheel/pinch zoom on the X axis with no panning).X,Y,Bothas composites (X = PanX | ZoomX,Y = PanY | ZoomY,Both = X | Y) — code or XAML that references them by name keeps the existing pan+zoom behavior.CartesianChartEngineso each gesture consults the right flag:Zoomand zoom-by-section checkZoomX/ZoomY,Panand the pointer-up bounce-back checkPanX/PanY, and the post-zoom fit checksZoomX/ZoomY.FitAllOnZoomto honor theNoFitflag passed to the publicZoom(...)call. It used to read the chart view'sZoomModeforNoFit, which silently ignored the argument when callers had setZoomMode = Noneand were driving zoom manually.llms-full.txtto list the new per-gesture flags and show the "zoom on X, no pan" combination.Closes #2175 (originally reported in discussion #2167 — "How to disable pan, but keep the zoom on axis?").
Closes #2119 — the "decouple Zoom and Pan" and "manual
Zoommust honorNoFit" parts. The third ask in #2119 (custom input mapping for keys/pointer events) is intentionally out of scope; the existing API surface is enough to build that on the consumer side.Notes
NoFitis now16, etc.). Code that references the enum by name is unaffected; anyone hardcoding integer values for the enum would see a shift.X(nowPanX | ZoomX = 3) still preserves the old pan+zoom-on-X behavior.Test plan
dotnet test --project tests/CoreTests/CoreTests.csproj --framework net8.0— 461/461 passing (453 prior + 8 new).ZoomX/PanXisolation,ZoomY/PanYisolation,Panno-op underZoomX-only,Zoomno-op underPanX-only, compositeXstill pans+zooms.NoFit-honoring regression test verified to fail without the engine fix (MaxLimitsnapped from 60 → ~9 with the bug, stays ~59 with the fix).🤖 Generated with Claude Code