Skip to content

Commit 14b1b5e

Browse files
authored
Merge pull request #1316 from unoplatform/dev/xygu/20250107/zcc-negative-canvas
fix(zcc): add support for negative canvas coord space
2 parents ee5d9f9 + 6c6b16f commit 14b1b5e

File tree

2 files changed

+82
-37
lines changed

2 files changed

+82
-37
lines changed

src/Uno.Toolkit.UI/Controls/ZoomContentControl/ZoomContentControl.cs

+67-37
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ T FindTemplatePart<T>(string name) where T : class =>
8989
_translation = FindTemplatePart<TranslateTransform>(TemplateParts.TranslateTransform);
9090

9191
ResetViewport();
92+
UpdateScrollDetails();
9293
}
9394

9495
protected override void OnContentChanged(object oldContent, object newContent)
@@ -98,6 +99,8 @@ protected override void OnContentChanged(object oldContent, object newContent)
9899
{
99100
fe.Loaded += OnContentLoaded;
100101
fe.SizeChanged += OnContentSizeChanged;
102+
UpdateScrollDetails();
103+
101104
_contentSubscriptions.Disposable = Disposable.Create(() =>
102105
{
103106
fe.Loaded -= OnContentLoaded;
@@ -107,19 +110,18 @@ protected override void OnContentChanged(object oldContent, object newContent)
107110

108111
void OnContentLoaded(object sender, RoutedEventArgs e)
109112
{
113+
if (Content is Canvas)
114+
{
115+
UpdateScrollDetails();
116+
}
110117
if (AutoFitToCanvas)
111118
{
112119
FitToCanvas();
113120
}
114121
}
115122
void OnContentSizeChanged(object sender, SizeChangedEventArgs e)
116123
{
117-
_contentSize = new Size(fe.ActualWidth, fe.ActualHeight);
118-
HorizontalZoomCenter = _contentSize.Width / 2;
119-
VerticalZoomCenter = _contentSize.Height / 2;
120-
121-
UpdateScrollBars();
122-
UpdateScrollVisibility();
124+
UpdateScrollDetails();
123125
}
124126
}
125127

@@ -152,28 +154,19 @@ private async void OnZoomLevelChanged()
152154
}
153155

154156
UpdateScrollBars();
155-
UpdateScrollVisibility();
156157
await RaiseRenderedContentUpdated();
157158
}
158159

159-
private void UpdateScrollVisibility()
160+
private async void UpdateScrollDetails()
160161
{
161-
if (Viewport is { } vp)
162-
{
163-
ToggleScrollBarVisibility(_scrollH, vp.ActualWidth < ScrollExtentWidth);
164-
ToggleScrollBarVisibility(_scrollV, vp.ActualHeight < ScrollExtentHeight);
165-
}
166-
167-
void ToggleScrollBarVisibility(ScrollBar? sb, bool value)
162+
if (Content is FrameworkElement fe)
168163
{
169-
if (sb is null) return;
170-
171-
// Showing/hiding the ScrollBar(s)could cause the ContentPresenter to move as it re-centers.
172-
// This adds unnecessary complexity for the zooming logics as we need to preserve the focal point
173-
// under the cursor position or the pinch center point after zooming.
174-
// To avoid all that, we just make them permanently there for layout calculation.
175-
sb.IsEnabled = value;
176-
sb.Opacity = value ? 1 : 0;
164+
_contentSize = new Size(fe.ActualWidth, fe.ActualHeight);
165+
HorizontalZoomCenter = _contentSize.Width / 2;
166+
VerticalZoomCenter = _contentSize.Height / 2;
167+
168+
UpdateScrollBars();
169+
await RaiseRenderedContentUpdated();
177170
}
178171
}
179172

