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;
}
}
}