summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.MacOS/Renderers/ScrollViewRenderer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Platform.MacOS/Renderers/ScrollViewRenderer.cs')
-rw-r--r--Xamarin.Forms.Platform.MacOS/Renderers/ScrollViewRenderer.cs214
1 files changed, 214 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.MacOS/Renderers/ScrollViewRenderer.cs b/Xamarin.Forms.Platform.MacOS/Renderers/ScrollViewRenderer.cs
new file mode 100644
index 00000000..c41d1d2b
--- /dev/null
+++ b/Xamarin.Forms.Platform.MacOS/Renderers/ScrollViewRenderer.cs
@@ -0,0 +1,214 @@
+using System;
+using System.ComponentModel;
+using AppKit;
+using RectangleF = CoreGraphics.CGRect;
+using ObjCRuntime;
+using Foundation;
+
+// ReSharper disable UnusedMember.Local
+// ReSharper disable UnusedParameter.Local
+
+namespace Xamarin.Forms.Platform.MacOS
+{
+ public class ScrollViewRenderer : NSScrollView, IVisualElementRenderer
+ {
+ EventTracker _events;
+ VisualElementTracker _tracker;
+ ScrollToRequestedEventArgs _requestedScroll;
+ IVisualElementRenderer _contentRenderer;
+
+ public ScrollViewRenderer() : base(RectangleF.Empty)
+ {
+ DrawsBackground = false;
+ ContentView.PostsBoundsChangedNotifications = true;
+ NSNotificationCenter.DefaultCenter.AddObserver(this, new Selector(nameof(UpdateScrollPosition)),
+ BoundsChangedNotification, ContentView);
+ }
+
+ IScrollViewController Controller => Element as IScrollViewController;
+
+ public VisualElement Element { get; private set; }
+
+ public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
+
+ public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ return NativeView.GetSizeRequest(widthConstraint, heightConstraint, 44, 44);
+ }
+
+ public NSView NativeView => this;
+
+ public void SetElement(VisualElement element)
+ {
+ _requestedScroll = null;
+ var oldElement = Element;
+ Element = element;
+
+ if (oldElement != null)
+ {
+ oldElement.PropertyChanged -= HandlePropertyChanged;
+ ((IScrollViewController)oldElement).ScrollToRequested -= OnScrollToRequested;
+ }
+
+ if (element != null)
+ {
+ element.PropertyChanged += HandlePropertyChanged;
+ ((IScrollViewController)element).ScrollToRequested += OnScrollToRequested;
+ if (_tracker == null)
+ {
+ PackContent();
+
+ _events = new EventTracker(this);
+ _events.LoadEvents(this);
+
+ _tracker = new VisualElementTracker(this);
+ _tracker.NativeControlUpdated += OnNativeControlUpdated;
+ }
+
+ UpdateContentSize();
+ UpdateBackgroundColor();
+
+ OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
+ }
+ }
+
+ public void SetElementSize(Size size)
+ {
+ Xamarin.Forms.Layout.LayoutChildIntoBoundingRegion(Element,
+ new Rectangle(Element.X, Element.Y, size.Width, size.Height));
+ }
+
+ public NSViewController ViewController => null;
+
+ public override void Layout()
+ {
+ base.Layout();
+ LayoutSubviews();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (_tracker == null)
+ return;
+
+ SetElement(null);
+
+ _tracker.NativeControlUpdated -= OnNativeControlUpdated;
+ _tracker.Dispose();
+ _tracker = null;
+
+ _events.Dispose();
+ _events = null;
+
+ ClearContentRenderer();
+
+ //NSNotificationCenter.DefaultCenter.RemoveObserver(this, BoundsChangedNotification);
+ }
+
+ base.Dispose(disposing);
+ }
+
+ void OnElementChanged(VisualElementChangedEventArgs e)
+ {
+ ElementChanged?.Invoke(this, e);
+ }
+
+ void PackContent()
+ {
+ ClearContentRenderer();
+
+ if (Controller.Children.Count == 0 || !(Controller.Children[0] is VisualElement))
+ return;
+
+ var content = (VisualElement)Controller.Children[0];
+ if (Platform.GetRenderer(content) == null)
+ Platform.SetRenderer(content, Platform.CreateRenderer(content));
+
+ _contentRenderer = Platform.GetRenderer(content);
+
+ DocumentView = _contentRenderer.NativeView;
+ }
+
+ void LayoutSubviews()
+ {
+ if (_requestedScroll != null && Superview != null)
+ {
+ var request = _requestedScroll;
+ _requestedScroll = null;
+ OnScrollToRequested(this, request);
+ }
+ }
+
+ void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == ScrollView.ContentSizeProperty.PropertyName)
+ UpdateContentSize();
+ else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+ UpdateBackgroundColor();
+ }
+
+ void HandleScrollAnimationEnded(object sender, EventArgs e)
+ {
+ Controller.SendScrollFinished();
+ }
+
+ void HandleScrolled(object sender, EventArgs e)
+ {
+ UpdateScrollPosition();
+ }
+
+ void OnNativeControlUpdated(object sender, EventArgs eventArgs)
+ {
+ UpdateContentSize();
+ }
+
+ void OnScrollToRequested(object sender, ScrollToRequestedEventArgs e)
+ {
+ if (Superview == null)
+ {
+ _requestedScroll = e;
+ return;
+ }
+
+ Point scrollPoint = (e.Mode == ScrollToMode.Position)
+ ? new Point(e.ScrollX, Element.Height - e.ScrollY)
+ : Controller.GetScrollPositionForElement(e.Element as VisualElement, e.Position);
+
+ (DocumentView as NSView)?.ScrollPoint(scrollPoint.ToPointF());
+
+ Controller.SendScrollFinished();
+ }
+
+ void UpdateBackgroundColor()
+ {
+ BackgroundColor = Element.BackgroundColor.ToNSColor(Color.Transparent);
+ }
+
+ void UpdateContentSize()
+ {
+ if (_contentRenderer == null)
+ return;
+ var contentSize = ((ScrollView)Element).ContentSize.ToSizeF();
+ if (!contentSize.IsEmpty)
+ _contentRenderer.NativeView.Frame = new RectangleF(0, Element.Height - contentSize.Height, contentSize.Width,
+ contentSize.Height);
+ }
+
+ [Export(nameof(UpdateScrollPosition))]
+ void UpdateScrollPosition()
+ {
+ var convertedPoint = (DocumentView as NSView)?.ConvertPointFromView(ContentView.Bounds.Location, ContentView);
+ if (convertedPoint.HasValue)
+ Controller.SetScrolledPosition(Math.Max(0, convertedPoint.Value.X), Math.Max(0, convertedPoint.Value.Y));
+ }
+
+ void ClearContentRenderer()
+ {
+ _contentRenderer?.NativeView?.RemoveFromSuperview();
+ _contentRenderer?.Dispose();
+ _contentRenderer = null;
+ }
+ }
+} \ No newline at end of file