return _editfieldLayout;
}
+ protected ELayout EditfieldLayout
+ {
+ get
+ {
+ return _editfieldLayout;
+ }
+ }
+
protected virtual ELayout CreateEditFieldLayout(EvasObject parent)
{
var layout = new ELayout(parent);
/// 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 ?? "");
}
[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))]
--- /dev/null
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public interface IFlyoutController
+ {
+ void Open();
+ }
+}
--- /dev/null
+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;
+ }
+ }
+ }
+}
--- /dev/null
+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;
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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 });
+ }
+ }
+}
--- /dev/null
+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;
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
</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>