3 using System.Threading.Tasks;
4 using System.Collections.Generic;
5 using Xamarin.Forms.Internals;
6 using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
8 using EButton = ElmSharp.Button;
9 using EToolbar = ElmSharp.Toolbar;
10 using EToolbarItem = ElmSharp.ToolbarItem;
11 using Specific = Xamarin.Forms.PlatformConfiguration.TizenSpecific.NavigationPage;
12 using SpecificPage = Xamarin.Forms.PlatformConfiguration.TizenSpecific.Page;
14 namespace Xamarin.Forms.Platform.Tizen
16 public class NavigationPageRenderer : VisualElementRenderer<NavigationPage>
18 enum ToolbarButtonPosition
24 const string PartTitle = "default";
25 const string PartBackButton = "elm.swallow.prev_btn";
26 const string PartLeftToolbar = "title_left_btn";
27 const string PartRightToolbar = "title_right_btn";
28 const string PartNavigationBar = "navigationbar";
29 const string StyleBackButton = "naviframe/back_btn/default";
30 const string StyleNavigationBar = "navigationbar";
32 readonly List<Widget> _naviItemContentPartList = new List<Widget>();
33 Naviframe _naviFrame = null;
34 Page _previousPage = null;
35 TaskCompletionSource<bool> _currentTaskSource = null;
36 ToolbarTracker _toolbarTracker = null;
37 IDictionary<Page, NaviItem> _naviItemMap;
39 Page CurrentPage => Element.CurrentPage;
40 Page PreviousPage => Element.Navigation.NavigationStack.Count > 1 ? Element.Navigation.NavigationStack[Element.Navigation.NavigationStack.Count - 2] : null;
41 NaviItem CurrentNaviItem => _naviFrame.NavigationStack.Count > 0 ? _naviFrame.NavigationStack.Last() : null;
42 NaviItem PreviousNaviItem => _naviFrame.NavigationStack.Count > 1 ? _naviFrame.NavigationStack[_naviFrame.NavigationStack.Count - 2] : null;
44 protected override void Dispose(bool disposing)
48 if (_naviFrame != null)
50 _naviFrame.AnimationFinished -= OnAnimationFinished;
52 if (_toolbarTracker != null)
54 _toolbarTracker.CollectionChanged -= OnToolbarCollectionChanged;
57 base.Dispose(disposing);
60 protected override void OnElementChanged(ElementChangedEventArgs<NavigationPage> e)
62 if (_naviFrame == null)
64 _naviFrame = new Naviframe(Forms.NativeParent);
65 _naviFrame.PreserveContentOnPop = true;
66 _naviFrame.DefaultBackButtonEnabled = false;
67 _naviFrame.AnimationFinished += OnAnimationFinished;
69 SetNativeView(_naviFrame);
70 _naviItemMap = new Dictionary<Page, NaviItem>();
73 if (_toolbarTracker == null)
75 _toolbarTracker = new ToolbarTracker();
76 _toolbarTracker.CollectionChanged += OnToolbarCollectionChanged;
79 if (e.OldElement != null)
81 var navigation = e.OldElement as INavigationPageController;
82 navigation.PopRequested -= OnPopRequested;
83 navigation.PopToRootRequested -= OnPopToRootRequested;
84 navigation.PushRequested -= OnPushRequested;
85 navigation.RemovePageRequested -= OnRemovePageRequested;
86 navigation.InsertPageBeforeRequested -= OnInsertPageBeforeRequested;
88 var pageController = e.OldElement as IPageController;
89 pageController.InternalChildren.CollectionChanged -= OnPageCollectionChanged;
92 if (e.NewElement != null)
94 var navigation = e.NewElement as INavigationPageController;
95 navigation.PopRequested += OnPopRequested;
96 navigation.PopToRootRequested += OnPopToRootRequested;
97 navigation.PushRequested += OnPushRequested;
98 navigation.RemovePageRequested += OnRemovePageRequested;
99 navigation.InsertPageBeforeRequested += OnInsertPageBeforeRequested;
101 _toolbarTracker.Target = e.NewElement;
102 _previousPage = e.NewElement.CurrentPage;
104 base.OnElementChanged(e);
107 protected override void OnElementReady()
109 base.OnElementReady();
110 var pageController = Element as IPageController;
111 pageController.InternalChildren.CollectionChanged += OnPageCollectionChanged;
113 foreach (Page page in pageController.InternalChildren)
115 _naviItemMap[page] = _naviFrame.Push(CreateNavItem(page), SpanTitle(page.Title));
116 page.PropertyChanged += NavigationBarPropertyChangedHandler;
118 UpdateHasNavigationBar(page);
122 protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
124 base.OnElementPropertyChanged(sender, e);
126 if (e.PropertyName == NavigationPage.CurrentPageProperty.PropertyName)
128 Device.BeginInvokeOnMainThread(() =>
130 (_previousPage as IPageController)?.SendDisappearing();
131 _previousPage = Element.CurrentPage;
132 (_previousPage as IPageController)?.SendAppearing();
135 else if (e.PropertyName == NavigationPage.BarTextColorProperty.PropertyName)
136 UpdateTitle(CurrentPage);
137 // Tizen does not support 'Tint', but only 'BarBackgroundColor'
138 else if (e.PropertyName == NavigationPage.BarBackgroundColorProperty.PropertyName)
139 UpdateBarBackgroundColor(CurrentNaviItem);
140 else if (e.PropertyName == Specific.HasBreadCrumbsBarProperty.PropertyName)
141 UpdateBreadCrumbsBar(CurrentNaviItem);
145 void OnPageCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
147 if (e.OldItems != null)
148 foreach (Page page in e.OldItems)
149 page.PropertyChanged -= NavigationBarPropertyChangedHandler;
150 if (e.NewItems != null)
151 foreach (Page page in e.NewItems)
152 page.PropertyChanged += NavigationBarPropertyChangedHandler;
155 void OnToolbarCollectionChanged(object sender, EventArgs eventArgs)
157 UpdateToolbarItem(Element.CurrentPage);
160 void NavigationBarPropertyChangedHandler(object sender, System.ComponentModel.PropertyChangedEventArgs e)
162 // this handler is invoked only for child pages (contained on a navigation stack)
163 if (e.PropertyName == NavigationPage.HasNavigationBarProperty.PropertyName)
164 UpdateHasNavigationBar(sender as Page);
165 else if (e.PropertyName == NavigationPage.HasBackButtonProperty.PropertyName ||
166 e.PropertyName == NavigationPage.BackButtonTitleProperty.PropertyName)
167 UpdateHasBackButton(sender as Page);
168 else if (e.PropertyName == Page.TitleProperty.PropertyName)
169 UpdateTitle(sender as Page);
170 else if (e.PropertyName == SpecificPage.BreadCrumbProperty.PropertyName)
171 UpdateBreadCrumbsBar(GetNaviItemForPage(sender as Page));
174 void UpdateHasNavigationBar(Page page)
176 NaviItem item = GetNaviItemForPage(page);
177 if (NavigationPage.GetTitleView(page) != null)
179 item.TitleBarVisible = false;
180 Native.TitleViewPage tvPage = item.Content as Native.TitleViewPage;
183 tvPage.HasNavigationBar = (bool)page.GetValue(NavigationPage.HasNavigationBarProperty);
187 item.TitleBarVisible = (bool)page.GetValue(NavigationPage.HasNavigationBarProperty);
188 UpdateToolbarItem(page, item);
189 UpdateBarBackgroundColor(item);
190 UpdateBreadCrumbsBar(item);
193 void UpdateToolbarItem(Page page, NaviItem item = null)
196 item = GetNaviItemForPage(page);
198 if (_naviFrame.NavigationStack.Count == 0 || item == null || item != _naviFrame.NavigationStack.Last())
201 Native.Button rightButton = GetToolbarButton(ToolbarButtonPosition.Right);
202 item.SetPartContent(PartRightToolbar, rightButton);
204 Native.Button leftButton = GetToolbarButton(ToolbarButtonPosition.Left);
205 item.SetPartContent(PartLeftToolbar, leftButton);
206 UpdateHasBackButton(page, item);
209 void UpdateHasBackButton(Page page, NaviItem item = null)
212 item = GetNaviItemForPage(page);
214 EButton button = null;
216 if ((bool)page.GetValue(NavigationPage.HasBackButtonProperty) && _naviFrame.NavigationStack.Count > 1)
218 button = CreateNavigationButton((string)page.GetValue(NavigationPage.BackButtonTitleProperty));
220 item.SetPartContent(PartBackButton, button);
223 void UpdateTitle(Page page, NaviItem item = null)
226 item = GetNaviItemForPage(page);
228 item.SetPartText(PartTitle, SpanTitle(page.Title));
231 string SpanTitle(string Title)
233 Native.Span span = new Native.Span
236 HorizontalTextAlignment = Native.TextAlignment.Center,
237 ForegroundColor = Element.BarTextColor.ToNative()
239 return span.GetMarkupText();
242 void UpdateBarBackgroundColor(NaviItem item)
244 item.TitleBarBackgroundColor = Element.BarBackgroundColor.ToNative();
247 void UpdateNavigationBar(Page page, NaviItem item = null)
250 item = GetNaviItemForPage(page);
252 UpdateTitle(page, item);
253 UpdateBarBackgroundColor(item);
256 void UpdateBreadCrumbsBar(NaviItem item)
258 if (Element.OnThisPlatform().HasBreadCrumbsBar())
260 item.Style = StyleNavigationBar;
261 item.SetPartContent(PartNavigationBar, GetBreadCrumbsBar());
265 item.SetPartContent(PartNavigationBar, null, false);
269 EButton CreateNavigationButton(string text)
271 EButton button = new EButton(Forms.NativeParent);
272 button.Clicked += (sender, e) =>
274 if (!Element.SendBackButtonPressed())
275 Forms.Context.Exit();
278 button.Style = StyleBackButton;
281 _naviItemContentPartList.Add(button);
282 button.Deleted += NaviItemPartContentDeletedHandler;
287 void NaviItemPartContentDeletedHandler(object sender, EventArgs e)
289 _naviItemContentPartList.Remove(sender as Widget);
292 NaviItem GetNaviItemForPage(Page page)
295 if (_naviItemMap.TryGetValue(page, out item))
302 Native.Button GetToolbarButton(ToolbarButtonPosition position)
304 ToolbarItem item = _toolbarTracker.ToolbarItems.Where(
305 i => (position == ToolbarButtonPosition.Right && i.Order <= ToolbarItemOrder.Primary)
306 || (position == ToolbarButtonPosition.Left && i.Order == ToolbarItemOrder.Secondary))
307 .OrderBy(i => i.Priority).FirstOrDefault();
309 if (item == default(ToolbarItem))
312 Native.ToolbarItemButton button = new Native.ToolbarItemButton(item);
316 EToolbar GetBreadCrumbsBar()
318 EToolbar toolbar = new EToolbar(Forms.NativeParent)
320 Style = StyleNavigationBar,
323 ShrinkMode = ToolbarShrinkMode.Scroll
326 foreach (var p in Element.Navigation.NavigationStack)
328 string breadCrumb = p.OnThisPlatform().GetBreadCrumb();
329 if (!string.IsNullOrEmpty(breadCrumb))
331 EToolbarItem toolbarItem = toolbar.Append(breadCrumb);
332 toolbarItem.Selected += (s, e) =>
334 var copyOfStack = Element.Navigation.NavigationStack.Reverse().Skip(1);
335 foreach (var lp in copyOfStack)
338 Element.Navigation.RemovePage(lp);
340 if (Element.Navigation.NavigationStack.Last() != p)
341 Element.Navigation.PopAsync();
349 void OnPopRequested(object sender, NavigationRequestedEventArgs nre)
351 if ((Element as IPageController).InternalChildren.Count == _naviFrame.NavigationStack.Count)
353 nre.Page?.SendDisappearing();
354 UpdateNavigationBar(PreviousPage, PreviousNaviItem);
360 _currentTaskSource = new TaskCompletionSource<bool>();
361 nre.Task = _currentTaskSource.Task;
363 // There is no TransitionFinished (AnimationFinished) event after Pop the last page
364 if (_naviFrame.NavigationStack.Count == 0)
365 CompleteCurrentNavigationTask();
369 CurrentNaviItem?.Delete();
372 if (_naviItemMap.ContainsKey(nre.Page))
373 _naviItemMap.Remove(nre.Page);
377 void OnPopToRootRequested(object sender, NavigationRequestedEventArgs nre)
379 List<NaviItem> copyOfStack = new List<NaviItem>(_naviFrame.NavigationStack);
380 NaviItem rootItem = copyOfStack.FirstOrDefault();
381 NaviItem topItem = copyOfStack.LastOrDefault();
383 foreach (NaviItem naviItem in copyOfStack)
384 if (naviItem != rootItem && naviItem != topItem)
387 if (topItem != rootItem)
389 UpdateNavigationBar(Element.Navigation.NavigationStack.Last(), rootItem);
394 _currentTaskSource = new TaskCompletionSource<bool>();
395 nre.Task = _currentTaskSource.Task;
401 _naviItemMap.Clear();
402 _naviItemMap[Element.Navigation.NavigationStack.Last()] = rootItem;
405 void OnPushRequested(object sender, NavigationRequestedEventArgs nre)
407 if (nre.Animated || _naviFrame.NavigationStack.Count == 0)
409 _naviItemMap[nre.Page] = _naviFrame.Push(CreateNavItem(nre.Page), SpanTitle(nre.Page.Title));
410 _currentTaskSource = new TaskCompletionSource<bool>();
411 nre.Task = _currentTaskSource.Task;
413 // There is no TransitionFinished (AnimationFinished) event after the first Push
414 if (_naviFrame.NavigationStack.Count == 1)
415 CompleteCurrentNavigationTask();
419 _naviItemMap[nre.Page] = _naviFrame.InsertAfter(_naviFrame.NavigationStack.Last(), CreateNavItem(nre.Page), SpanTitle(nre.Page.Title));
421 UpdateHasNavigationBar(nre.Page);
424 void OnRemovePageRequested(object sender, NavigationRequestedEventArgs nre)
426 GetNaviItemForPage(nre.Page).Delete();
427 if (_naviItemMap.ContainsKey(nre.Page))
428 _naviItemMap.Remove(nre.Page);
431 void OnInsertPageBeforeRequested(object sender, NavigationRequestedEventArgs nre)
433 if (nre.BeforePage == null)
434 throw new ArgumentException("BeforePage is null");
435 if (nre.Page == null)
436 throw new ArgumentException("Page is null");
438 _naviItemMap[nre.Page] = _naviFrame.InsertBefore(GetNaviItemForPage(nre.BeforePage), CreateNavItem(nre.Page), SpanTitle(nre.Page.Title));
439 UpdateHasNavigationBar(nre.Page);
442 void OnAnimationFinished(object sender, EventArgs e)
444 CompleteCurrentNavigationTask();
447 void CompleteCurrentNavigationTask()
449 if (_currentTaskSource != null)
451 var tmp = _currentTaskSource;
452 _currentTaskSource = null;
457 EvasObject CreateNavItem(Page page)
459 View titleView = NavigationPage.GetTitleView(page);
460 EvasObject nativeView = null;
461 if (titleView != null)
463 titleView.Parent = this.Element;
464 nativeView = new Native.TitleViewPage(Forms.NativeParent, page, titleView);
469 nativeView = Platform.GetOrCreateRenderer(page).NativeView;