@@ -207,20 +200,57 @@ private void UpdateScrollBars()
207200
{
208201
if (Viewport is { } vp)
209202
{
210-
var scrollableWidth = Math.Max(0, ScrollExtentWidth - vp.ActualWidth);
211-
var scrollableHeight = Math.Max(0, ScrollExtentHeight - vp.ActualHeight);
212-
213-
// since the content is always centered, we need to able to scroll both way equally:
214-
// [Content-Content-Content]
215-
// [=======[Viewport]======]
216-
HorizontalMaxScroll = scrollableWidth / 2;
217-
HorizontalMinScroll = -HorizontalMaxScroll;
218-
VerticalMaxScroll = scrollableHeight / 2;
219-
VerticalMinScroll = -VerticalMaxScroll;
220-
221-
// update size of thumb
222-
if (_scrollH is { }) _scrollH.ViewportSize = vp.ActualWidth;
223-
if (_scrollV is { }) _scrollV.ViewportSize = vp.ActualHeight;
203+
if (Content is Canvas { Children: { Count: > 0 } } canvas)
204+
{
205+
var realm = canvas.Children
206+
.Select(x => new Rect(x.ActualOffset.X, x.ActualOffset.Y, x.ActualSize.X, x.ActualSize.Y))
207+
.Aggregate(RectHelper.Union);
208+
if (realm != default)
209+
{
210+
realm = realm.Multiply(ZoomLevel).Inflate(AdditionalMargin);
211+
212+
HorizontalMinScroll = -realm.Right + (vp.ActualWidth / 2);
213+
HorizontalMaxScroll = -realm.Left - (vp.ActualWidth / 2);
214+
215+
VerticalMinScroll = realm.Top + (vp.ActualHeight / 2);
216+
VerticalMaxScroll = realm.Bottom - (vp.ActualHeight / 2);
217+
}
218+
else
219+
{
220+
HorizontalMaxScroll = HorizontalMinScroll = 0;
221+
VerticalMaxScroll = VerticalMinScroll = 0;
222+
}
223+
}
224+
else
225+
{
226+
var scrollableWidth = Math.Max(0, ScrollExtentWidth - vp.ActualWidth);
227+
var scrollableHeight = Math.Max(0, ScrollExtentHeight - vp.ActualHeight);
228+
229+
// since the content is always centered, we need to able to scroll both way equally:
230+
// [Content-Content-Content]
231+
// [=======[Viewport]======]
232+
HorizontalMaxScroll = scrollableWidth / 2;
233+
HorizontalMinScroll = -HorizontalMaxScroll;
234+
VerticalMaxScroll = scrollableHeight / 2;
235+
VerticalMinScroll = -VerticalMaxScroll;
236+
}
237+
238+
Update(_scrollH, HorizontalMinScroll < HorizontalMaxScroll, vp.ActualWidth);
239+
Update(_scrollV, VerticalMinScroll < VerticalMaxScroll, vp.ActualHeight);
240+
void Update(ScrollBar? sb, bool shown, double thumbSize)
241+
{
242+
if (sb is null) return;
243+
244+
// update size of thumb
245+
sb.ViewportSize = thumbSize;
246+
247+
// Showing/hiding the ScrollBar(s)could cause the ContentPresenter to move as it re-centers.
248+
// This adds unnecessary complexity for the zooming logics as we need to preserve the focal point
249+
// under the cursor position or the pinch center point after zooming.
250+
// To avoid all that, we just make them permanently there for layout calculation.
251+
sb.IsEnabled = shown;
252+
sb.Opacity = shown ? 1 : 0;
253+
}
224254
}
225255
}
226256

src/Uno.Toolkit.UI/Extensions/RectExtensions.cs

+15
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,25 @@
55
using System.Threading.Tasks;
66
using Windows.Foundation;
77

8+
#if IS_WINUI
9+
using Microsoft.UI.Xaml;
10+
#else
11+
using Windows.UI.Xaml;
12+
#endif
13+
814
namespace Uno.Toolkit.UI
915
{
1016
internal static class RectExtensions
1117
{
1218
public static bool IsEmptyOrZero(this Rect rect) => rect is { Width: 0, Height: 0 };
19+
20+
public static Rect Multiply(this Rect x, double value) => new Rect(x.X * value, x.Y * value, x.Width * value, x.Height * value);
21+
22+
public static Rect Inflate(this Rect x, Thickness value) => new Rect(
23+
x.X - value.Left,
24+
x.Y - value.Top,
25+
x.Width + (value.Left + value.Right),
26+
x.Height + (value.Top + value.Bottom)
27+
);
1328
}
1429
}

0 commit comments

Comments
 (0)