diff --git a/Handlers/FlexLayoutHandler.cs b/Handlers/FlexLayoutHandler.cs index 9ff66e5..fb9d678 100644 --- a/Handlers/FlexLayoutHandler.cs +++ b/Handlers/FlexLayoutHandler.cs @@ -97,7 +97,7 @@ public class FlexLayoutHandler : LayoutHandler Microsoft.Maui.Layouts.FlexAlignContent.Stretch => FlexAlignContent.Stretch, Microsoft.Maui.Layouts.FlexAlignContent.SpaceBetween => FlexAlignContent.SpaceBetween, Microsoft.Maui.Layouts.FlexAlignContent.SpaceAround => FlexAlignContent.SpaceAround, - Microsoft.Maui.Layouts.FlexAlignContent.SpaceEvenly => FlexAlignContent.SpaceAround, + Microsoft.Maui.Layouts.FlexAlignContent.SpaceEvenly => FlexAlignContent.SpaceEvenly, _ => FlexAlignContent.Stretch, }; } diff --git a/Types/FlexAlignContent.cs b/Types/FlexAlignContent.cs index 16d6473..9c87869 100644 --- a/Types/FlexAlignContent.cs +++ b/Types/FlexAlignContent.cs @@ -1,5 +1,9 @@ namespace Microsoft.Maui.Platform; +/// +/// Specifies how flex lines are aligned within the flex container. +/// Matches Microsoft.Maui.Layouts.FlexAlignContent enum. +/// public enum FlexAlignContent { Start, @@ -7,5 +11,6 @@ public enum FlexAlignContent End, Stretch, SpaceBetween, - SpaceAround + SpaceAround, + SpaceEvenly } diff --git a/Views/SkiaScrollView.cs b/Views/SkiaScrollView.cs index d9c1d16..4544059 100644 --- a/Views/SkiaScrollView.cs +++ b/Views/SkiaScrollView.cs @@ -603,9 +603,113 @@ public class SkiaScrollView : SkiaView /// public void ScrollTo(float x, float y, bool animated = false) { - // TODO: Implement animation - ScrollX = x; - ScrollY = y; + if (animated) + { + // Animated scroll - use async version + _ = ScrollToAsync(x, y, animated); + } + else + { + ScrollX = x; + ScrollY = y; + } + } + + /// + /// Scrolls to the specified position asynchronously with optional animation. + /// Matches MAUI's ScrollView.ScrollToAsync signature. + /// + public async Task ScrollToAsync(double x, double y, bool animated) + { + if (!animated) + { + ScrollX = (float)x; + ScrollY = (float)y; + return; + } + + // Animate scroll over 250ms (standard MAUI animation duration) + const int animationDurationMs = 250; + const int frameIntervalMs = 16; // ~60fps + int steps = animationDurationMs / frameIntervalMs; + + float startX = _scrollX; + float startY = _scrollY; + float targetX = (float)x; + float targetY = (float)y; + + for (int i = 1; i <= steps; i++) + { + float progress = (float)i / steps; + // Use ease-out cubic for smooth deceleration + float easedProgress = 1f - (1f - progress) * (1f - progress) * (1f - progress); + + _scrollX = startX + (targetX - startX) * easedProgress; + _scrollY = startY + (targetY - startY) * easedProgress; + + Invalidate(); + await Task.Delay(frameIntervalMs); + } + + // Ensure we end at exact target position + ScrollX = targetX; + ScrollY = targetY; + } + + /// + /// Scrolls to make the specified element visible. + /// Matches MAUI's ScrollView.ScrollToAsync signature for elements. + /// + public Task ScrollToAsync(SkiaView element, ScrollToPosition position, bool animated) + { + if (element == null || _content == null) + return Task.CompletedTask; + + var elementBounds = element.Bounds; + float targetX = _scrollX; + float targetY = _scrollY; + + // Calculate viewport dimensions + float viewportWidth = Bounds.Width; + float viewportHeight = Bounds.Height; + + switch (position) + { + case ScrollToPosition.Start: + targetX = elementBounds.Left; + targetY = elementBounds.Top; + break; + + case ScrollToPosition.Center: + targetX = elementBounds.Left - (viewportWidth - elementBounds.Width) / 2; + targetY = elementBounds.Top - (viewportHeight - elementBounds.Height) / 2; + break; + + case ScrollToPosition.End: + targetX = elementBounds.Right - viewportWidth; + targetY = elementBounds.Bottom - viewportHeight; + break; + + case ScrollToPosition.MakeVisible: + default: + // Only scroll if element is not fully visible + if (elementBounds.Left < _scrollX) + targetX = elementBounds.Left; + else if (elementBounds.Right > _scrollX + viewportWidth) + targetX = elementBounds.Right - viewportWidth; + + if (elementBounds.Top < _scrollY) + targetY = elementBounds.Top; + else if (elementBounds.Bottom > _scrollY + viewportHeight) + targetY = elementBounds.Bottom - viewportHeight; + break; + } + + // Clamp to valid scroll range + targetX = Math.Clamp(targetX, 0, ScrollableWidth); + targetY = Math.Clamp(targetY, 0, ScrollableHeight); + + return ScrollToAsync(targetX, targetY, animated); } /// @@ -773,6 +877,33 @@ public enum ScrollBarVisibility Auto } +/// +/// Specifies the position within the ScrollView to scroll an element to. +/// Matches Microsoft.Maui.ScrollToPosition enum. +/// +public enum ScrollToPosition +{ + /// + /// Scroll so the element is just visible (minimal scroll). + /// + MakeVisible, + + /// + /// Scroll so the element is at the start of the viewport. + /// + Start, + + /// + /// Scroll so the element is at the center of the viewport. + /// + Center, + + /// + /// Scroll so the element is at the end of the viewport. + /// + End +} + /// /// Event args for scroll events. ///