Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions docs/cartesianChart/axes.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@ Zooming and panning is disabled by default, you can enable it by setting the `Zo
[ZoomAndPanMode](https://lvcharts.com/api/{{ version }}/LiveChartsCore.Measure.ZoomAndPanMode) and the options are:

- `None`: Disables zooming and panning.
- `X`: Enables zooming and panning on the X axis.
- `Y`: Enables zooming and panning on the Y axis.
- `PanX`: Enables panning on the X axis.
- `ZoomX`: Enables zooming on the X axis (wheel, pinch and zoom by section).
- `PanY`: Enables panning on the Y axis.
- `ZoomY`: Enables zooming on the Y axis (wheel, pinch and zoom by section).
- `X`: Enables zooming and panning on the X axis. Equivalent to `PanX | ZoomX`.
- `Y`: Enables zooming and panning on the Y axis. Equivalent to `PanY | ZoomY`.
- `Both`: Enables zooming and panning on both axes.
- `NoFit`: Disables the "Fit to Bounds" feature that forces the chart to bounce back to the data bounds when zooming and panning finishes.
- `NoZoomBySection`: Disables the "Zoom by Section" feature, this feature selects an area and zooms to this area, normally by right clicking or double tapping and then dragging to the end of the section.
- `InvertPanningPointerTrigger`: Inverts the panning and zoom by section pointer triggers, when the flag is present, panning is triggered by right clicking the chart, and zoom by section by left clicking (or inverts single/double taps on mobile).

Because each gesture has its own flag you can mix and match — for example `ZoomMode = ZoomAndPanMode.ZoomX | ZoomAndPanMode.PanY` enables zoom on the X axis and panning on the Y axis only, and `ZoomMode = ZoomAndPanMode.ZoomX` enables zoom on the X axis with no panning at all (useful on mobile where pan gestures can hide tooltips).

The [ZoomAndPanMode](https://lvcharts.com/api/{{ version }}/LiveChartsCore.Measure.ZoomAndPanMode) type is a flag enum,
so you can combine the options, for example, if you want to enable zooming on the `X` axis and disable "Fit to Bounds"
and "Zoom by Section" you can set the `ZoomMode` property to:
Expand Down
14 changes: 10 additions & 4 deletions docs/samples/lines/zoom/template.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,21 @@ You can enable zooming and panning by setting the `ZoomMode` property, this prop
[ZoomAndPanMode](https://lvcharts.com/api/{{ version }}/LiveChartsCore.Measure.ZoomAndPanMode) and the options are:

- `None`: Disables zooming and panning.
- `X`: Enables zooming and panning on the X axis.
- `Y`: Enables zooming and panning on the Y axis.
- `PanX`: Enables panning on the X axis.
- `ZoomX`: Enables zooming on the X axis (wheel, pinch and zoom by section).
- `PanY`: Enables panning on the Y axis.
- `ZoomY`: Enables zooming on the Y axis (wheel, pinch and zoom by section).
- `X`: Enables zooming and panning on the X axis. Equivalent to `PanX | ZoomX`.
- `Y`: Enables zooming and panning on the Y axis. Equivalent to `PanY | ZoomY`.
- `Both`: Enables zooming and panning on both axes.
- `NoFit`: Disables the "Fit to Bounds" feature that forces the the chart to bounce back to the data bounds when zooming and panning finishes.
- `NoFit`: Disables the "Fit to Bounds" feature that forces the chart to bounce back to the data bounds when zooming and panning finishes.
- `NoZoomBySection`: Disables the "Zoom by Section" feature, this feature selects an area and zooms to this area, normally by right clicking or double tapping and then dragging to the end of the section.
- `InvertPanningPointerTrigger`: Inverts the panning and zoom by section pointer triggers, when the flag is present, panning is triggered by right clicking the chart, and zoom by section by left clicking (or inverts single/double taps on mobile).

Because each gesture has its own flag you can mix and match — for example `ZoomMode = ZoomAndPanMode.ZoomX | ZoomAndPanMode.PanY` enables zoom on the X axis and panning on the Y axis only, and `ZoomMode = ZoomAndPanMode.ZoomX` enables zoom on the X axis with no panning at all (useful on mobile where pan gestures can hide tooltips).

The [ZoomAndPanMode](https://lvcharts.com/api/{{ version }}/LiveChartsCore.Measure.ZoomAndPanMode) type is a flag enum,
so you can combine the options, for example, if you want to enable zooming on the `Both` axes and disable "Fit top Bounds"
so you can combine the options, for example, if you want to enable zooming on the `Both` axes and disable "Fit to Bounds"
you could set the `ZoomMode` property to:


Expand Down
10 changes: 10 additions & 0 deletions llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,16 @@ myChart.ZoomMode = ZoomAndPanMode.Both;
myChart.ZoomMode = ZoomAndPanMode.None;
```

