Add the ShellRenderer for Tizen (#5818)
authoryourina <rina6350.you@gmail.com>
Mon, 8 Apr 2019 16:54:41 +0000 (01:54 +0900)
committerSamantha Houts <samantha.houts@xamarin.com>
Thu, 25 Apr 2019 19:01:13 +0000 (12:01 -0700)
16 files changed:
Xamarin.Forms.Platform.Tizen/Native/EditfieldEntry.cs
Xamarin.Forms.Platform.Tizen/Native/Entry.cs
Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs
Xamarin.Forms.Platform.Tizen/Resource/arrow_left.png [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/Resource/dots_horizontal.png [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/Resource/menu.png [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/Shell/IFlyoutController.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/Shell/NavigationDrawer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/Shell/NavigationView.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/Shell/ShellItemRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/Shell/ShellMoreToolbar.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/Shell/ShellNavBar.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/Shell/ShellRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/Shell/ShellSectionNavigation.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/Shell/ShellSectionRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Tizen/Xamarin.Forms.Platform.Tizen.csproj

index 1abd14b..cce50d0 100644 (file)
@@ -91,6 +91,14 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                        return _editfieldLayout;
                }
 
+               protected ELayout EditfieldLayout
+               {
+                       get
+                       {
+                               return _editfieldLayout;
+                       }
+               }
+
                protected virtual ELayout CreateEditFieldLayout(EvasObject parent)
                {
                        var layout = new ELayout(parent);
index 27670e3..25bfab1 100644 (file)
@@ -437,7 +437,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                /// Sets placeholder's internal text and style.
                /// </summary>
                /// <param name="markupText">Markup text to be used as a placeholder.</param>
-               void SetInternalPlaceholderAndStyle(string markupText)
+               protected virtual void SetInternalPlaceholderAndStyle(string markupText)
                {
                        SetPartText("elm.guide", markupText ?? "");
                }
index 9f5d235..e8b04ba 100644 (file)
@@ -14,6 +14,7 @@ using System.Reflection;
 [assembly: ExportRenderer(typeof(NavigationPage), typeof(NavigationPageRenderer))]
 [assembly: ExportRenderer(typeof(MasterDetailPage), typeof(MasterDetailPageRenderer))]
 [assembly: ExportRenderer(typeof(TabbedPage), typeof(TabbedPageRenderer))]
+[assembly: ExportRenderer(typeof(Shell), typeof(ShellRenderer))]
 
 [assembly: ExportRenderer(typeof(Label), typeof(LabelRenderer))]
 [assembly: ExportRenderer(typeof(Button), typeof(ButtonRenderer))]
diff --git a/Xamarin.Forms.Platform.Tizen/Resource/arrow_left.png b/Xamarin.Forms.Platform.Tizen/Resource/arrow_left.png
new file mode 100644 (file)
index 0000000..923dfeb
Binary files /dev/null and b/Xamarin.Forms.Platform.Tizen/Resource/arrow_left.png differ
diff --git a/Xamarin.Forms.Platform.Tizen/Resource/dots_horizontal.png b/Xamarin.Forms.Platform.Tizen/Resource/dots_horizontal.png
new file mode 100644 (file)
index 0000000..63d0f4b
Binary files /dev/null and b/Xamarin.Forms.Platform.Tizen/Resource/dots_horizontal.png differ
diff --git a/Xamarin.Forms.Platform.Tizen/Resource/menu.png b/Xamarin.Forms.Platform.Tizen/Resource/menu.png
new file mode 100644 (file)
index 0000000..6dcfc05
Binary files /dev/null and b/Xamarin.Forms.Platform.Tizen/Resource/menu.png differ
diff --git a/Xamarin.Forms.Platform.Tizen/Shell/IFlyoutController.cs b/Xamarin.Forms.Platform.Tizen/Shell/IFlyoutController.cs
new file mode 100644 (file)
index 0000000..5a1841b
--- /dev/null
@@ -0,0 +1,7 @@
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public interface IFlyoutController
+       {
+               void Open();
+       }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Shell/NavigationDrawer.cs b/Xamarin.Forms.Platform.Tizen/Shell/NavigationDrawer.cs
new file mode 100644 (file)
index 0000000..cbe8096
--- /dev/null
@@ -0,0 +1,194 @@
+using System;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public class NavigationDrawer : Native.Box
+       {
+               NavigationView _navigationView;
+               Box _mainContainer;
+               Box _dimArea;
+               EvasObject _main;
+               Panel _drawer;
+
+               bool _isLock = false;
+               double _navigationViewRatio = 0.85;
+
+               public NavigationDrawer(EvasObject parent) : base(parent)
+               {
+                       Initialize(parent);
+                       LayoutUpdated += (s, e) =>
+                       {
+                               UpdateChildGeometry();
+                       };
+               }
+
+               public event EventHandler Toggled;
+
+               public NavigationView NavigationView
+               {
+                       get
+                       {
+                               return _navigationView;
+                       }
+                       set
+                       {
+                               UpdateNavigationView(value);
+                       }
+               }
+
+               public EvasObject Main
+               {
+                       get
+                       {
+                               return _main;
+                       }
+                       set
+                       {
+                               UpdateMain(value);
+                       }
+               }
+
+               public bool IsOpen
+               {
+                       get
+                       {
+                               return _drawer.IsOpen;
+                       }
+                       set
+                       {
+                               _drawer.IsOpen = value;
+                       }
+               }
+
+               public bool IsLock
+               {
+                       get
+                       {
+                               return _isLock;
+                       }
+                       set
+                       {
+                               _isLock = value;
+                               _drawer.SetScrollable(!_isLock);
+                       }
+               }
+
+               void Initialize(EvasObject parent)
+               {
+                       _mainContainer = new Box(parent)
+                       {
+                               AlignmentX = -1,
+                               AlignmentY = -1,
+                               WeightX = 1,
+                               WeightY = 1,
+                       };
+                       _mainContainer.Show();
+                       PackEnd(_mainContainer);
+
+                       _dimArea = new Box(parent)
+                       {
+                               AlignmentX = -1,
+                               AlignmentY = -1,
+                               WeightX = 1,
+                               WeightY = 1,
+                               BackgroundColor = new ElmSharp.Color(0, 0, 0, 82)
+                       };
+
+                       PackEnd(_dimArea);
+
+                       _drawer = new Panel(parent);
+
+                       _drawer.SetScrollable(!_isLock);
+                       _drawer.SetScrollableArea(_navigationViewRatio);
+                       _drawer.Direction = PanelDirection.Left;
+                       _drawer.Toggled += (s, e) =>
+                       {
+                               UpdateDimArea();
+
+                               Toggled?.Invoke(this, e);
+                       };
+
+                       _drawer.Show();
+                       PackEnd(_drawer);
+               }
+
+               void UpdateNavigationView(NavigationView navigationView)
+               {
+                       if (_navigationView != null)
+                       {
+                               _navigationView.Hide();
+                       }
+
+                       _navigationView = navigationView;
+
+                       if (_navigationView != null)
+                       {
+                               _navigationView.SetAlignment(-1, -1);
+                               _navigationView.SetWeight(1, 1);
+                               if (!_navigationView.IsVisible)
+                               {
+                                       _navigationView.Show();
+                               }
+                               _drawer.SetContent(_navigationView, true);
+                               UpdateDimArea();
+                               UpdateNavigationViewGeometry();
+                       }
+                       else
+                       {
+                               _drawer.SetContent(null, true);
+                       }
+               }
+
+               void UpdateMain(EvasObject main)
+               {
+                       if (_main != null)
+                       {
+                               _mainContainer.UnPack(_main);
+                               _main.Hide();
+                       }
+
+                       _main = main;
+
+                       if (_main != null)
+                       {
+                               if (!_main.IsVisible)
+                               {
+                                       _main.Show();
+                               }
+                               _mainContainer.PackEnd(_main);
+                       }
+               }
+
+               void UpdateDimArea()
+               {
+                       if (_drawer.IsOpen)
+                       {
+                               _dimArea.Show();
+                       }
+                       else
+                       {
+                               _dimArea.Hide();
+                       }
+               }
+
+               void UpdateChildGeometry()
+               {
+                       if (_main != null)
+                       {
+                               _mainContainer.Geometry = Geometry;
+                       }
+
+                       UpdateNavigationViewGeometry();
+               }
+
+               void UpdateNavigationViewGeometry()
+               {
+                       if (_navigationView != null)
+                       {
+                               _drawer.Geometry = Geometry;
+                               _dimArea.Geometry = Geometry;
+                       }
+               }
+       }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Shell/NavigationView.cs b/Xamarin.Forms.Platform.Tizen/Shell/NavigationView.cs
new file mode 100644 (file)
index 0000000..fd94004
--- /dev/null
@@ -0,0 +1,205 @@
+using System;
+using System.Collections.Generic;
+using ElmSharp;
+using EColor = ElmSharp.Color;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public class NavigationView : Native.Box
+       {
+               EvasObject _header;
+               GenList _menu;
+               EColor _backgroundColor;
+               EColor _defaultBackgroundColor = EColor.White;
+
+               IList<Group> _groups;
+               GenItemClass _defaultClass;
+
+               public NavigationView(EvasObject parent) : base(parent)
+               {
+                       Initialize(parent);
+                       LayoutUpdated += (s, e) =>
+                       {
+                               UpdateChildGeometry();
+                       };
+                       base.BackgroundColor = _defaultBackgroundColor;
+                       _menu.BackgroundColor = _defaultBackgroundColor;
+               }
+
+               public event EventHandler<GenListItemEventArgs> MenuItemSelected;
+
+               public override EColor BackgroundColor
+               {
+                       get
+                       {
+                               return _backgroundColor;
+                       }
+                       set
+                       {
+                               _backgroundColor = value;
+
+                               EColor effectiveColor = _backgroundColor.IsDefault ? _defaultBackgroundColor : _backgroundColor;
+                               _menu.BackgroundColor = effectiveColor;
+                               base.BackgroundColor = effectiveColor;
+                       }
+               }
+
+               public EvasObject Header
+               {
+                       get
+                       {
+                               return _header;
+                       }
+                       set
+                       {
+                               if (_header != null)
+                               {
+                                       UnPack(_header);
+                                       _header.Hide();
+                               }
+                               _header = value;
+
+                               if (_header != null)
+                               {
+                                       PackStart(_header);
+                                       if (!_header.IsVisible)
+                                       {
+                                               _header.Show();
+                                       }
+                               }
+
+                               UpdateChildGeometry();
+                       }
+               }
+
+               public IList<Group> Menu
+               {
+                       get
+                       {
+                               return _groups;
+                       }
+                       set
+                       {
+                               _groups = value;
+                               UpdateMenu();
+                       }
+               }
+
+               void Initialize(EvasObject parent)
+               {
+                       _menu = new GenList(parent)
+                       {
+                               BackgroundColor = EColor.Transparent,
+                               Style = "solid/default",
+                       };
+
+                       _menu.ItemSelected += (s, e) =>
+                       {
+                               MenuItemSelected?.Invoke(this, e);
+                       };
+
+                       _menu.Show();
+                       PackEnd(_menu);
+
+                       _defaultClass = new GenItemClass("double_label")
+                       {
+                               GetTextHandler = (obj, part) =>
+                               {
+                                       if (part == "elm.text")
+                                       {
+                                               return ((Item)obj).Title;
+                                       }
+                                       else
+                                       {
+                                               return null;
+                                       }
+                               },
+                               GetContentHandler = (obj, part) =>
+                               {
+                                       if (part == "elm.swallow.icon")
+                                       {
+                                               var icon = ((Item)obj).Icon;
+
+                                               if (icon != null)
+                                               {
+                                                       var image = new ElmSharp.Image(parent)
+                                                       {
+                                                               MinimumWidth = Forms.ConvertToScaledPixel(24),
+                                                               MinimumHeight = Forms.ConvertToScaledPixel(24)
+                                                       };
+                                                       var result = image.Load(ResourcePath.GetPath(icon));
+                                                       return image;
+                                               }
+                                               else
+                                               {
+                                                       return null;
+                                               }
+                                       }
+                                       else
+                                       {
+                                               return null;
+                                       }
+                               }
+                       };
+               }
+
+               void UpdateMenu()
+               {
+                       _menu.Clear();
+
+                       if (_groups != null)
+                       {
+                               for (int i = 0; i < _groups.Count; i++)
+                               {
+                                       for (int j = 0; j < _groups[i].Items.Count; j++)
+                                       {
+                                               var item = _menu.Append(_defaultClass, _groups[i].Items[j]);
+                                               if (j != 0)
+                                               {
+                                                       item.SetPartColor("bottomline", EColor.Transparent);
+                                               }
+
+                                               item.SetPartColor("bg", EColor.Transparent);
+                                       }
+                               }
+                       }
+               }
+
+               void UpdateChildGeometry()
+               {
+                       int headerHeight = 0;
+                       if (_header != null)
+                       {
+                               headerHeight = _header.MinimumHeight;
+                               _header.Geometry = new Rect(Geometry.X, Geometry.Y, Geometry.Width, headerHeight);
+                       }
+                       _menu.Geometry = new Rect(Geometry.X, Geometry.Y + headerHeight, Geometry.Width, Geometry.Height - headerHeight);
+               }
+       }
+
+       public class Item
+       {
+               public string Title { get; set; }
+
+               public string Icon { get; set; }
+
+               public Item(string title, string icon = null)
+               {
+                       Title = title;
+                       Icon = icon;
+               }
+       }
+
+       public class Group
+       {
+               public string Title { get; set; }
+
+               public List<Item> Items { get; set; }
+
+               public Group(List<Item> items, string title = null)
+               {
+                       Items = items;
+                       Title = title;
+               }
+       }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Shell/ShellItemRenderer.cs b/Xamarin.Forms.Platform.Tizen/Shell/ShellItemRenderer.cs
new file mode 100644 (file)
index 0000000..9616581
--- /dev/null
@@ -0,0 +1,431 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Reflection;
+using ElmSharp;
+using EColor = ElmSharp.Color;
+using EToolbarItem = ElmSharp.ToolbarItem;
+using Xamarin.Forms.Platform.Tizen.Native;
+using System.Collections.Specialized;
+using System.Linq;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public class ShellItemRenderer : IAppearanceObserver, IDisposable
+       {
+               Native.Box _box = null;
+               Toolbar _toolbar = null;
+               Panel _drawer = null;
+               ShellMoreToolbar _more = null;
+               EToolbarItem _moreToolbarItem = null;
+
+               IFlyoutController _flyoutController = null;
+
+               Dictionary<EToolbarItem, ShellSection> _itemToSection = new Dictionary<EToolbarItem, ShellSection>();
+               Dictionary<ShellSection, EToolbarItem> _sectionToitem = new Dictionary<ShellSection, EToolbarItem>();
+               Dictionary<ShellSection, ShellSectionNavigation> _sectionToPage = new Dictionary<ShellSection, ShellSectionNavigation>();
+               LinkedList<EToolbarItem> _toolbarItemList = new LinkedList<EToolbarItem>();
+
+               EColor _backgroudColor = ShellRenderer.DefaultBackgroundColor.ToNative();
+
+               ShellItem _shellItem = null;
+               ShellSectionNavigation _currentSection = null;
+
+               bool _disposed = false;
+
+               public ShellItemRenderer(IFlyoutController flyoutController, ShellItem item)
+               {
+                       _flyoutController = flyoutController;
+                       _shellItem = item;
+                       _shellItem.PropertyChanged += OnShellItemPropertyChanged;
+                       (_shellItem.Items as INotifyCollectionChanged).CollectionChanged += OnShellItemsCollectionChanged;
+
+                       _box = new Native.Box(Forms.NativeParent);
+                       _box.LayoutUpdated += OnLayoutUpdated;
+                       _box.Show();
+
+                       CreateToolbar();
+                       CreateMoreToolbar();
+                       ResetToolbarItems();
+
+                       UpdateCurrentShellSection(_shellItem.CurrentItem);
+                       if (_drawer != null)
+                               _currentSection?.StackBelow(_drawer);
+
+                       ((IShellController)_shellItem.Parent).AddAppearanceObserver(this, _shellItem);
+               }
+
+               ~ShellItemRenderer()
+               {
+                       Dispose(false);
+               }
+
+               public void Dispose()
+               {
+                       Dispose(true);
+                       GC.SuppressFinalize(this);
+               }
+
+               public Native.Box Control
+               {
+                       get
+                       {
+                               return _box;
+                       }
+               }
+
+               public EColor BackgroundColor
+               {
+                       get
+                       {
+                               return _backgroudColor;
+                       }
+                       set
+                       {
+                               _backgroudColor = value;
+                               UpdateToolbarBackgroudColor(_backgroudColor);
+                       }
+               }
+
+               public void UpdateCurrentItem(ShellSection section)
+               {
+                       UpdateCurrentShellSection(section);
+
+                       if (_drawer != null)
+                               _currentSection?.StackBelow(_drawer);
+
+                       if (_sectionToitem.ContainsKey(section))
+                       {
+                               _sectionToitem[section].IsSelected = true;
+                       }
+                       else if(section != null)
+                       {
+                               _drawer.IsOpen = false;
+                               var more = _toolbarItemList.Last() as EToolbarItem;
+                               more.IsSelected = true;
+                       }
+                       UpdateLayout();
+               }
+
+               public void SetCurrentItem(ShellSection section)
+               {
+                       if (_shellItem.CurrentItem != section)
+                       {
+                               _shellItem.SetValueFromRenderer(ShellItem.CurrentItemProperty, section);
+                       }
+               }
+
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (_disposed)
+                               return;
+
+                       if (disposing)
+                       {
+                               if (_shellItem != null)
+                               {
+                                       Control.LayoutUpdated -= OnLayoutUpdated;
+                                       ((IShellController)_shellItem.Parent).RemoveAppearanceObserver(this);
+                                       _shellItem.PropertyChanged -= OnShellItemPropertyChanged;
+                                       (_shellItem.Items as INotifyCollectionChanged).CollectionChanged -= OnShellItemsCollectionChanged;
+                                       _shellItem = null;
+
+                                       foreach (var pair in _sectionToPage)
+                                       {
+                                               var navi = pair.Value as ShellSectionNavigation;
+                                               navi.Dispose();
+                                       }
+
+                                       if (_toolbar != null)
+                                       {
+                                               _toolbar.Selected -= OnTabsSelected;
+                                       }
+                                       _itemToSection.Clear();
+                                       _sectionToitem.Clear();
+                                       _sectionToPage.Clear();
+                                       _toolbarItemList.Clear();
+                               }
+                               Control.Unrealize();
+                       }
+                       _disposed = true;
+               }
+
+               void OnShellItemPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == "CurrentItem")
+                       {
+                               UpdateCurrentItem(_shellItem.CurrentItem);
+                       }
+               }
+
+               void IAppearanceObserver.OnAppearanceChanged(ShellAppearance appearance)
+               {
+                       if (appearance == null)
+                               return;
+
+                       _backgroudColor = appearance.BackgroundColor.ToNative();
+                       UpdateToolbarBackgroudColor(appearance.BackgroundColor.ToNative());
+               }
+
+               void UpdateToolbarBackgroudColor(ElmSharp.Color color)
+               {
+                       foreach (EToolbarItem item in _toolbarItemList)
+                       {
+                               item.SetPartColor("bg", color);
+                       }
+               }
+
+               void CreateMoreToolbar()
+               {
+                       if (_more != null)
+                               return;
+
+                       _more = new ShellMoreToolbar(this);
+                       _more.Show();
+                       _drawer = new Panel(Forms.NativeParent);
+                       _drawer.SetScrollable(true);
+                       _drawer.SetScrollableArea(1.0);
+                       _drawer.Direction = PanelDirection.Bottom;
+                       _drawer.IsOpen = false;
+                       _drawer.SetContent(_more, true);
+                       _drawer.Show();
+                       Control.PackEnd(_drawer);
+               }
+
+               void CreateToolbar()
+               {
+                       if (_toolbar != null)
+                               return;
+
+                       _toolbar = new Toolbar(Forms.NativeParent)
+                       {
+                               AlignmentX = -1,
+                               WeightX = 1,
+                               BackgroundColor = _backgroudColor,
+                               ShrinkMode = ToolbarShrinkMode.Expand,
+                               Style = "material"
+                       };
+                       _toolbar.Show();
+                       _toolbar.Selected += OnTabsSelected;
+                       Control.PackEnd(_toolbar);
+               }
+
+               void ResetToolbarItems()
+               {
+                       foreach (ShellSection section in _shellItem.Items)
+                       {
+                               InsertToolbarItem(section);
+                       }
+               }
+
+               void AddToolbarItems(NotifyCollectionChangedEventArgs e)
+               {
+                       foreach (var item in e.NewItems)
+                       {
+                               InsertToolbarItem(item as ShellSection);
+                       }
+                       UpdateLayout();
+               }
+
+               void RemoveToolbarItems(NotifyCollectionChangedEventArgs e)
+               {
+                       foreach (var item in e.OldItems)
+                       {
+                               RemoveToolbarItem(item as ShellSection);
+                       }
+                       UpdateLayout();
+               }
+
+               void OnShellItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+               {
+                       switch (e.Action)
+                       {
+                               case NotifyCollectionChangedAction.Add:
+                                       AddToolbarItems(e);
+                                       break;
+
+                               case NotifyCollectionChangedAction.Remove:
+                                       RemoveToolbarItems(e);
+                                       break;
+
+                               default:
+                                       break;
+                       }
+               }
+
+               string GetIconPath(ImageSource src)
+               {
+                       if (src is FileImageSource fis)
+                       {
+                               return ResourcePath.GetPath(fis.File);
+                       }
+                       else
+                       {
+                               return null;
+                       }
+               }
+
+               void RemoveToolbarItem(ShellSection section)
+               {
+                       if (_sectionToitem.ContainsKey(section))
+                       {
+                               var del = _sectionToitem[section];
+                               _toolbarItemList.Remove(del);
+                               _itemToSection.Remove(del);
+                               _sectionToitem.Remove(section);
+                               del.Delete();
+
+                               if (_moreToolbarItem != null)
+                               {
+                                       ShellSection move = _more.RemoveFirst();
+                                       InsertToolbarItem(move);
+                               }
+                       }
+                       else
+                       {
+                               _more.RemoveItem(section);
+                       }
+
+                       if (_more.Count == 1)
+                       {
+                               _toolbarItemList.Remove(_moreToolbarItem);
+                               _moreToolbarItem.Delete();
+                               _moreToolbarItem = null;
+
+                               ShellSection move = _more.RemoveFirst();
+                               InsertToolbarItem(move);
+                       }
+               }
+
+               void InsertToolbarItem(ShellSection section)
+               {
+                       if (_toolbarItemList.Count < 5)
+                       {
+                               EToolbarItem item = null;
+                               if (_moreToolbarItem == null)
+                                       item = _toolbar.Append(section.Title, GetIconPath(section.Icon));
+                               else
+                                       item = _toolbar.InsertBefore(_moreToolbarItem, section.Title, GetIconPath(section.Icon));
+
+                               if (item != null)
+                               {
+                                       item.SetPartColor("bg", _backgroudColor);
+                                       item.SetPartColor("underline", EColor.Transparent);
+
+                                       _toolbarItemList.AddLast(item);
+                                       _itemToSection.Add(item, section);
+                                       _sectionToitem.Add(section, item);
+                               }
+                       }
+                       else if (_moreToolbarItem == null && _toolbarItemList.Count == 5)
+                       {
+                               var last = _toolbarItemList.Last() as EToolbarItem;
+                               var lastSection = _itemToSection[last];
+
+                               _toolbarItemList.RemoveLast();
+                               _itemToSection.Remove(last);
+                               _sectionToitem.Remove(lastSection);
+                               last.Delete();
+
+                               CreateMoreToolbarItem();
+
+                               _more.AddItem(lastSection);
+                               _more.AddItem(section);
+                       }
+                       else
+                       {
+                               _more.AddItem(section);
+                       }
+               }
+
+               void CreateMoreToolbarItem()
+               {
+                       if (_moreToolbarItem != null)
+                               return;
+
+                       //The source of icon resources is https://materialdesignicons.com/
+                       ImageSource src = ImageSource.FromResource("Xamarin.Forms.Platform.Tizen.Resource.dots_horizontal.png", typeof(ShellItemRenderer).GetTypeInfo().Assembly);
+                       Native.Image icon = new Native.Image(Forms.NativeParent);
+                       var task = icon.LoadFromImageSourceAsync(src);
+
+                       _moreToolbarItem = _toolbar.Append("More", null);
+                       _moreToolbarItem.SetPartContent("elm.swallow.icon", icon);
+                       _moreToolbarItem.SetPartColor("bg", _backgroudColor);
+                       _moreToolbarItem.SetPartColor("underline", EColor.Transparent);
+                       _toolbarItemList.AddLast(_moreToolbarItem);
+               }
+
+               void UpdateCurrentShellSection(ShellSection section)
+               {
+                       _currentSection?.Hide();
+
+                       if (section == null)
+                       {
+                               _currentSection = null;
+                               return;
+                       }
+
+                       ShellSectionNavigation native = null;
+                       if (_sectionToPage.ContainsKey(section))
+                       {
+                               native = _sectionToPage[section] as ShellSectionNavigation;
+                       }
+                       else
+                       {
+                               native = new ShellSectionNavigation(_flyoutController, section);
+                               _sectionToPage[section] = native;
+                               Control.PackEnd(native);
+                       }
+                       _currentSection = native;
+                       _currentSection.Show();
+                       return;
+               }
+
+               void OnTabsSelected(object sender, ToolbarItemEventArgs e)
+               {
+                       if (_toolbar.SelectedItem == null)
+                               return;
+
+                       if (e.Item == _moreToolbarItem)
+                       {
+                               _drawer.IsOpen = true;
+                       }
+                       else
+                       {
+                               ShellSection section = _itemToSection[_toolbar.SelectedItem];
+                               SetCurrentItem(section);
+                       }
+               }
+
+               void UpdateLayout()
+               {
+                       OnLayoutUpdated(this, new LayoutEventArgs() { Geometry = Control.Geometry });
+               }
+
+               void OnLayoutUpdated(object sender, LayoutEventArgs e)
+               {
+                       int toolbarHeight = _toolbar.MinimumHeight;
+                       if (_shellItem.Items.Count <= 1)
+                       {
+                               toolbarHeight = 0;
+                               _toolbar?.Hide();
+                               _drawer?.Hide();
+                       }
+
+                       _currentSection?.Move(e.Geometry.X, e.Geometry.Y);
+                       _currentSection?.Resize(e.Geometry.Width, e.Geometry.Height - toolbarHeight);
+                       if (_shellItem.Items.Count > 1)
+                       {
+                               _toolbar.Show();
+                               _toolbar.Move(e.Geometry.X, e.Geometry.Y + e.Geometry.Height - toolbarHeight);
+                               _toolbar.Resize(e.Geometry.Width, toolbarHeight);
+                               if (_drawer != null)
+                               {
+                                       _drawer.Show();
+                                       _drawer.Move(e.Geometry.X, e.Geometry.Y + e.Geometry.Height - _more.Height);
+                                       _drawer.Resize(e.Geometry.Width, _more.Height);
+                               }
+                       }
+               }
+       }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Shell/ShellMoreToolbar.cs b/Xamarin.Forms.Platform.Tizen/Shell/ShellMoreToolbar.cs
new file mode 100644 (file)
index 0000000..9013f00
--- /dev/null
@@ -0,0 +1,119 @@
+using System.Collections.Generic;
+using ElmSharp;
+using Xamarin.Forms.Platform.Tizen.Native;
+using System.Linq;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public class ShellMoreToolbar : GenList
+       {
+               ShellItemRenderer _shellItemRenderer = null;
+
+               GenItemClass _defaultClass = null;
+               Dictionary<ShellSection, GenListItem> _sectionToItem = new Dictionary<ShellSection, GenListItem>();
+               LinkedList<ShellSection> _shellSectionList = new LinkedList<ShellSection>();
+
+               const int _cellHeight = 100;
+               const int _iconPadding = 30;
+               const int _iconSize = 60;
+
+               public ShellMoreToolbar(ShellItemRenderer renderer) : base(Forms.NativeParent)
+               {
+                       _shellItemRenderer = renderer;
+
+                       Homogeneous = true;
+                       AlignmentX = -1;
+                       AlignmentY = -1;
+                       WeightX = 1;
+                       WeightY = 1;
+                       BackgroundColor = ShellRenderer.DefaultBackgroundColor.ToNative();
+                       ItemSelected += OnItemSelected;
+                       _defaultClass = new GenItemClass("full")
+                       {
+                               GetContentHandler = GetContent,
+                       };
+               }
+
+               public void AddItem(ShellSection section)
+               {
+                       GenListItem item = Append(_defaultClass, section);
+                       if (item != null)
+                       {
+                               _sectionToItem[section] = item;
+                               _shellSectionList.AddLast(section);
+                       }
+               }
+
+               public void RemoveItem(ShellSection section)
+               {
+                       if (_sectionToItem.ContainsKey(section))
+                       {
+                               GenListItem del = _sectionToItem[section];
+                               _sectionToItem.Remove(section);
+                               _shellSectionList.Remove(section);
+                               del.Delete();
+                       }
+               }
+
+               public ShellSection RemoveFirst()
+               {
+                       ShellSection del = _shellSectionList.First();
+                       RemoveItem(del);
+                       return del;
+               }
+
+               public int Height
+               {
+                       get
+                       {
+                               int height = _cellHeight * Count;
+                               int maxHeight = _shellItemRenderer.Control.Geometry.Height;
+                               return height <= maxHeight ? height : maxHeight;
+                       }
+               }
+
+               EvasObject GetContent(object data, string part)
+               {
+                       ShellSection section = data as ShellSection;
+
+                       var box = new Native.Box(Forms.NativeParent);
+                       box.Show();
+
+                       var icon = new Native.Image(Forms.NativeParent)
+                       {
+                               MinimumWidth = Forms.ConvertToScaledPixel(44),
+                               MinimumHeight = Forms.ConvertToScaledPixel(27)
+                       };
+                       var task = icon.LoadFromImageSourceAsync(section.Icon);
+                       icon.Show();
+
+                       var title = new Native.Label(Forms.NativeParent)
+                       {
+                               Text = section.Title,
+                               FontSize = Forms.ConvertToEflFontPoint(14),
+                               HorizontalTextAlignment = Native.TextAlignment.Start,
+                               VerticalTextAlignment = Native.TextAlignment.Center
+                       };
+                       title.Show();
+
+                       box.PackEnd(icon);
+                       box.PackEnd(title);
+                       box.LayoutUpdated += (object sender, LayoutEventArgs e) =>
+                       {
+                               icon.Move(e.Geometry.X + _iconPadding, e.Geometry.Y + _iconPadding);
+                               icon.Resize(_iconSize, _iconSize);
+
+                               title.Move(e.Geometry.X + 2 * _iconPadding + _iconSize, e.Geometry.Y);
+                               title.Resize(e.Geometry.Width - (2 * _iconPadding + _iconSize), e.Geometry.Height);
+                       };
+                       box.MinimumHeight = _cellHeight;
+                       return box;
+               }
+
+               void OnItemSelected(object sender, GenListItemEventArgs e)
+               {
+                       ShellSection section = e.Item.Data as ShellSection;
+                       _shellItemRenderer.SetCurrentItem(section);
+               }
+       }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Shell/ShellNavBar.cs b/Xamarin.Forms.Platform.Tizen/Shell/ShellNavBar.cs
new file mode 100644 (file)
index 0000000..5bcb66a
--- /dev/null
@@ -0,0 +1,286 @@
+using System;
+using System.Reflection;
+using ElmSharp;
+using EColor = ElmSharp.Color;
+using Xamarin.Forms.Platform.Tizen.Native;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public class ShellNavBar : Native.Box
+       {
+               Native.Image _menu = null;
+               Native.Label _title = null;
+               Native.SearchBar _nativeSearchHandler = null;
+               EvasObject _nativeTitleView = null;
+               ShellSectionNavigation _shellSectionNavigation = null;
+
+               SearchHandler _searchHandler = null;
+               View _titleView = null;
+               Page _page = null;
+
+               IFlyoutController _flyoutController = null;
+
+               EColor _backgroudColor = ShellRenderer.DefaultBackgroundColor.ToNative();
+               EColor _foregroudColor = ShellRenderer.DefaultForegroundColor.ToNative();
+
+               // The source of icon resources is https://materialdesignicons.com/
+               const string _menuIcon = "Xamarin.Forms.Platform.Tizen.Resource.menu.png";
+               const string _backIcon = "Xamarin.Forms.Platform.Tizen.Resource.arrow_left.png";
+
+               bool _hasBackButton = false;
+
+               public ShellNavBar(IFlyoutController flyoutController, ShellSectionNavigation shellSectionNavigation) : base(Forms.NativeParent)
+               {
+                       _flyoutController = flyoutController;
+                       _shellSectionNavigation = shellSectionNavigation;
+
+                       _menu = new Native.Image(Forms.NativeParent);
+                       _menu.Clicked += OnMenuClicked;
+                       UpdateMenuIcon();
+                       _menu.Show();
+
+                       _title = new Native.Label(Forms.NativeParent)
+                       {
+                               FontSize = Device.Idiom == TargetIdiom.TV ? 60 : 23,
+                               VerticalTextAlignment = Native.TextAlignment.Center,
+                               TextColor = _backgroudColor,
+                               FontAttributes = FontAttributes.Bold,
+                       };
+                       _title.Show();
+
+                       BackgroundColor = _backgroudColor;
+                       PackEnd(_menu);
+                       PackEnd(_title);
+                       LayoutUpdated += OnLayoutUpdated;
+               }
+
+               public bool HasBackButton
+               {
+                       get
+                       {
+                               return _hasBackButton;
+                       }
+                       set
+                       {
+                               _hasBackButton = value;
+                               UpdateMenuIcon();
+                       }
+               }
+
+               public SearchHandler SearchHandler
+               {
+                       get
+                       {
+                               return _searchHandler;
+                       }
+                       set
+                       {
+                               _searchHandler = value;
+                               UpdateSearchHandler(_searchHandler);
+                               UpdateChildren();
+                       }
+               }
+
+               public View TitleView
+               {
+                       get
+                       {
+                               return _titleView;
+                       }
+                       set
+                       {
+                               _titleView = value;
+                               UpdateTitleView(_titleView);
+                               UpdateChildren();
+                       }
+               }
+
+               public string Title
+               {
+                       get
+                       {
+                               return _title?.Text;
+                       }
+                       set
+                       {
+                               if (string.IsNullOrEmpty(value))
+                               {
+                                       _title?.Hide();
+                               }
+                               else
+                               {
+                                       _title.Text = value;
+                               }
+                       }
+               }
+
+               public override EColor BackgroundColor
+               {
+                       get
+                       {
+                               return _backgroudColor;
+                       }
+                       set
+                       {
+                               _backgroudColor = value;
+                               base.BackgroundColor = _backgroudColor;
+                       }
+               }
+
+               public EColor ForegroundColor
+               {
+                       get
+                       {
+                               return _foregroudColor;
+                       }
+                       set
+                       {
+                               _foregroudColor = value;
+                               _menu.Color = value;
+                       }
+               }
+
+               public EColor TitleColor
+               {
+                       get
+                       {
+                               return _title.TextColor;
+                       }
+                       set
+                       {
+                               _title.TextColor = value;
+                       }
+               }
+
+               internal Page CurrentPage
+               {
+                       get
+                       {
+                               return _page;
+                       }
+                       set
+                       {
+                               _page = value;
+                       }
+               }
+
+               async void UpdateMenuIcon()
+               {
+                       string file = _hasBackButton ? _backIcon : _menuIcon;
+                       ImageSource source = ImageSource.FromResource(file, typeof(ShellNavBar).GetTypeInfo().Assembly);
+                       bool ret = await _menu.LoadFromImageSourceAsync(source);
+               }
+
+               void OnMenuClicked(object sender, EventArgs e)
+               {
+                       var backButtonHandler = Shell.GetBackButtonBehavior(_page);
+                       if (backButtonHandler?.Command != null)
+                       {
+                               backButtonHandler.Command.Execute(backButtonHandler.CommandParameter);
+                       }
+                       else if (_hasBackButton)
+                       {
+                               _shellSectionNavigation.PopRequest(this, new Internals.NavigationRequestedEventArgs(_page, false));
+                       }
+                       else
+                       {
+                               _flyoutController.Open();
+                       }
+               }
+
+               void UpdateTitleView(View titleView)
+               {
+                       _nativeTitleView?.Unrealize();
+                       _nativeTitleView = null;
+
+                       if (titleView != null)
+                       {
+                               var renderer = Platform.GetOrCreateRenderer(titleView);
+                               (renderer as LayoutRenderer)?.RegisterOnLayoutUpdated();
+                               _nativeTitleView = renderer.NativeView;
+                               PackEnd(_nativeTitleView);
+                       }
+               }
+
+               void UpdateSearchHandler(SearchHandler handler)
+               {
+                       _nativeSearchHandler?.Unrealize();
+                       _nativeSearchHandler = null;
+
+                       if (handler != null)
+                       {
+                               _nativeSearchHandler = new Native.SearchBar(Forms.NativeParent);
+                               _nativeSearchHandler.IsSingleLine = true;
+                               _nativeSearchHandler.BackgroundColor = ElmSharp.Color.White;
+                               _nativeSearchHandler.Placeholder = handler.Placeholder;
+                               _nativeSearchHandler.Show();
+                               PackEnd(_nativeSearchHandler);
+                       }
+               }
+
+               Native.Image GetSearchHandlerIcon(ImageSource source)
+               {
+                       Native.Image _icon = new Native.Image(Forms.NativeParent);
+                       if (source != null)
+                       {
+                               var task = _icon.LoadFromImageSourceAsync(source);
+                       }
+                       return _icon;
+               }
+
+               void UpdateChildren()
+               {
+                       if (_searchHandler != null)
+                       {
+                               _nativeSearchHandler.Show();
+                               _title?.Hide();
+                               _nativeTitleView?.Hide();
+                       }
+                       else if (_titleView != null)
+                       {
+                               _nativeTitleView.Show();
+                               _title?.Hide();
+                               _nativeSearchHandler?.Hide();
+                       }
+                       else
+                       {
+                               _title.Show();
+                               _nativeTitleView?.Hide();
+                               _nativeSearchHandler?.Hide();
+                       }
+                       UpdatPageLayout();
+               }
+
+               void OnLayoutUpdated(object sender, LayoutEventArgs e)
+               {
+                       int menuSize = 50;
+                       int menuMargin = 20;
+                       int titleLeftMargin = 40;
+                       int titleViewTopMargin = 40;
+
+                       _menu.Move(e.Geometry.X + menuMargin, e.Geometry.Y + (e.Geometry.Height - menuSize) / 2);
+                       _menu.Resize(menuSize, menuSize);
+
+                       if (_searchHandler != null)
+                       {
+                               _nativeSearchHandler.Move(e.Geometry.X + (menuSize + menuMargin * 2) + titleLeftMargin, e.Geometry.Y + (titleViewTopMargin / 2));
+                               _nativeSearchHandler.Resize(e.Geometry.Width - (menuSize + menuMargin * 2) - titleLeftMargin - (titleViewTopMargin / 2), e.Geometry.Height - titleViewTopMargin);
+                       }
+                       else if (_titleView != null)
+                       {
+                               _nativeTitleView.Move(e.Geometry.X + (menuSize + menuMargin * 2) + titleLeftMargin, e.Geometry.Y + (titleViewTopMargin / 2));
+                               _nativeTitleView.Resize(e.Geometry.Width - (menuSize + menuMargin * 2) - titleLeftMargin - (titleViewTopMargin / 2), e.Geometry.Height - titleViewTopMargin);
+                       }
+                       else
+                       {
+                               _title.Move(e.Geometry.X + (menuSize + menuMargin * 2) + titleLeftMargin, e.Geometry.Y);
+                               _title.Resize(e.Geometry.Width - (menuSize + menuMargin) - titleLeftMargin, e.Geometry.Height);
+                       }
+               }
+
+               void UpdatPageLayout()
+               {
+                       OnLayoutUpdated(this, new LayoutEventArgs() { Geometry = Geometry });
+               }
+       }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Shell/ShellRenderer.cs b/Xamarin.Forms.Platform.Tizen/Shell/ShellRenderer.cs
new file mode 100644 (file)
index 0000000..6563fcc
--- /dev/null
@@ -0,0 +1,183 @@
+using System;
+using System.Collections.Generic;
+using ElmSharp;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public class ShellRenderer : VisualElementRenderer<Shell>, IFlyoutController
+       {
+               NavigationDrawer _native;
+               ShellItemRenderer _shellItem;
+
+               IDictionary<int, Element> _flyoutMenu = new Dictionary<int, Element>();
+
+               public static readonly Color DefaultBackgroundColor = Color.FromRgb(33, 150, 243);
+               public static readonly Color DefaultForegroundColor = Color.White;
+               public static readonly Color DefaultTitleColor = Color.White;
+
+               public ShellRenderer()
+               {
+                       RegisterPropertyHandler(Shell.CurrentItemProperty, UpdateCurrentItem);
+                       RegisterPropertyHandler(Shell.FlyoutBackgroundColorProperty, UpdateFlyoutBackgroundColor);
+                       RegisterPropertyHandler(Shell.FlyoutIsPresentedProperty, UpdateFlyoutIsPresented);
+               }
+
+               public override Rect GetNativeContentGeometry()
+               {
+                       var rect = base.GetNativeContentGeometry();
+                       rect.X = 0;
+                       rect.Y = 0;
+                       return rect;
+               }
+
+               protected override void OnElementChanged(ElementChangedEventArgs<Shell> e)
+               {
+                       if (_native == null)
+                       {
+                               _native = new NavigationDrawer(Forms.NativeParent)
+                               {
+                                       NavigationView = new NavigationView(Forms.NativeParent)
+                               };
+                               SetNativeView(_native);
+
+                               _native.Toggled += OnFlyoutIsPresentedChanged;
+
+                               InitializeFlyout();
+                       }
+
+                       base.OnElementChanged(e);
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               ((IShellController)Element).StructureChanged -= OnShellStructureChanged;
+                               if (_native != null)
+                               {
+                                       _native.Toggled -= OnFlyoutIsPresentedChanged;
+                                       _native.NavigationView.MenuItemSelected -= OnItemSelected;
+                               }
+                       }
+                       base.Dispose(disposing);
+               }
+
+               void InitializeFlyout()
+               {
+                       ((IShellController)Element).StructureChanged += OnShellStructureChanged;
+
+                       View flyoutHeader = ((IShellController)Element).FlyoutHeader;
+                       if (flyoutHeader != null)
+                       {
+                               var headerView = Platform.GetOrCreateRenderer(flyoutHeader);
+                               (headerView as LayoutRenderer)?.RegisterOnLayoutUpdated();
+
+                               Size request = flyoutHeader.Measure(Forms.ConvertToScaledDP(_native.NavigationView.MinimumWidth), Forms.ConvertToScaledDP(_native.NavigationView.MinimumHeight)).Request;
+                               headerView.NativeView.MinimumHeight = Forms.ConvertToScaledPixel(request.Height);
+
+                               _native.NavigationView.Header = headerView.NativeView;
+                       }
+
+                       BuildMenu();
+                       _native.NavigationView.MenuItemSelected += OnItemSelected;
+               }
+
+               void UpdateCurrentItem()
+               {
+                       _shellItem?.Dispose();
+                       if (Element.CurrentItem != null)
+                       {
+                               _shellItem = new ShellItemRenderer(this, Element.CurrentItem);
+                               _shellItem.Control.SetAlignment(-1, -1);
+                               _shellItem.Control.SetWeight(1, 1);
+                               _native.Main = _shellItem.Control;
+                       }
+                       else
+                       {
+                               _native.Main = null;
+                       }
+               }
+
+               void UpdateFlyoutBackgroundColor()
+               {
+                       _native.NavigationView.BackgroundColor = Element.FlyoutBackgroundColor.ToNative();
+               }
+
+               void UpdateFlyoutIsPresented()
+               {
+                       _native.IsOpen = Element.FlyoutIsPresented;
+               }
+
+               void OnFlyoutIsPresentedChanged(object sender, EventArgs e)
+               {
+                       Element.SetValueFromRenderer(Shell.FlyoutIsPresentedProperty, _native.IsOpen);
+               }
+
+               void OnItemSelected(object sender, GenListItemEventArgs e)
+               {
+                       _flyoutMenu.TryGetValue(e.Item.Index - 1, out Element element);
+
+                       if (element != null)
+                       {
+                               ((IShellController)Element).OnFlyoutItemSelected(element);
+                       }
+               }
+
+               void OnShellStructureChanged(object sender, EventArgs e)
+               {
+                       BuildMenu();
+               }
+
+               void BuildMenu()
+               {
+                       var groups = new List<Group>();
+                       var flyoutGroups = ((IShellController)Element).GenerateFlyoutGrouping();
+
+                       _flyoutMenu.Clear();
+
+                       int index = 0;
+                       for (int i = 0; i < flyoutGroups.Count; i++)
+                       {
+                               var flyoutGroup = flyoutGroups[i];
+                               var items = new List<Item>();
+                               for (int j = 0; j < flyoutGroup.Count; j++)
+                               {
+                                       string title = null;
+                                       string icon = null;
+                                       if (flyoutGroup[j] is BaseShellItem shellItem)
+                                       {
+                                               title = shellItem.Title;
+
+                                               if (shellItem.FlyoutIcon is FileImageSource flyoutIcon)
+                                               {
+                                                       icon = flyoutIcon.File;
+                                               }
+                                       }
+                                       else if (flyoutGroup[j] is MenuItem menuItem)
+                                       {
+                                               title = menuItem.Text;
+                                               if (menuItem.Icon != null)
+                                               {
+                                                       icon = menuItem.Icon.File;
+                                               }
+                                       }
+
+                                       items.Add(new Item(title, icon));
+
+                                       _flyoutMenu.Add(index, flyoutGroup[j]);
+                                       index++;
+                               }
+
+                               var group = new Group(items);
+                               groups.Add(group);
+                       }
+
+                       _native.NavigationView.Menu = groups;
+               }
+
+               void IFlyoutController.Open()
+               {
+                       _native.IsOpen = true;
+               }
+       }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Shell/ShellSectionNavigation.cs b/Xamarin.Forms.Platform.Tizen/Shell/ShellSectionNavigation.cs
new file mode 100644 (file)
index 0000000..7d104e7
--- /dev/null
@@ -0,0 +1,347 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.ComponentModel;
+using ElmSharp;
+using Xamarin.Forms.Platform.Tizen.Native;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public class ShellSectionNavigation : Native.Box, IAppearanceObserver
+       {
+               ShellNavBar _navBar = null;
+               ShellSection _section = null;
+               Page _currentPage = null;
+               Page _rootPage = null;
+
+               LinkedList<EvasObject> _navigationStack = new LinkedList<EvasObject>();
+               Dictionary<Page, EvasObject> _pageToNative = new Dictionary<Page, EvasObject>();
+               Dictionary<EvasObject, Page> _nativeToPage = new Dictionary<EvasObject, Page>();
+
+               bool _disposed = false;
+               bool _navBarIsVisible = true;
+               const int _defaultNavBarHeight = 110;
+               int _navBarHeight = _defaultNavBarHeight;
+
+               public ShellSectionNavigation(IFlyoutController flyoutController, ShellSection section) : base(Forms.NativeParent)
+               {
+                       _section = section;
+                       _section.PropertyChanged += OnSectionPropertyChanged;
+                       _rootPage = ((IShellContentController)_section.CurrentItem).GetOrCreateContent();
+
+                       _navBar = new ShellNavBar(flyoutController, this);
+                       _navBar.Show();
+
+                       var renderer = new ShellSectionRenderer(section);
+                       renderer.Control.Show();
+                       _navigationStack.AddLast(renderer.Control);
+                       _pageToNative[_rootPage] = renderer.Control;
+                       _nativeToPage[renderer.Control] = _rootPage;
+
+                       IShellSectionController controller = _section as IShellSectionController;
+                       controller.NavigationRequested += OnNavigationRequested;
+                       controller.AddDisplayedPageObserver(this, UpdateDisplayedPage);
+
+                       PackEnd(_navBar);
+                       PackEnd(renderer.Control);
+                       LayoutUpdated += OnLayoutUpdated;
+                       ((IShellController)_section.Parent.Parent).AddAppearanceObserver(this, _section);
+               }
+
+               ~ShellSectionNavigation()
+               {
+                       Dispose(false);
+               }
+
+               public void Dispose()
+               {
+                       Dispose(true);
+                       GC.SuppressFinalize(this);
+               }
+
+               public bool NavBarIsVisible
+               {
+                       get
+                       {
+                               return _navBarIsVisible;
+                       }
+                       set
+                       {
+                               _navBarIsVisible = value;
+                               UpdateLayout();
+                       }
+               }
+
+               public EvasObject CurrentNative
+               {
+                       get
+                       {
+                               return _navigationStack.Last();
+                       }
+               }
+
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (_disposed)
+                               return;
+
+                       if (disposing)
+                       {
+                               if (_section != null)
+                               {
+                                       IShellSectionController controller = _section as IShellSectionController;
+                                       controller.NavigationRequested -= OnNavigationRequested;
+                                       controller.RemoveDisplayedPageObserver(this);
+
+                                       _section.PropertyChanged -= OnSectionPropertyChanged;
+                                       _section = null;
+                               }
+                               if (_currentPage != null)
+                               {
+                                       _currentPage.PropertyChanged -= OnPagePropertyChanged;
+                               }
+                               Unrealize();
+                       }
+                       _disposed = true;
+               }
+
+               void UpdateDisplayedPage(Page page)
+               {
+                       if (_currentPage != null)
+                       {
+                               _currentPage.PropertyChanged -= OnPagePropertyChanged;
+                       }
+                       _currentPage = page;
+                       _currentPage.PropertyChanged += OnPagePropertyChanged;
+
+                       _navBar.Title = page.Title;
+                       _navBar.SearchHandler = Shell.GetSearchHandler(page);
+                       _navBar.TitleView = Shell.GetTitleView(page);
+                       _navBar.CurrentPage = page;
+                       NavBarIsVisible = Shell.GetNavBarIsVisible(page);
+
+                       if (((IShellContentController)_section.CurrentItem).GetOrCreateContent() != page)
+                               _navBar.HasBackButton = true;
+                       else
+                               _navBar.HasBackButton = false;
+               }
+
+               void IAppearanceObserver.OnAppearanceChanged(ShellAppearance appearance)
+               {
+                       if (appearance == null)
+                               return;
+
+                       if (!appearance.TitleColor.IsDefault)
+                       {
+                               _navBar.TitleColor = appearance.TitleColor.ToNative();
+                       }
+                       else
+                       {
+                               _navBar.TitleColor = ShellRenderer.DefaultTitleColor.ToNative();
+                       }
+
+                       if (!appearance.BackgroundColor.IsDefault)
+                       {
+                               _navBar.BackgroundColor = appearance.BackgroundColor.ToNative();
+                       }
+                       else
+                       {
+                               _navBar.BackgroundColor = ShellRenderer.DefaultBackgroundColor.ToNative();
+                       }
+
+                       if (!appearance.ForegroundColor.IsDefault)
+                       {
+                               _navBar.ForegroundColor = appearance.ForegroundColor.ToNative();
+                       }
+                       else
+                       {
+                               _navBar.ForegroundColor = ShellRenderer.DefaultForegroundColor.ToNative();
+                       }
+               }
+
+               void OnSectionPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == "CurrentItem")
+                       {
+                               var native = _pageToNative[_rootPage];
+                               _pageToNative.Remove(_rootPage);
+                               _rootPage = ((IShellContentController)_section.CurrentItem).GetOrCreateContent();
+                               _pageToNative[_rootPage] = native;
+                               _nativeToPage[native] = _rootPage;
+                       }
+               }
+
+               EvasObject GetOrCreatePage(Page page)
+               {
+                       Native.Page native = Platform.GetOrCreateRenderer(page).NativeView as Native.Page;
+                       _pageToNative[page] = native;
+                       _nativeToPage[native] = page;
+                       native.BackgroundColor = (page.BackgroundColor != Xamarin.Forms.Color.Default ? page.BackgroundColor.ToNative() : ElmSharp.Color.White);
+                       PackEnd(native);
+                       return native;
+               }
+
+               void OnPagePropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == Page.TitleProperty.PropertyName)
+                       {
+                               _navBar.Title = (sender as Page)?.Title;
+                       }
+                       else if (e.PropertyName == Shell.SearchHandlerProperty.PropertyName)
+                       {
+                               _navBar.SearchHandler = Shell.GetSearchHandler(sender as Page);
+                       }
+                       else if (e.PropertyName == Shell.NavBarIsVisibleProperty.PropertyName)
+                       {
+                               NavBarIsVisible = Shell.GetNavBarIsVisible(sender as Page);
+                       }
+                       else if (e.PropertyName == Shell.TitleViewProperty.PropertyName)
+                       {
+                               _navBar.TitleView = Shell.GetTitleView(sender as Page);
+                       }
+               }
+
+               void OnNavigationRequested(object sender, Internals.NavigationRequestedEventArgs e)
+               {
+                       if (e.RequestType == Internals.NavigationRequestType.Push)
+                       {
+                               PushRequest(sender, e);
+                       }
+                       else if (e.RequestType == Internals.NavigationRequestType.Insert)
+                       {
+                               InsertRequest(sender, e);
+                       }
+                       else if (e.RequestType == Internals.NavigationRequestType.Pop)
+                       {
+                               PopRequest(sender, e);
+                       }
+                       else if (e.RequestType == Internals.NavigationRequestType.PopToRoot)
+                       {
+                               PopToRootRequest(sender, e);
+                       }
+                       else if (e.RequestType == Internals.NavigationRequestType.Remove)
+                       {
+                               RemoveRequest(sender, e);
+                       }
+               }
+
+               void RemoveRequest(object sender, Internals.NavigationRequestedEventArgs e)
+               {
+                       if (_pageToNative.ContainsKey(e.Page))
+                       {
+                               EvasObject del = _pageToNative[e.Page];
+                               EvasObject top = CurrentNative;
+
+                               if (del == CurrentNative)
+                               {
+                                       PopRequest(sender, e);
+                               }
+                               else
+                               {
+                                       RemovePage(del);
+                                       UpdateTaskCompletionSource(e, true);
+                               }
+                       }
+               }
+
+               internal void PopRequest(object sender, Internals.NavigationRequestedEventArgs e)
+               {
+                       CurrentNative?.Hide();
+                       RemovePage(CurrentNative);
+
+                       UpdateLayout();
+                       UpdateTaskCompletionSource(e, true);
+               }
+
+               void RemovePage(EvasObject del)
+               {
+                       _pageToNative.Remove(_nativeToPage[del]);
+                       _nativeToPage.Remove(del);
+                       _navigationStack.Remove(del);
+
+                       del.Hide();
+                       del.Unrealize();
+               }
+
+               void PopToRootRequest(object sender, Internals.NavigationRequestedEventArgs e)
+               {
+                       CurrentNative?.Hide();
+
+                       var root = _pageToNative[_rootPage];
+                       foreach (var pair in _pageToNative)
+                       {
+                               if (pair.Value != root)
+                                       pair.Value.Unrealize();
+                       }
+                       _pageToNative.Clear();
+                       _nativeToPage.Clear();
+                       _navigationStack.Clear();
+
+                       _navigationStack.AddLast(root);
+                       _pageToNative[_rootPage] = root;
+                       _nativeToPage[root] = _rootPage;
+
+                       UpdateLayout();
+                       UpdateTaskCompletionSource(e, true);
+               }
+
+               void PushRequest(object sender, Internals.NavigationRequestedEventArgs e)
+               {
+                       CurrentNative?.Hide();
+
+                       EvasObject native = GetOrCreatePage(e.Page);
+                       _navigationStack.AddLast(native);
+
+                       UpdateLayout();
+                       UpdateTaskCompletionSource(e, true);
+               }
+
+               void UpdateTaskCompletionSource(Internals.NavigationRequestedEventArgs e, bool result)
+               {
+                       var tcs = new TaskCompletionSource<bool>();
+                       e.Task = tcs.Task;
+                       tcs.SetResult(result);
+               }
+
+               void InsertRequest(object sender, Internals.NavigationRequestedEventArgs e)
+               {
+                       if (_pageToNative.ContainsKey(e.BeforePage))
+                       {
+                               var before = _navigationStack.Find(_pageToNative[e.BeforePage]);
+                               EvasObject after = GetOrCreatePage(e.Page);
+                               _navigationStack.AddBefore(before, after);
+
+                               UpdateTaskCompletionSource(e, true);
+                       }
+                       else
+                       {
+                               PushRequest(sender, e);
+                       }
+               }
+
+               void UpdateLayout()
+               {
+                       OnLayoutUpdated(this, new LayoutEventArgs() { Geometry = Geometry });
+               }
+
+               void OnLayoutUpdated(object sender, LayoutEventArgs e)
+               {
+                       if (_navBarIsVisible)
+                       {
+                               _navBarHeight = _defaultNavBarHeight;
+                               _navBar.Show();
+                               _navBar.Move(e.Geometry.X, e.Geometry.Y);
+                               _navBar.Resize(e.Geometry.Width, _navBarHeight);
+                       }
+                       else
+                       {
+                               _navBarHeight = 0;
+                               _navBar.Hide();
+                       }
+                       CurrentNative.Show();
+                       CurrentNative.Move(e.Geometry.X, e.Geometry.Y + _navBarHeight);
+                       CurrentNative.Resize(e.Geometry.Width, e.Geometry.Height - _navBarHeight);
+               }
+       }
+}
diff --git a/Xamarin.Forms.Platform.Tizen/Shell/ShellSectionRenderer.cs b/Xamarin.Forms.Platform.Tizen/Shell/ShellSectionRenderer.cs
new file mode 100644 (file)
index 0000000..308df7c
--- /dev/null
@@ -0,0 +1,347 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using ElmSharp;
+using EToolbarItem = ElmSharp.ToolbarItem;
+using EColor = ElmSharp.Color;
+using Xamarin.Forms.Platform.Tizen.Native;
+using System.Collections.Specialized;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public class ShellSectionRenderer : IAppearanceObserver
+       {
+               Native.Box _box = null;
+               Toolbar _toolbar = null;
+               Native.Page _currentContent = null;
+               ShellSection _section = null;
+
+               Dictionary<ShellContent, Native.Page> _contentToPage = new Dictionary<ShellContent, Native.Page>();
+               Dictionary<ShellContent, EToolbarItem> _contentToItem = new Dictionary<ShellContent, EToolbarItem>();
+               Dictionary<EToolbarItem, ShellContent> _itemToContent = new Dictionary<EToolbarItem, ShellContent>();
+               LinkedList<EToolbarItem> _toolbarItemList = new LinkedList<EToolbarItem>();
+
+               EColor _backgroundColor = ShellRenderer.DefaultBackgroundColor.ToNative();
+               EColor _foregroundCollor = ShellRenderer.DefaultForegroundColor.ToNative();
+
+               bool _disposed = false;
+
+               public ShellSectionRenderer(ShellSection section)
+               {
+                       _section = section;
+                       _section.PropertyChanged += OnSectionPropertyChanged;
+                       (_section.Items as INotifyCollectionChanged).CollectionChanged += OnShellSectionCollectionChanged;
+
+                       _box = new Native.Box(Forms.NativeParent);
+                       _box.LayoutUpdated += OnLayoutUpdated;
+
+                       CreateToolbar();
+                       UpdateCurrentShellContent(_section.CurrentItem);
+
+                       ((IShellController)_section.Parent.Parent).AddAppearanceObserver(this, _section);
+               }
+
+               ~ShellSectionRenderer()
+               {
+                       Dispose(false);
+               }
+
+               public void Dispose()
+               {
+                       Dispose(true);
+                       GC.SuppressFinalize(this);
+               }
+
+               public Native.Box Control
+               {
+                       get
+                       {
+                               return _box;
+                       }
+               }
+
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (_disposed)
+                               return;
+
+                       if (disposing)
+                       {
+                               if (_section != null)
+                               {
+                                       Control.LayoutUpdated -= OnLayoutUpdated;
+                                       ((IShellController)_section.Parent.Parent).RemoveAppearanceObserver(this);
+                                       _section.PropertyChanged -= OnSectionPropertyChanged;
+                                       _section = null;
+
+                                       foreach (var pair in _contentToPage)
+                                       {
+                                               var content = pair.Value as Native.Page;
+                                               content.Unrealize();
+                                       }
+
+                                       if (_toolbar != null)
+                                       {
+                                               _toolbar.Selected -= OnTabsSelected;
+                                       }
+                                       _contentToPage.Clear();
+                                       _contentToItem.Clear();
+                                       _itemToContent.Clear();
+                                       _toolbarItemList.Clear();
+                               }
+                               Control.Unrealize();
+                       }
+                       _disposed = true;
+               }
+
+               void OnSectionPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == "CurrentItem")
+                       {
+                               UpdateCurrentItem(_section.CurrentItem);
+                       }
+               }
+
+               void UpdateCurrentItem(ShellContent content)
+               {
+                       UpdateCurrentShellContent(content);
+                       if (_contentToItem.ContainsKey(content))
+                       {
+                               _contentToItem[content].IsSelected = true;
+                       }
+                       UpdateLayout();
+               }
+
+               void IAppearanceObserver.OnAppearanceChanged(ShellAppearance appearance)
+               {
+                       if (appearance == null)
+                               return;
+
+                       if (!appearance.BackgroundColor.IsDefault)
+                       {
+                               BackgroundColor = appearance.BackgroundColor.ToNative();
+                       }
+                       else
+                       {
+                               BackgroundColor = ShellRenderer.DefaultBackgroundColor.ToNative();
+                       }
+
+                       if (!appearance.ForegroundColor.IsDefault)
+                       {
+                               ForegroundColor = appearance.ForegroundColor.ToNative();
+                       }
+                       else
+                       {
+                               ForegroundColor = ShellRenderer.DefaultForegroundColor.ToNative();
+                       }
+               }
+
+               public EColor BackgroundColor
+               {
+                       get
+                       {
+                               return _backgroundColor;
+                       }
+                       set
+                       {
+                               _backgroundColor = value;
+                               UpdateToolbarBackgroudColor(_backgroundColor);
+                       }
+               }
+
+               public EColor ForegroundColor
+               {
+                       get
+                       {
+                               return _foregroundCollor;
+                       }
+                       set
+                       {
+                               _foregroundCollor = value;
+                               UpdateToolbarForegroundColor(_foregroundCollor);
+                       }
+               }
+
+               void UpdateToolbarBackgroudColor(EColor color)
+               {
+                       foreach (EToolbarItem item in _toolbarItemList)
+                       {
+                               item.SetPartColor("bg", color);
+                       }
+               }
+
+               void UpdateToolbarForegroundColor(EColor color)
+               {
+                       foreach (EToolbarItem item in _toolbarItemList)
+                       {
+                               if (item != _toolbar.SelectedItem)
+                               {
+                                       item.SetPartColor("underline", EColor.Transparent);
+                               }
+                               else
+                               {
+                                       item.SetPartColor("underline", color);
+                               }
+                       }
+               }
+
+               void CreateToolbar()
+               {
+                       if (_toolbar != null)
+                               return;
+
+                       _toolbar = new Toolbar(Forms.NativeParent)
+                       {
+                               BackgroundColor = _backgroundColor,
+                               ShrinkMode = ToolbarShrinkMode.Expand,
+                               Style = "material"
+                       };
+                       _toolbar.Show();
+                       _toolbar.Selected += OnTabsSelected;
+                       Control.PackEnd(_toolbar);
+
+                       ResetToolbarItem();
+               }
+
+               void ResetToolbarItem()
+               {
+                       foreach (ShellContent content in _section.Items)
+                       {
+                               InsertToolbarItem(content);
+                       }
+                       if (_section.Items.Count > 3)
+                       {
+                               _toolbar.ShrinkMode = ToolbarShrinkMode.Scroll;
+                       }
+               }
+
+               EToolbarItem InsertToolbarItem(ShellContent content)
+               {
+                       EToolbarItem item = _toolbar.Append(content.Title, null);
+                       item.SetPartColor("bg", _backgroundColor);
+
+                       _toolbarItemList.AddLast(item);
+                       _itemToContent.Add(item, content);
+                       _contentToItem.Add(content, item);
+
+                       return item;
+               }
+
+               void RemoveToolbarItem(ShellContent section)
+               {
+                       if (_contentToItem.ContainsKey(section))
+                       {
+                               var del = _contentToItem[section];
+                               _toolbarItemList.Remove(del);
+                               _itemToContent.Remove(del);
+                               _contentToItem.Remove(section);
+                               del.Delete();
+                       }
+               }
+
+               void OnShellSectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+               {
+                       switch (e.Action)
+                       {
+                               case NotifyCollectionChangedAction.Add:
+                                       AddToolbarItems(e);
+                                       break;
+
+                               case NotifyCollectionChangedAction.Remove:
+                                       RemoveToolbarItems(e);
+                                       break;
+
+                               default:
+                                       break;
+                       }
+               }
+
+               void AddToolbarItems(NotifyCollectionChangedEventArgs e)
+               {
+                       foreach (var item in e.NewItems)
+                       {
+                               InsertToolbarItem(item as ShellContent);
+                       }
+                       UpdateLayout();
+               }
+
+               void RemoveToolbarItems(NotifyCollectionChangedEventArgs e)
+               {
+                       foreach (var item in e.OldItems)
+                       {
+                               RemoveToolbarItem(item as ShellContent);
+                       }
+                       UpdateLayout();
+               }
+
+               void OnTabsSelected(object sender, ToolbarItemEventArgs e)
+               {
+                       if (_toolbar.SelectedItem == null)
+                       {
+                               return;
+                       }
+
+                       ShellContent content = _itemToContent[_toolbar.SelectedItem];
+                       if (_section.CurrentItem != content)
+                       {
+                               _section.SetValueFromRenderer(ShellSection.CurrentItemProperty, content);
+                       }
+               }
+
+               void UpdateCurrentShellContent(ShellContent content)
+               {
+                       _currentContent?.Hide();
+
+                       if (content == null)
+                       {
+                               _currentContent = null;
+                               return;
+                       }
+
+                       Native.Page native = null;
+                       if (_contentToPage.ContainsKey(content))
+                       {
+                               native = _contentToPage[content];
+                       }
+                       else
+                       {
+                               native = CreateShellContent(content);
+                               Control.PackEnd(native);
+                               _contentToPage.Add(content, native);
+                       }
+                       _currentContent = native;
+                       _currentContent.Show();
+                       return;
+               }
+
+               Native.Page CreateShellContent(ShellContent content)
+               {
+                       Page xpage = ((IShellContentController)content).GetOrCreateContent();
+                       Native.Page page = Platform.GetOrCreateRenderer(xpage).NativeView as Native.Page;
+                       page.BackgroundColor = (xpage.BackgroundColor != Color.Default ? xpage.BackgroundColor.ToNative() : EColor.White);
+                       return page;
+               }
+
+               void UpdateLayout()
+               {
+                       OnLayoutUpdated(this, new LayoutEventArgs() { Geometry = Control.Geometry });
+               }
+
+               void OnLayoutUpdated(object sender, LayoutEventArgs e)
+               {
+                       int toolbarHeight = 0;
+                       if (_section.Items.Count <= 1)
+                       {
+                               toolbarHeight = 0;
+                       }
+                       else
+                       {
+                               toolbarHeight = _toolbar.MinimumHeight;
+                       }
+                       _toolbar.Move(e.Geometry.X, e.Geometry.Y);
+                       _toolbar.Resize(e.Geometry.Width, toolbarHeight);
+                       _currentContent?.Move(e.Geometry.X, e.Geometry.Y + toolbarHeight);
+                       _currentContent?.Resize(e.Geometry.Width, e.Geometry.Height - toolbarHeight);
+               }
+       }
+}
index 7056bd8..6d8eedd 100644 (file)
   </PropertyGroup>
 
   <ItemGroup>
+    <None Remove="Resource\arrow_left.png" />
+    <None Remove="Resource\dots_horizontal.png" />
+    <None Remove="Resource\menu.png" />
+  </ItemGroup>
+
+  <ItemGroup>
     <Compile Include="..\Xamarin.Forms.Core\Properties\GlobalAssemblyInfo.cs">
       <Link>Properties\GlobalAssemblyInfo.cs</Link>
     </Compile>
   </ItemGroup>
 
   <ItemGroup>
+    <EmbeddedResource Include="Resource\arrow_left.png" />
+    <EmbeddedResource Include="Resource\dots_horizontal.png" />
+    <EmbeddedResource Include="Resource\menu.png" />
+  </ItemGroup>
+
+  <ItemGroup>
     <PackageReference Include="Tizen.NET" Version="4.0.0" />
   </ItemGroup>