using System; using ElmSharp; using ESize = ElmSharp.Size; namespace Xamarin.Forms.Platform.Tizen { /// /// Renderer of a CarouselPage widget. /// public class CarouselPageRenderer : VisualElementRenderer { Box _outterLayout; Box _innerContainer; Scroller _scroller; int _pageIndex = 0; int _changedByScroll = 0; ESize _layoutBound; /// /// Invoked whenever the CarouselPage element has been changed in Xamarin. /// /// Event parameters. protected override void OnElementChanged(ElementChangedEventArgs e) { if (NativeView == null) { //Create box that holds toolbar and selected content _outterLayout = new Box(Forms.Context.MainWindow) { AlignmentX = -1, AlignmentY = -1, WeightX = 1, WeightY = 1, IsHorizontal = false, }; _outterLayout.Show(); _scroller = new Scroller(Forms.Context.MainWindow); _scroller.PageScrolled += OnScrolled; // Disables the visibility of the scrollbar in both directions: _scroller.HorizontalScrollBarVisiblePolicy = ElmSharp.ScrollBarVisiblePolicy.Invisible; _scroller.VerticalScrollBarVisiblePolicy = ElmSharp.ScrollBarVisiblePolicy.Invisible; // Sets the limit of scroll to one page maximum: _scroller.HorizontalPageScrollLimit = 1; _scroller.SetPageSize(1.0, 1.0); _scroller.SetAlignment(-1, -1); _scroller.SetWeight(1.0, 1.0); _scroller.Show(); _innerContainer = new Box(Forms.Context.MainWindow); _innerContainer.SetLayoutCallback(OnInnerLayoutUpdate); _innerContainer.SetAlignment(-1, -1); _innerContainer.SetWeight(1.0, 1.0); _innerContainer.Show(); _scroller.SetContent(_innerContainer); _outterLayout.PackEnd(_scroller); SetNativeControl(_outterLayout); } if (e.OldElement != null) { e.OldElement.CurrentPageChanged -= OnCurrentPageChanged; e.OldElement.PagesChanged -= OnPagesChanged; } if (e.NewElement != null) { Element.CurrentPageChanged += OnCurrentPageChanged; Element.PagesChanged += OnPagesChanged; UpdateCarouselContent(); } base.OnElementChanged(e); } /// /// Called just before the associated element is deleted. /// /// True if the memory release was requested on demand. protected override void Dispose(bool disposing) { if (disposing) { if (Element != null) { Element.CurrentPageChanged -= OnCurrentPageChanged; Element.PagesChanged -= OnPagesChanged; } _innerContainer = null; _scroller = null; } base.Dispose(disposing); } void OnInnerLayoutUpdate() { if (_layoutBound == _innerContainer.Geometry.Size) return; _layoutBound = _innerContainer.Geometry.Size; int baseX = _innerContainer.Geometry.X; Rect bound = _scroller.Geometry; int index = 0; foreach (var page in Element.Children) { var nativeView = Platform.GetRenderer(page).NativeView; bound.X = baseX + index * bound.Width; nativeView.Geometry = bound; index++; } _innerContainer.MinimumWidth = Element.Children.Count * bound.Width; if (_scroller.HorizontalPageIndex != _pageIndex) { _scroller.ScrollTo(_pageIndex, 0, false); } } /// /// Handles the process of switching between the displayed pages. /// /// An object originating the request /// Additional arguments to the event handler void OnCurrentPageChanged(object sender, EventArgs ea) { if (IsChangedByScroll()) return; if (Element.CurrentPage != Element.Children[_pageIndex]) { var previousPageIndex = _pageIndex; _pageIndex = Element.Children.IndexOf(Element.CurrentPage); if (previousPageIndex != _pageIndex) { // notify disappearing/appearing pages and scroll to the requested page (Element.Children[previousPageIndex] as IPageController)?.SendDisappearing(); _scroller.ScrollTo(_pageIndex, 0, false); (Element.CurrentPage as IPageController)?.SendAppearing(); } } } /// /// Handles the PageScrolled event of the _scroller. /// void OnScrolled(object sender, EventArgs e2) { _changedByScroll++; int previousIndex = _pageIndex; _pageIndex = _scroller.HorizontalPageIndex; if (previousIndex != _pageIndex) { (Element.CurrentPage as IPageController)?.SendDisappearing(); Element.CurrentPage = Element.Children[_pageIndex]; (Element.CurrentPage as IPageController)?.SendAppearing(); } _changedByScroll--; } /// /// Updates the content of the carousel. /// void UpdateCarouselContent() { _innerContainer.UnPackAll(); foreach (var page in Element.Children) { EvasObject nativeView = Platform.GetOrCreateRenderer(page).NativeView; _innerContainer.PackEnd(nativeView); // if possible, make the subpage focusable, this ensures that there is something // to focus on all pages and prevents the scroller from auto-scrolling to focused widget (nativeView as ElmSharp.Widget)?.AllowFocus(true); } _pageIndex = Element.Children.IndexOf(Element.CurrentPage); _scroller.ScrollTo(_pageIndex, 0, false); } /// /// Handles the notifications about content sub-page changes. /// void OnPagesChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { UpdateCarouselContent(); } bool IsChangedByScroll() { return _changedByScroll > 0; } } }