Each gesture also has its own flag (`PanX`, `ZoomX`, `PanY`, `ZoomY`) so zoom and pan can be enabled independently. The composites `X = PanX | ZoomX`, `Y = PanY | ZoomY` and `Both = X | Y` keep their original pan+zoom semantics.

```csharp
// Allow zooming on the X axis but disable panning (e.g. so pan gestures don't hide tooltips on mobile):
myChart.ZoomMode = ZoomAndPanMode.ZoomX;

// Zoom on X, pan on Y:
myChart.ZoomMode = ZoomAndPanMode.ZoomX | ZoomAndPanMode.PanY;
```

`ZoomAndPanMode` is a flags enum — combine values:
```csharp
myChart.ZoomMode = ZoomAndPanMode.X | ZoomAndPanMode.NoFit | ZoomAndPanMode.NoZoomBySection;
Expand Down
45 changes: 24 additions & 21 deletions src/LiveChartsCore/CartesianChartEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public double[] ScaleUIPoint(LvcPoint point, int xAxisIndex = 0, int yAxisIndex
/// Zooms at the specified pivot.
/// </summary>
/// <param name="flags">
/// The flags, for example ZoomAndPanMode.X | ZoomAndPanMode.NoFit, will zoom only in the x axis
/// The flags, for example ZoomAndPanMode.ZoomX | ZoomAndPanMode.NoFit, will zoom only in the x axis
/// and will ignore the fit to bounds feature.
/// </param>
/// <param name="pivot">The pivot, is the reference point, the center where the zoom operation is calculated.</param>
Expand All @@ -164,11 +164,11 @@ public void Zoom(ZoomAndPanMode flags, LvcPoint pivot, ZoomDirection direction,
$"When the scale factor is defined, the zoom direction must be {nameof(ZoomDirection.DefinedByScaleFactor)}... " +
$"it just makes sense.");

if (flags.HasFlag(ZoomAndPanMode.X))
if (flags.HasFlag(ZoomAndPanMode.ZoomX))
foreach (var axis in XAxes)
ZoomAxis(axis, flags, pivot.X, direction, scaleFactor);

if (flags.HasFlag(ZoomAndPanMode.Y))
if (flags.HasFlag(ZoomAndPanMode.ZoomY))
foreach (var axis in YAxes)
ZoomAxis(axis, flags, pivot.Y, direction, scaleFactor);

Expand All @@ -179,17 +179,17 @@ public void Zoom(ZoomAndPanMode flags, LvcPoint pivot, ZoomDirection direction,
/// Pans with the specified delta.
/// </summary>
/// <param name="flags">
/// The flags, for example ZoomAndPanMode.X | ZoomAndPanMode.NoFit, will pan only in the x axis
/// The flags, for example ZoomAndPanMode.PanX | ZoomAndPanMode.NoFit, will pan only in the x axis
/// and will ignore the fit to bounds feature.
/// </param>
/// <param name="delta">The delta.</param>
public void Pan(ZoomAndPanMode flags, LvcPoint delta)
{
if (flags.HasFlag(ZoomAndPanMode.X))
if (flags.HasFlag(ZoomAndPanMode.PanX))
foreach (var axis in XAxes)
PanAxis(axis, flags, delta.X, true);

if (flags.HasFlag(ZoomAndPanMode.Y))
if (flags.HasFlag(ZoomAndPanMode.PanY))
foreach (var axis in YAxes)
PanAxis(axis, flags, delta.Y, true);
}
Expand All @@ -198,14 +198,14 @@ public void Pan(ZoomAndPanMode flags, LvcPoint delta)
/// Starts a zooming section operation at the specified point.
/// </summary>
/// <param name="flags">
/// The flags, for example ZoomAndPanMode.X | ZoomAndPanMode.NoFit, will zoom only in the x axis
/// The flags, for example ZoomAndPanMode.ZoomX | ZoomAndPanMode.NoFit, will zoom only in the x axis
/// and will ignore the fit to bounds feature.
/// </param>
/// <param name="point">The point to start the panning operation.</param>
/// <param name="point">The point where the zooming section operation starts.</param>
public void StartZoomingSection(ZoomAndPanMode flags, LvcPoint point)
{
var xMode = (flags & ZoomAndPanMode.X) == ZoomAndPanMode.X;
var yMode = (flags & ZoomAndPanMode.Y) == ZoomAndPanMode.Y;
var xMode = flags.HasFlag(ZoomAndPanMode.ZoomX);
var yMode = flags.HasFlag(ZoomAndPanMode.ZoomY);

if (flags.HasFlag(ZoomAndPanMode.NoZoomBySection) || (!xMode && !yMode))
return;
Expand Down Expand Up @@ -249,16 +249,16 @@ public void StartZoomingSection(ZoomAndPanMode flags, LvcPoint point)
/// in the UI, it does not apply the zoom yet.
/// </summary>
/// <param name="flags">
/// The flags, for example ZoomAndPanMode.X | ZoomAndPanMode.NoFit, will zoom only in the x axis
/// The flags, for example ZoomAndPanMode.ZoomX | ZoomAndPanMode.NoFit, will zoom only in the x axis
/// and will ignore the fit to bounds feature.
/// </param>
/// <param name="point">The point.</param>
public void GrowZoomingSection(ZoomAndPanMode flags, LvcPoint point)
{
if (_zoomingSection is null || _sectionZoomingStart is null) return;

var xMode = (flags & ZoomAndPanMode.X) == ZoomAndPanMode.X;
var yMode = (flags & ZoomAndPanMode.Y) == ZoomAndPanMode.Y;
var xMode = flags.HasFlag(ZoomAndPanMode.ZoomX);
var yMode = flags.HasFlag(ZoomAndPanMode.ZoomY);

var x = point.X;
var y = point.Y;
Expand All @@ -279,7 +279,7 @@ public void GrowZoomingSection(ZoomAndPanMode flags, LvcPoint point)
/// End the zooming section operation at the specified point, and applies the zoom.
/// </summary>
/// <param name="flags">
/// The flags, for example ZoomAndPanMode.X | ZoomAndPanMode.NoFit, will zoom only in the x axis
/// The flags, for example ZoomAndPanMode.ZoomX | ZoomAndPanMode.NoFit, will zoom only in the x axis
/// and will ignore the fit to bounds feature.
/// </param>
/// <param name="point">The point.</param>
Expand All @@ -301,11 +301,11 @@ public void EndZoomingSection(ZoomAndPanMode flags, LvcPoint point)
return;
}

if ((flags & ZoomAndPanMode.X) == ZoomAndPanMode.X)
if (flags.HasFlag(ZoomAndPanMode.ZoomX))
foreach (var axis in XAxes)
ZoomAxisBySection(axis, point.X);

if ((flags & ZoomAndPanMode.Y) == ZoomAndPanMode.Y)
if (flags.HasFlag(ZoomAndPanMode.ZoomY))
foreach (var axis in YAxes)
ZoomAxisBySection(axis, point.Y);

Expand Down Expand Up @@ -944,10 +944,10 @@ protected internal override void InvokePointerUp(LvcPoint point, bool isSecondar
var fits = !flags.HasFlag(ZoomAndPanMode.NoFit);
if (fits)
{
if (flags.HasFlag(ZoomAndPanMode.X))
if (flags.HasFlag(ZoomAndPanMode.PanX))
foreach (var axis in XAxes)
PanAxis(axis, flags, 0, false);
if (flags.HasFlag(ZoomAndPanMode.Y))
if (flags.HasFlag(ZoomAndPanMode.PanY))
foreach (var axis in YAxes)
PanAxis(axis, flags, 0, false);
}
Expand Down Expand Up @@ -986,7 +986,10 @@ internal void SubscribeSharedEvents(HashSet<CartesianChartEngine> instance)

private void FitAllOnZoom(ZoomAndPanMode flags)
{
if (_chartView.ZoomMode.HasFlag(ZoomAndPanMode.NoFit))
// Honor the flags passed to the public Zoom(...) call rather than the view's
// ZoomMode. Manual callers (e.g. a button-driven zoom) need NoFit to take
// effect even when the view's ZoomMode does not include it.
if (flags.HasFlag(ZoomAndPanMode.NoFit))
return;

void Fit(ICartesianAxis axis)
Expand All @@ -1009,11 +1012,11 @@ void Fit(ICartesianAxis axis)
axis.SetLimits(min, max);
}

if (flags.HasFlag(ZoomAndPanMode.X))
if (flags.HasFlag(ZoomAndPanMode.ZoomX))
foreach (var axis in XAxes)
Fit(axis);

if (flags.HasFlag(ZoomAndPanMode.Y))
if (flags.HasFlag(ZoomAndPanMode.ZoomY))
foreach (var axis in YAxes)
Fit(axis);
}
Expand Down
52 changes: 39 additions & 13 deletions src/LiveChartsCore/Measure/ZoomAndPanMode.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// The MIT License(MIT)
// The MIT License(MIT)
//
// Copyright(c) 2021 Alberto Rodriguez Orozco & LiveCharts Contributors
//
Expand Down Expand Up @@ -36,33 +36,59 @@ public enum ZoomAndPanMode
None = 0,

/// <summary>
/// Enables zooming and panning on the X axis and enables fitting to bounds.
/// Enables panning on the X axis.
/// </summary>
X = 1 << 0,
PanX = 1 << 0,

/// <summary>
/// Enables zooming and panning on the Y axis and enables fitting to bounds.
/// Enables zooming on the X axis (wheel, pinch and zoom by section).
/// </summary>
ZoomX = 1 << 1,
Comment thread
beto-rodriguez marked this conversation as resolved.

/// <summary>
/// Enables panning on the Y axis.
/// </summary>
PanY = 1 << 2,

/// <summary>
/// Enables zooming on the Y axis (wheel, pinch and zoom by section).
/// </summary>
Y = 1 << 1,
ZoomY = 1 << 3,

/// <summary>
/// Disables data bounds fitting when zooming or panning, this flag must be used in conjunction with
/// <see cref="X"/>, <see cref="Y"/>, or <see cref="Both"/> to have an effect.
/// any of the pan/zoom flags (<see cref="PanX"/>, <see cref="ZoomX"/>, <see cref="PanY"/>, <see cref="ZoomY"/>,
/// <see cref="X"/>, <see cref="Y"/>, or <see cref="Both"/>) to have an effect.
/// </summary>
NoFit = 1 << 2,
NoFit = 1 << 4,

/// <summary>
/// Disables the "Zoom by section" feature, which allows zooming in on a specific section of the chart.
/// </summary>
NoZoomBySection = 1 << 3,
NoZoomBySection = 1 << 5,

/// <summary>
/// When this flag is present the panning will be triggered using the right click on desktop devices and the touch-and-hold gesture on touch devices.
/// The "Zoom by section" feature will be triggered to the left click on desktop devices and the touch-and-hold gesture on touch devices,
/// this flag must be used in conjunction with
/// <see cref="X"/>, <see cref="Y"/>, or <see cref="Both"/> to have an effect.
/// When this flag is present the panning will be triggered using the right click on desktop devices and a double tap on touch devices.
/// The "Zoom by section" feature will be triggered by the left click on desktop devices and a single tap on touch devices,
/// this flag must be used in conjunction with any of the pan/zoom flags
/// (<see cref="PanX"/>, <see cref="ZoomX"/>, <see cref="PanY"/>, <see cref="ZoomY"/>,
/// <see cref="X"/>, <see cref="Y"/>, or <see cref="Both"/>) to have an effect.
/// </summary>
InvertPanningPointerTrigger = 1 << 6,

/// <summary>
/// Enables zooming and panning on the X axis and enables fitting to bounds.
/// Equivalent to <see cref="PanX"/> | <see cref="ZoomX"/>. To enable only one
/// gesture on the X axis use <see cref="PanX"/> or <see cref="ZoomX"/> directly.
/// </summary>
X = PanX | ZoomX,

/// <summary>
/// Enables zooming and panning on the Y axis and enables fitting to bounds.
/// Equivalent to <see cref="PanY"/> | <see cref="ZoomY"/>. To enable only one
/// gesture on the Y axis use <see cref="PanY"/> or <see cref="ZoomY"/> directly.
/// </summary>
InvertPanningPointerTrigger = 1 << 4,
Y = PanY | ZoomY,

/// <summary>
/// Enables zooming and panning on both axes and enables fitting to bounds.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@ private void OnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
// Only mark the event as handled when zoom is enabled; otherwise let it bubble
// so parent ScrollViewers still scroll and external subscribers still fire
// (Avalonia routed events skip subsequent handlers once Handled = true).
// Pan-only modes (PanX/PanY) must not swallow wheel either since the Zoom
// call below is a no-op for them.
// See https://github.com/Live-Charts/LiveCharts2/issues/1864.
if (ZoomMode == ZoomAndPanMode.None) return;
if ((ZoomMode & (ZoomAndPanMode.ZoomX | ZoomAndPanMode.ZoomY)) == 0) return;
e.Handled = true;

var c = (CartesianChartEngine)CoreChart;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ public SourceGenCartesianChart()

private void OnMouseWheel(object? sender, MouseEventArgs e)
{
// Don't swallow the wheel when zoom is not enabled — pan-only modes
// (PanX/PanY) and None must let the event bubble so containers can scroll.
if ((ZoomMode & (ZoomAndPanMode.ZoomX | ZoomAndPanMode.ZoomY)) == 0) return;

var c = (CartesianChartEngine)CoreChart;
var p = e.Location;
c.Zoom(ZoomMode, new LvcPoint(p.X, p.Y), e.Delta.Height > 0 ? ZoomDirection.ZoomIn : ZoomDirection.ZoomOut);
Expand Down
Loading
Loading