diff --git a/src/Controls/src/Core/Handlers/Shell/ShellHandler.Tizen.cs b/src/Controls/src/Core/Handlers/Shell/ShellHandler.Tizen.cs index 712d2ab78e9d..f6c1d5b97678 100644 --- a/src/Controls/src/Core/Handlers/Shell/ShellHandler.Tizen.cs +++ b/src/Controls/src/Core/Handlers/Shell/ShellHandler.Tizen.cs @@ -1,5 +1,6 @@ using Microsoft.Maui.Controls.Platform; using Microsoft.Maui.Handlers; +using Tizen.UIExtensions.Common; namespace Microsoft.Maui.Controls.Handlers { @@ -8,12 +9,19 @@ public partial class ShellHandler : ViewHandler public override void SetVirtualView(IView view) { base.SetVirtualView(view); - NativeView.SetElement((Shell)view, MauiContext); + NativeView?.SetElement((Shell)view, MauiContext); } protected override ShellView CreateNativeView() { - return new ShellView(NativeParent); + if (DeviceInfo.GetDeviceType() == DeviceType.TV) + { + return new TVShellView(NativeParent); + } + else + { + return new ShellView(NativeParent); + } } } } diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/ShellFlyoutItemAdaptor.cs b/src/Controls/src/Core/Platform/Tizen/Shell/ShellFlyoutItemAdaptor.cs index 83a140ecaa7b..2913f744683e 100644 --- a/src/Controls/src/Core/Platform/Tizen/Shell/ShellFlyoutItemAdaptor.cs +++ b/src/Controls/src/Core/Platform/Tizen/Shell/ShellFlyoutItemAdaptor.cs @@ -1,9 +1,10 @@ -using System; +#nullable enable + +using System; using System.Collections; using System.Collections.Generic; -using Microsoft.Maui.Controls.Internals; -using Microsoft.Maui.Handlers; using ElmSharp; +using Microsoft.Maui.Controls.Internals; using Tizen.UIExtensions.ElmSharp; namespace Microsoft.Maui.Controls.Platform @@ -11,19 +12,19 @@ namespace Microsoft.Maui.Controls.Platform public class ShellFlyoutItemAdaptor : ItemAdaptor { Dictionary _nativeFormsTable = new Dictionary(); - Dictionary _dataBindedViewTable = new Dictionary(); + Dictionary _dataBindedViewTable = new Dictionary(); Shell _shell; - View _headerCache; + View? _headerCache; IMauiContext _context; protected Shell Shell => _shell; public bool HasHeader { get; set; } - protected virtual DataTemplate DefaultItemTemplate => null; + protected virtual DataTemplate? DefaultItemTemplate => null; - protected virtual DataTemplate DefaultMenuItemTemplate => null; + protected virtual DataTemplate? DefaultMenuItemTemplate => null; public ShellFlyoutItemAdaptor(Shell shell, IMauiContext context, IEnumerable items, bool hasHeader) : base(items) { @@ -32,32 +33,37 @@ public ShellFlyoutItemAdaptor(Shell shell, IMauiContext context, IEnumerable ite HasHeader = hasHeader; } - public override EvasObject CreateNativeView(EvasObject parent) + public override EvasObject? CreateNativeView(EvasObject parent) { return CreateNativeView(0, parent); } - DataTemplate GetDataTemplate(int index) + DataTemplate? GetDataTemplate(int index) { - var item = (BindableObject)this[index]; - DataTemplate dataTemplate = (Shell as IShellController)?.GetFlyoutItemDataTemplate(item); - if (item is IMenuItemController) + var item = this[index]; + if (item != null && item is BindableObject bo) { - if (DefaultMenuItemTemplate != null && Shell.MenuItemTemplate == dataTemplate) - dataTemplate = DefaultMenuItemTemplate; + DataTemplate? dataTemplate = (Shell as IShellController)?.GetFlyoutItemDataTemplate(bo); + if (item is IMenuItemController) + { + if (DefaultMenuItemTemplate != null && Shell.MenuItemTemplate == dataTemplate) + dataTemplate = DefaultMenuItemTemplate; + } + else + { + if (DefaultItemTemplate != null && Shell.ItemTemplate == dataTemplate) + dataTemplate = DefaultItemTemplate; + } + + var template = dataTemplate.SelectDataTemplate(item, Shell); + + return template; } - else - { - if (DefaultItemTemplate != null && Shell.ItemTemplate == dataTemplate) - dataTemplate = DefaultItemTemplate; - } - - var template = dataTemplate.SelectDataTemplate(item, Shell); - return template; + return null; } - public override EvasObject CreateNativeView(int index, EvasObject parent) + public override EvasObject? CreateNativeView(int index, EvasObject parent) { var template = GetDataTemplate(index); @@ -74,12 +80,12 @@ public override EvasObject CreateNativeView(int index, EvasObject parent) return null; } - public override EvasObject GetFooterView(EvasObject parent) + public override EvasObject? GetFooterView(EvasObject parent) { return null; } - public override EvasObject GetHeaderView(EvasObject parent) + public override EvasObject? GetHeaderView(EvasObject parent) { if (!HasHeader) return null; @@ -112,7 +118,8 @@ public override Size MeasureItem(int widthConstraint, int heightConstraint) public override Size MeasureItem(int index, int widthConstraint, int heightConstraint) { - if (_dataBindedViewTable.TryGetValue(this[index], out View createdView) && createdView != null) + var item = this[index]; + if (item != null && _dataBindedViewTable.TryGetValue(item, out View? createdView) && createdView != null) { return createdView.Measure(DPExtensions.ConvertToScaledDP(widthConstraint), DPExtensions.ConvertToScaledDP(heightConstraint), MeasureFlags.IncludeMargins).Request.ToEFLPixel(); } @@ -127,11 +134,15 @@ public override void RemoveNativeView(EvasObject native) public override void SetBinding(EvasObject native, int index) { - if (_nativeFormsTable.TryGetValue(native, out View view)) + if (_nativeFormsTable.TryGetValue(native, out View? view)) { ResetBindedView(view); - view.BindingContext = this[index]; - _dataBindedViewTable[this[index]] = view; + var item = this[index]; + if (item != null) + { + view.BindingContext = item; + _dataBindedViewTable[item] = view; + } view.MeasureInvalidated += OnItemMeasureInvalidated; Shell.AddLogicalChild(view); @@ -140,7 +151,7 @@ public override void SetBinding(EvasObject native, int index) public override void UnBinding(EvasObject native) { - if (_nativeFormsTable.TryGetValue(native, out View view)) + if (_nativeFormsTable.TryGetValue(native, out View? view)) { view.MeasureInvalidated -= OnItemMeasureInvalidated; ResetBindedView(view); @@ -157,13 +168,16 @@ void ResetBindedView(View view) } } - void OnItemMeasureInvalidated(object sender, EventArgs e) + void OnItemMeasureInvalidated(object? sender, EventArgs e) { var data = (sender as View)?.BindingContext ?? null; - int index = GetItemIndex(data); - if (index != -1) + if (data != null) { - CollectionView?.ItemMeasureInvalidated(index); + int index = GetItemIndex(data); + if (index != -1) + { + CollectionView?.ItemMeasureInvalidated(index); + } } } } diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/ShellItemView.cs b/src/Controls/src/Core/Platform/Tizen/Shell/ShellItemView.cs index b95a278daa12..a6db5b93697f 100644 --- a/src/Controls/src/Core/Platform/Tizen/Shell/ShellItemView.cs +++ b/src/Controls/src/Core/Platform/Tizen/Shell/ShellItemView.cs @@ -1,4 +1,6 @@ -using System; +#nullable enable + +using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; @@ -17,13 +19,13 @@ namespace Microsoft.Maui.Controls.Platform { public class ShellItemView : IAppearanceObserver, IDisposable { - Tabs _tabs = null; - EBox _mainLayout = null; - EBox _contentHolder = null; - Panel _moreItemsDrawer = null; - ShellMoreTabs _moreItemsList = null; - EToolbarItem _moreTabItem = null; - ShellSectionStack _currentStack = null; + Tabs? _tabs = null; + EBox _mainLayout; + EBox _contentHolder; + Panel? _moreItemsDrawer = null; + ShellMoreTabs? _moreItemsList = null; + EToolbarItem? _moreTabItem = null; + ShellSectionStack? _currentStack = null; Dictionary _sectionsTable = new Dictionary(); Dictionary _tabItemsTable = new Dictionary(); @@ -41,7 +43,13 @@ public ShellItemView(ShellItem item, IMauiContext context) ShellItem = item; MauiContext = context; - Initialize(); + //Initialize(); + _mainLayout = new EBox(NativeParent); + _mainLayout.SetLayoutCallback(OnLayout); + _mainLayout.Show(); + _contentHolder = new EBox(NativeParent); + _contentHolder.Show(); + _mainLayout.PackEnd(_contentHolder); ShellItem.PropertyChanged += OnShellItemPropertyChanged; if (ShellItem.Items is INotifyCollectionChanged notifyCollectionChanged) @@ -61,7 +69,7 @@ public ShellItemView(ShellItem item, IMauiContext context) protected IMauiContext MauiContext { get; private set; } - protected EvasObject NativeParent + protected EvasObject? NativeParent { get => MauiContext?.Context?.BaseLayout; } @@ -170,53 +178,48 @@ void UpdateCurrentItem(ShellSection section) { UpdateCurrentShellSection(section); - if (HasTabs) + if (_tabs != null) { if (_tabItemsTable.ContainsKey(section)) { _tabItemsTable[section].IsSelected = true; } - else if (HasMoreItems) + else if (_moreItemsDrawer != null) { _disableMoreItemOpen = true; - _moreTabItem.IsSelected = true; + + if (_moreTabItem != null) + _moreTabItem.IsSelected = true; + _disableMoreItemOpen = false; } - if (HasMoreItems) + if (_moreItemsDrawer != null) { _moreItemsDrawer.IsOpen = false; } } } - void UpdateCurrentItemFromUI(ShellSection section) + void UpdateCurrentItemFromUI(ShellSection? section) { - if (ShellItem.CurrentItem != section) + if (section != null && ShellItem.CurrentItem != section) { ShellItem.SetValueFromRenderer(ShellItem.CurrentItemProperty, section); } - if (HasMoreItems) + if (_moreItemsDrawer != null) { _moreItemsDrawer.IsOpen = false; } } - void Initialize() - { - _mainLayout = new EBox(NativeParent); - _mainLayout.SetLayoutCallback(OnLayout); - _mainLayout.Show(); - _contentHolder = new EBox(NativeParent); - _contentHolder.Show(); - _mainLayout.PackEnd(_contentHolder); - } - void InitializeTabs() { if (_tabs != null) return; + _ = NativeParent ?? throw new InvalidOperationException($"{nameof(NativeParent)} should have been set by base class."); + _tabs = new Tabs(NativeParent); _tabs.Show(); _tabs.BackgroundColor = _tabBarBackgroudColor; @@ -263,20 +266,20 @@ void DestroyMoreItems() _mainLayout.UnPack(_moreItemsDrawer); - _moreItemsList.Unrealize(); - _moreItemsDrawer.Unrealize(); + _moreItemsList?.Unrealize(); + _moreItemsDrawer?.Unrealize(); _moreItemsList = null; _moreItemsDrawer = null; } - void OnMoreItemSelected(object sender, GenListItemEventArgs e) + void OnMoreItemSelected(object? sender, GenListItemEventArgs e) { - ShellSection section = e.Item.Data as ShellSection; + ShellSection? section = e.Item.Data as ShellSection; UpdateCurrentItemFromUI(section); } - void OnShellItemPropertyChanged(object sender, PropertyChangedEventArgs e) + void OnShellItemPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(ShellItem.CurrentItem)) { @@ -288,8 +291,9 @@ void IAppearanceObserver.OnAppearanceChanged(ShellAppearance appearance) { var tabBarBackgroudColor = (appearance as IShellAppearanceElement)?.EffectiveTabBarBackgroundColor; var tabBarTitleColor = (appearance as IShellAppearanceElement)?.EffectiveTabBarTitleColor; - TabBarBackgroundColor = tabBarBackgroudColor.IsDefault() ? ShellView.DefaultBackgroundColor : tabBarBackgroudColor.ToNativeEFL(); - TabBarTitleColor = tabBarTitleColor.IsDefault() ? ShellView.DefaultTitleColor : tabBarTitleColor.ToNativeEFL(); + + TabBarBackgroundColor = tabBarBackgroudColor.IsDefault() ? ShellView.DefaultBackgroundColor : (tabBarBackgroudColor?.ToNativeEFL()).GetValueOrDefault(); + TabBarTitleColor = tabBarTitleColor.IsDefault() ? ShellView.DefaultTitleColor : (tabBarTitleColor?.ToNativeEFL()).GetValueOrDefault(); } void UpdateTabsBackgroudColor(EColor color) @@ -322,7 +326,7 @@ void ResetTabs() _moreTabItem = null; } - void OnShellItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + void OnShellItemsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { UpdateTabsItems(); } @@ -332,11 +336,14 @@ void AddTabsItem(ShellSection section) if (_tabsItems.Count < 5) { var item = AppendTabsItem(section.Title, section.Icon); - _sectionsTable.Add(item, section); - _tabItemsTable.Add(section, item); - _tabsItems.Add(item); + if (item != null) + { + _sectionsTable.Add(item, section); + _tabItemsTable.Add(section, item); + _tabsItems.Add(item); + } } - else if (!HasMoreItems) + else if (_moreItemsDrawer == null) { CreateMoreItems(); @@ -352,14 +359,15 @@ void AddTabsItem(ShellSection section) var assembly = typeof(ShellItemView).GetTypeInfo().Assembly; var assemblyName = assembly.GetName().Name; _moreTabItem = AppendTabsItem("More", ImageSource.FromResource(assemblyName + "." + _dotsIcon, assembly)); - _tabsItems.Add(_moreTabItem); + if (_moreTabItem != null) + _tabsItems.Add(_moreTabItem); - _moreItemsList.AddItem(lastSection); - _moreItemsList.AddItem(section); + _moreItemsList?.AddItem(lastSection); + _moreItemsList?.AddItem(section); } else { - _moreItemsList.AddItem(section); + _moreItemsList?.AddItem(section); } } @@ -392,12 +400,12 @@ void UpdateCurrentShellSection(ShellSection section) _contentHolder.PackEnd(_currentStack); } - void OnTabsSelected(object sender, EToolbarItemEventArgs e) + void OnTabsSelected(object? sender, EToolbarItemEventArgs e) { - if (_tabs.SelectedItem == null) + if (_tabs?.SelectedItem == null) return; - if (HasMoreItems && e.Item == _moreTabItem) + if (_moreItemsDrawer != null && e.Item == _moreTabItem) { if (!_disableMoreItemOpen) { @@ -417,14 +425,14 @@ void OnLayout() int tabsHeight = 0; var bound = _mainLayout.Geometry; - if (HasTabs) + if (_tabs != null) { tabsHeight = _tabs.MinimumHeight; var tabsBound = bound; tabsBound.Y += (bound.Height - tabsHeight); tabsBound.Height = tabsHeight; _tabs.Geometry = tabsBound; - if (HasMoreItems) + if (_moreItemsDrawer != null && _moreItemsList != null) { int moreItemListHeight = _moreItemsList.HeightRequest; moreItemListHeight = Math.Min(moreItemListHeight, bound.Height - tabsHeight); @@ -438,24 +446,30 @@ void OnLayout() _contentHolder.Geometry = bound; } - EToolbarItem AppendTabsItem(string text, ImageSource iconSource) + EToolbarItem? AppendTabsItem(string text, ImageSource iconSource) { - var item = _tabs.Append(text); - if (iconSource != null) + _ = NativeParent ?? throw new InvalidOperationException($"{nameof(NativeParent)} should have been set by base class."); + + var item = _tabs?.Append(text); + if (item != null) { - TImage image = new TImage(NativeParent); - var provider = MauiContext.Services.GetRequiredService(); - var service = provider.GetRequiredImageSourceService(iconSource); + if (iconSource != null) + { + TImage image = new TImage(NativeParent); + var provider = MauiContext.Services.GetRequiredService(); + var service = provider.GetRequiredImageSourceService(iconSource); - _ = service.GetImageAsync(iconSource, image); + _ = service.GetImageAsync(iconSource, image); - item.SetIconPart(image); - } - item.SetBackgroundColor(_tabBarBackgroudColor); - item.SetUnderlineColor(EColor.Transparent); - item.SetTextColor(_tabBarTitleColor); + item.SetIconPart(image); + } - return item; + item.SetBackgroundColor(_tabBarBackgroudColor); + item.SetUnderlineColor(EColor.Transparent); + item.SetTextColor(_tabBarTitleColor); + return item; + } + return null; } } } diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/ShellMoreTabs.cs b/src/Controls/src/Core/Platform/Tizen/Shell/ShellMoreTabs.cs index b532731b4cfb..408cf903996d 100644 --- a/src/Controls/src/Core/Platform/Tizen/Shell/ShellMoreTabs.cs +++ b/src/Controls/src/Core/Platform/Tizen/Shell/ShellMoreTabs.cs @@ -1,9 +1,9 @@ using ElmSharp; -using Tizen.UIExtensions.ElmSharp; using EBox = ElmSharp.Box; using EImage = ElmSharp.Image; using TImage = Tizen.UIExtensions.ElmSharp.Image; using TLabel = Tizen.UIExtensions.ElmSharp.Label; +using TThemeConstants = Tizen.UIExtensions.ElmSharp.ThemeConstants; namespace Microsoft.Maui.Controls.Platform { @@ -20,7 +20,7 @@ public ShellMoreTabs(EvasObject parent) : base(parent) Homogeneous = true; SelectionMode = GenItemSelectionMode.Always; BackgroundColor = ShellView.DefaultBackgroundColor; - _defaultClass = new GenItemClass(ThemeConstants.GenItemClass.Styles.Full) + _defaultClass = new GenItemClass(TThemeConstants.GenItemClass.Styles.Full) { GetContentHandler = GetContent, }; @@ -37,7 +37,7 @@ public int HeightRequest { get { - var cellHeight = this.GetIconSize() * 2 + this.GetIconSize(); + var cellHeight = this.GetDefaultIconSize() * 2 + this.GetDefaultIconSize(); return DPExtensions.ConvertToScaledPixel(cellHeight) * Count; } } @@ -66,8 +66,8 @@ EvasObject GetContent(object data, string part) }; title.Show(); box.PackEnd(title); - int iconPadding = DPExtensions.ConvertToScaledPixel(this.GetIconPadding()); - int iconSize = DPExtensions.ConvertToScaledPixel(this.GetIconSize()); + int iconPadding = DPExtensions.ConvertToScaledPixel(this.GetDefaultIconPadding()); + int iconSize = DPExtensions.ConvertToScaledPixel(this.GetDefaultIconSize()); int cellHeight = iconPadding * 2 + iconSize; box.SetLayoutCallback(() => { diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/ShellNavBar.cs b/src/Controls/src/Core/Platform/Tizen/Shell/ShellNavBar.cs index bc0e27c5077c..f8356c66d682 100644 --- a/src/Controls/src/Core/Platform/Tizen/Shell/ShellNavBar.cs +++ b/src/Controls/src/Core/Platform/Tizen/Shell/ShellNavBar.cs @@ -1,4 +1,6 @@ -using System; +#nullable enable + +using System; using System.Reflection; using ElmSharp; using Microsoft.Extensions.DependencyInjection; @@ -14,15 +16,15 @@ namespace Microsoft.Maui.Controls.Platform { public class ShellNavBar : EBox, IFlyoutBehaviorObserver, IDisposable { - TImage _menuIcon = null; - TButton _menuButton = null; - TLabel _title = null; - ShellSearchView _searchView = null; - EvasObject _nativeTitleView = null; + TImage? _menuIcon; + TButton _menuButton; + TLabel _title; + ShellSearchView? _searchView = null; + EvasObject? _nativeTitleView = null; - SearchHandler _searchHandler = null; - View _titleView = null; - Page _page = null; + SearchHandler? _searchHandler = null; + View? _titleView = null; + Page? _page = null; FlyoutBehavior _flyoutBehavior = FlyoutBehavior.Flyout; @@ -42,6 +44,8 @@ public ShellNavBar(IMauiContext context) : base(context?.Context?.BaseLayout) { MauiContext = context; + _ = NativeParent ?? throw new ArgumentNullException(nameof(NativeParent)); + SetLayoutCallback(OnLayout); _menuButton = new TButton(NativeParent); @@ -70,9 +74,9 @@ public ShellNavBar(IMauiContext context) : base(context?.Context?.BaseLayout) Dispose(false); } - protected IMauiContext MauiContext { get; private set; } + protected IMauiContext? MauiContext { get; private set; } - protected EvasObject NativeParent + protected EvasObject? NativeParent { get => MauiContext?.Context?.BaseLayout; } @@ -105,7 +109,7 @@ public FlyoutBehavior FlyoutBehavior } } - public SearchHandler SearchHandler + public SearchHandler? SearchHandler { get { @@ -119,7 +123,7 @@ public SearchHandler SearchHandler } } - public View TitleView + public View? TitleView { get { @@ -137,7 +141,7 @@ public string Title { get { - return _title?.Text; + return _title.Text; } set { @@ -213,13 +217,15 @@ protected virtual void Dispose(bool disposing) async void UpdateMenuIcon() { - ImageSource source = null; + _ = NativeParent ?? throw new InvalidOperationException($"{nameof(NativeParent)} should have been set by base class."); + + ImageSource? source = null; if (HasBackButton) { if (_isTV) { - _menuButton.Style = ThemeConstants.Button.Styles.Default; - _menuButton.Text = ThemeConstants.Shell.Resources.TV.BackIconCode; + _menuButton.Style = TThemeConstants.Button.Styles.Default; + _menuButton.Text = TThemeConstants.Shell.Resources.TV.BackIconCode; _menuIcon = null; } else @@ -237,7 +243,7 @@ async void UpdateMenuIcon() { if (_isTV) { - _menuButton.Style = ThemeConstants.Button.Styles.Circle; + _menuButton.Style = TThemeConstants.Button.Styles.Circle; _menuIcon = new TImage(NativeParent); } source = Shell.Current.FlyoutIcon; @@ -246,8 +252,8 @@ async void UpdateMenuIcon() { if (_isTV) { - _menuButton.Style = ThemeConstants.Button.Styles.Default; - _menuButton.Text = ThemeConstants.Shell.Resources.TV.MenuIconCode; + _menuButton.Style = TThemeConstants.Button.Styles.Default; + _menuButton.Text = TThemeConstants.Shell.Resources.TV.MenuIconCode; _menuIcon = null; } else @@ -262,16 +268,18 @@ async void UpdateMenuIcon() if (source != null && _menuIcon != null) { _menuIcon.Show(); - var provider = MauiContext.Services.GetRequiredService(); - var service = provider.GetRequiredImageSourceService(source); - - await service.GetImageAsync(source, _menuIcon); + var provider = MauiContext?.Services.GetRequiredService(); + var service = provider?.GetRequiredImageSourceService(source); + if (service != null) + { + await service.GetImageAsync(source, _menuIcon); + } } _menuButton.SetIconPart(_menuIcon); _menuButton.Show(); } - void OnMenuClicked(object sender, EventArgs e) + void OnMenuClicked(object? sender, EventArgs e) { var backButtonHandler = Shell.GetBackButtonBehavior(_page); if (backButtonHandler?.Command != null) @@ -288,8 +296,10 @@ void OnMenuClicked(object sender, EventArgs e) } } - void UpdateTitleView(View titleView) + void UpdateTitleView(View? titleView) { + _ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); + _nativeTitleView?.Unrealize(); _nativeTitleView = null; @@ -301,7 +311,7 @@ void UpdateTitleView(View titleView) } } - void UpdateSearchHandler(SearchHandler handler) + void UpdateSearchHandler(SearchHandler? handler) { if (_searchView != null) { @@ -313,7 +323,7 @@ void UpdateSearchHandler(SearchHandler handler) if (handler != null) { _searchView = new ShellSearchView(handler, MauiContext); - _searchView.NativeView.Show(); + _searchView.NativeView?.Show(); PackEnd(_searchView.NativeView); } } @@ -322,13 +332,13 @@ void UpdateChildren() { if (_searchHandler != null) { - _searchView.NativeView.Show(); + _searchView?.NativeView?.Show(); _title?.Hide(); _nativeTitleView?.Hide(); } else if (_titleView != null) { - _nativeTitleView.Show(); + _nativeTitleView?.Show(); _title?.Hide(); _searchView?.NativeView?.Hide(); } @@ -366,11 +376,11 @@ void OnLayout() contentBound.Width -= (menuBound.Width + menuMargin + titleHMargin * 2); contentBound.Height -= titleVMargin * 2; - if (_searchView != null) + if (_searchView != null && _searchView.NativeView != null) { _searchView.NativeView.Geometry = contentBound; } - else if (_titleView != null) + else if (_titleView != null && _nativeTitleView != null) { _nativeTitleView.Geometry = contentBound; } diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/ShellSearchResultList.cs b/src/Controls/src/Core/Platform/Tizen/Shell/ShellSearchResultList.cs index 425b64965d9a..d792a6415267 100644 --- a/src/Controls/src/Core/Platform/Tizen/Shell/ShellSearchResultList.cs +++ b/src/Controls/src/Core/Platform/Tizen/Shell/ShellSearchResultList.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; using ElmSharp; -using Tizen.UIExtensions.ElmSharp; using EColor = ElmSharp.Color; +using TThemeConstants = Tizen.UIExtensions.ElmSharp.ThemeConstants; namespace Microsoft.Maui.Controls.Platform { @@ -23,7 +23,7 @@ public ShellSearchResultList(IMauiContext context) : base(context?.Context?.Base SelectionMode = GenItemSelectionMode.Always; BackgroundColor = EColor.White; - _defaultClass = new GenItemClass(ThemeConstants.GenItemClass.Styles.Full) + _defaultClass = new GenItemClass(TThemeConstants.GenItemClass.Styles.Full) { GetContentHandler = GetContent, }; diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/ShellSearchView.cs b/src/Controls/src/Core/Platform/Tizen/Shell/ShellSearchView.cs index 86865cc05a59..c9263a3bce36 100644 --- a/src/Controls/src/Core/Platform/Tizen/Shell/ShellSearchView.cs +++ b/src/Controls/src/Core/Platform/Tizen/Shell/ShellSearchView.cs @@ -14,7 +14,7 @@ public class ShellSearchView : IDisposable bool disposedValue; ShellSearchResultList? _searchResultList; - public ShellSearchView(SearchHandler searchHandler, IMauiContext context) + public ShellSearchView(SearchHandler searchHandler, IMauiContext? context) { Element = searchHandler; MauiContext = context; @@ -55,11 +55,11 @@ public ShellSearchView(SearchHandler searchHandler, IMauiContext context) public EvasObject? NativeView => Control; - protected IMauiContext MauiContext { get; private set; } + protected IMauiContext? MauiContext { get; private set; } protected EvasObject? NativeParent { - get => MauiContext.Context?.BaseLayout; + get => MauiContext?.Context?.BaseLayout; } ISearchHandlerController SearchHandlerController => Element; diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/ShellSectionView.cs b/src/Controls/src/Core/Platform/Tizen/Shell/ShellSectionHandler.cs similarity index 80% rename from src/Controls/src/Core/Platform/Tizen/Shell/ShellSectionView.cs rename to src/Controls/src/Core/Platform/Tizen/Shell/ShellSectionHandler.cs index 306b0809134a..1b848741d840 100644 --- a/src/Controls/src/Core/Platform/Tizen/Shell/ShellSectionView.cs +++ b/src/Controls/src/Core/Platform/Tizen/Shell/ShellSectionHandler.cs @@ -1,4 +1,6 @@ -using System; +#nullable enable + +using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; @@ -11,18 +13,18 @@ namespace Microsoft.Maui.Controls.Platform { - public interface IShellSectionRenderer : IDisposable + public interface IShellSectionHandler : IDisposable { EvasObject NativeView { get; } } - public class ShellSectionView : IAppearanceObserver, IShellSectionRenderer + public class ShellSectionHandler : IAppearanceObserver, IShellSectionHandler { - EBox _mainLayout = null; - EBox _contentArea = null; - Tabs _tabs = null; - EvasObject _currentContent = null; - Page _displayedPage; + EBox _mainLayout; + EBox _contentArea; + Tabs? _tabs = null; + EvasObject? _currentContent = null; + Page? _displayedPage; Dictionary _contentCache = new Dictionary(); Dictionary _contentToTabsItem = new Dictionary(); @@ -34,12 +36,15 @@ public class ShellSectionView : IAppearanceObserver, IShellSectionRenderer bool _disposed = false; - public ShellSectionView(ShellSection section, IMauiContext context) + public ShellSectionHandler(ShellSection section, IMauiContext context) { ShellSection = section; MauiContext = context; ShellSection.PropertyChanged += OnSectionPropertyChanged; - (ShellSection.Items as INotifyCollectionChanged).CollectionChanged += OnShellSectionCollectionChanged; + if (ShellSection.Items is INotifyCollectionChanged collection) + { + collection.CollectionChanged += OnShellSectionCollectionChanged; + } _mainLayout = new EBox(NativeParent); _mainLayout.SetLayoutCallback(OnLayout); @@ -61,7 +66,7 @@ public ShellSectionView(ShellSection section, IMauiContext context) protected IMauiContext MauiContext { get; private set; } - protected EvasObject NativeParent + protected EvasObject? NativeParent { get => MauiContext?.Context?.BaseLayout; } @@ -124,7 +129,7 @@ public EColor ToolbarForegroundColor } } - ~ShellSectionView() + ~ShellSectionHandler() { Dispose(false); } @@ -139,8 +144,9 @@ void IAppearanceObserver.OnAppearanceChanged(ShellAppearance appearance) { var backgroundColor = (appearance as IShellAppearanceElement)?.EffectiveTabBarBackgroundColor; var foregroundColor = appearance?.ForegroundColor; - ToolbarBackgroundColor = backgroundColor.IsDefault() ? ShellView.DefaultBackgroundColor : backgroundColor.ToNativeEFL(); - ToolbarForegroundColor = foregroundColor.IsDefault() ? ShellView.DefaultForegroundColor : foregroundColor.ToNativeEFL(); + + ToolbarBackgroundColor = backgroundColor.IsDefault() ? ShellView.DefaultBackgroundColor : (backgroundColor?.ToNativeEFL()).GetValueOrDefault(); + ToolbarForegroundColor = foregroundColor.IsDefault() ? ShellView.DefaultForegroundColor : (foregroundColor?.ToNativeEFL()).GetValueOrDefault(); } void UpdateDisplayedPage(Page page) @@ -160,7 +166,7 @@ void UpdateDisplayedPage(Page page) TabBarIsVisible = Shell.GetTabBarIsVisible(page); } - void OnDisplayedPagePropertyChanged(object sender, PropertyChangedEventArgs e) + void OnDisplayedPagePropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == Shell.TabBarIsVisibleProperty.PropertyName) { @@ -197,6 +203,8 @@ protected virtual void Dispose(bool disposing) void InitializeTabs() { + _ = NativeParent ?? throw new InvalidOperationException($"{nameof(NativeParent)} should have been set by base class."); + if (_tabs != null) { return; @@ -234,7 +242,7 @@ void DeinitializeTabs() _tabs = null; } - void OnSectionPropertyChanged(object sender, PropertyChangedEventArgs e) + void OnSectionPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == "CurrentItem") { @@ -281,29 +289,35 @@ void UpdateTabsItem() { InsertTabsItem(content); } - _tabs.Scrollable = ShellSection.Items.Count > 3 ? TabsType.Scrollable : TabsType.Fixed; + + if(_tabs !=null) + _tabs.Scrollable = ShellSection.Items.Count > 3 ? TabsType.Scrollable : TabsType.Fixed; } - EToolbarItem InsertTabsItem(ShellContent content) + EToolbarItem? InsertTabsItem(ShellContent content) { - EToolbarItem item = _tabs.Append(content.Title, null); - item.SetBackgroundColor(_backgroundColor); - item.SetUnderlineColor(_foregroundColor); + EToolbarItem? item = _tabs?.Append(content.Title, null); + item?.SetBackgroundColor(_backgroundColor); + item?.SetUnderlineColor(_foregroundColor); + + if (item != null) + { + _tabsItems.Add(item); + _itemToContent[item] = content; + _contentToTabsItem[content] = item; + } - _tabsItems.Add(item); - _itemToContent[item] = content; - _contentToTabsItem[content] = item; return item; } - void OnShellSectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + void OnShellSectionCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { UpdateTabsItem(); } - void OnTabsSelected(object sender, EToolbarItemEventArgs e) + void OnTabsSelected(object? sender, EToolbarItemEventArgs e) { - if (_tabs.SelectedItem == null) + if (_tabs?.SelectedItem == null) { return; } @@ -354,7 +368,7 @@ void OnLayout() var bound = NativeView.Geometry; int tabsHeight; - if (HasTabs && TabBarIsVisible) + if (_tabs != null && TabBarIsVisible) { var tabsBound = bound; tabsHeight = _tabs.MinimumHeight; diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/ShellSectionStack.cs b/src/Controls/src/Core/Platform/Tizen/Shell/ShellSectionStack.cs index ca5a393498c6..4a72c99d14d8 100644 --- a/src/Controls/src/Core/Platform/Tizen/Shell/ShellSectionStack.cs +++ b/src/Controls/src/Core/Platform/Tizen/Shell/ShellSectionStack.cs @@ -1,4 +1,6 @@ -using System; +#nullable enable + +using System; using System.ComponentModel; using System.Threading.Tasks; using ElmSharp; @@ -8,10 +10,10 @@ namespace Microsoft.Maui.Controls.Platform { public class ShellSectionStack : EBox, IAppearanceObserver, IDisposable { - ShellNavBar _navBar = null; - Page _currentPage = null; - SimpleViewStack _viewStack = null; - IShellSectionRenderer _shellSectionView; + ShellNavBar? _navBar = null; + Page? _currentPage = null; + SimpleViewStack _viewStack; + IShellSectionHandler? _shellSectionHandler; bool _disposed = false; bool _navBarIsVisible = true; @@ -20,12 +22,25 @@ public ShellSectionStack(ShellSection section, IMauiContext context) : base(cont { ShellSection = section; MauiContext = context; + + SetAlignment(-1, -1); + SetWeight(1, 1); + SetLayoutCallback(OnLayout); + + _viewStack = new SimpleViewStack(NativeParent); + if (Device.Idiom == TargetIdiom.Phone) + { + _viewStack.BackgroundColor = ElmSharp.Color.White; + } + _viewStack.Show(); + PackEnd(_viewStack); + InitializeComponent(); } - protected IMauiContext MauiContext { get; private set; } + protected IMauiContext? MauiContext { get; private set; } - protected EvasObject NativeParent + protected EvasObject? NativeParent { get => MauiContext?.Context?.BaseLayout; } @@ -85,24 +100,16 @@ protected virtual void Dispose(bool disposing) _disposed = true; } - protected virtual IShellSectionRenderer CreateShellSectionView(ShellSection section) + protected virtual IShellSectionHandler CreateShellSectionView(ShellSection section) { - return new ShellSectionView(section, MauiContext); + _ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); + + return new ShellSectionHandler(section, MauiContext); } void InitializeComponent() { - SetAlignment(-1, -1); - SetWeight(1, 1); - SetLayoutCallback(OnLayout); - - _viewStack = new SimpleViewStack(NativeParent); - if (Device.Idiom == TargetIdiom.Phone) - { - _viewStack.BackgroundColor = ElmSharp.Color.White; - } - _viewStack.Show(); - PackEnd(_viewStack); + _ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); _navBar = new ShellNavBar(MauiContext); _navBar.Show(); @@ -114,13 +121,13 @@ void InitializeComponent() ((IShellController)Shell.Current).AddAppearanceObserver(this, ShellSection); ((IShellController)Shell.Current).AddFlyoutBehaviorObserver(_navBar); - _shellSectionView = CreateShellSectionView(ShellSection); - _shellSectionView.NativeView.Show(); - _viewStack.Push(_shellSectionView.NativeView); + _shellSectionHandler = CreateShellSectionView(ShellSection); + _shellSectionHandler.NativeView.Show(); + _viewStack.Push(_shellSectionHandler.NativeView); Device.BeginInvokeOnMainThread(() => { - (_shellSectionView.NativeView as Widget)?.SetFocus(true); + (_shellSectionHandler.NativeView as Widget)?.SetFocus(true); }); } @@ -137,7 +144,7 @@ void UpdateDisplayedPage(Page page) _currentPage = page; _currentPage.PropertyChanged += OnPagePropertyChanged; NavBarIsVisible = Shell.GetNavBarIsVisible(page); - _navBar.SetPage(page); + _navBar?.SetPage(page); } void IAppearanceObserver.OnAppearanceChanged(ShellAppearance appearance) @@ -149,17 +156,18 @@ void IAppearanceObserver.OnAppearanceChanged(ShellAppearance appearance) var backgroundColor = appearance?.BackgroundColor; var foregroundColor = appearance?.ForegroundColor; - _navBar.TitleColor = titleColor.IsDefault() ? ShellView.DefaultTitleColor : titleColor.ToNativeEFL(); - _navBar.BackgroundColor = backgroundColor.IsDefault() ? ShellView.DefaultBackgroundColor : backgroundColor.ToNativeEFL(); - _navBar.ForegroundColor = foregroundColor.IsDefault() ? ShellView.DefaultForegroundColor : foregroundColor.ToNativeEFL(); + _navBar.TitleColor = titleColor.IsDefault() ? ShellView.DefaultTitleColor : (titleColor?.ToNativeEFL()).GetValueOrDefault(); + _navBar.BackgroundColor = backgroundColor.IsDefault() ? ShellView.DefaultBackgroundColor : (backgroundColor?.ToNativeEFL()).GetValueOrDefault(); + _navBar.ForegroundColor = foregroundColor.IsDefault() ? ShellView.DefaultForegroundColor : (foregroundColor?.ToNativeEFL()).GetValueOrDefault(); } - protected virtual void OnPagePropertyChanged(object sender, PropertyChangedEventArgs e) + protected virtual void OnPagePropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == Page.TitleProperty.PropertyName) { - _navBar.Title = (sender as Page)?.Title; + if (_navBar != null) + _navBar.Title = (sender as Page)?.Title ?? ""; } else if (e.PropertyName == Shell.NavBarIsVisibleProperty.PropertyName) { @@ -167,11 +175,12 @@ protected virtual void OnPagePropertyChanged(object sender, PropertyChangedEvent } else if (e.PropertyName == Shell.TitleViewProperty.PropertyName) { - _navBar.TitleView = Shell.GetTitleView(sender as Page); + if (_navBar != null) + _navBar.TitleView = Shell.GetTitleView(sender as Page); } } - void OnNavigationRequested(object sender, Internals.NavigationRequestedEventArgs e) + void OnNavigationRequested(object? sender, Internals.NavigationRequestedEventArgs e) { if (e.RequestType == Internals.NavigationRequestType.Push) { @@ -196,8 +205,10 @@ void OnNavigationRequested(object sender, Internals.NavigationRequestedEventArgs UpdateHasBackButton(); } - void RemoveRequest(object sender, Internals.NavigationRequestedEventArgs request) + void RemoveRequest(object? sender, Internals.NavigationRequestedEventArgs request) { + _ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); + var nativePage = request.Page.ToNative(MauiContext); if (nativePage == null) { @@ -208,20 +219,22 @@ void RemoveRequest(object sender, Internals.NavigationRequestedEventArgs request request.Task = Task.FromResult(true); } - void PopRequest(object sender, Internals.NavigationRequestedEventArgs request) + void PopRequest(object? sender, Internals.NavigationRequestedEventArgs request) { _viewStack.Pop(); request.Task = Task.FromResult(true); } - void PopToRootRequest(object sender, Internals.NavigationRequestedEventArgs request) + void PopToRootRequest(object? sender, Internals.NavigationRequestedEventArgs request) { _viewStack.PopToRoot(); request.Task = Task.FromResult(true); } - void PushRequest(object sender, Internals.NavigationRequestedEventArgs request) + void PushRequest(object? sender, Internals.NavigationRequestedEventArgs request) { + _ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); + var nativePage = request.Page.ToNative(MauiContext); _viewStack.Push(nativePage); request.Task = Task.FromResult(true); @@ -231,8 +244,10 @@ void PushRequest(object sender, Internals.NavigationRequestedEventArgs request) }); } - void InsertRequest(object sender, Internals.NavigationRequestedEventArgs request) + void InsertRequest(object? sender, Internals.NavigationRequestedEventArgs request) { + _ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); + var before = request.BeforePage.ToNative(MauiContext); if (before == null) { @@ -246,6 +261,9 @@ void InsertRequest(object sender, Internals.NavigationRequestedEventArgs request void UpdateHasBackButton() { + if (_navBar == null) + return; + if (_viewStack.Stack.Count > 1) _navBar.HasBackButton = true; else @@ -262,17 +280,20 @@ void OnLayout() if (NavBarIsVisible) { var navBound = bound; - navBarHeight = DPExtensions.ConvertToScaledPixel(_navBar.GetDefaultHeight()); + navBarHeight = DPExtensions.ConvertToScaledPixel(_navBar.GetDefaultNavBarHeight()); navBound.Height = navBarHeight; - _navBar.Show(); - _navBar.Geometry = navBound; - _navBar.RaiseTop(); + if (_navBar != null) + { + _navBar.Show(); + _navBar.Geometry = navBound; + _navBar.RaiseTop(); + } } else { navBarHeight = 0; - _navBar.Hide(); + _navBar?.Hide(); } bound.Y += navBarHeight; diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/ShellView.cs b/src/Controls/src/Core/Platform/Tizen/Shell/ShellView.cs index cc0d6afeb99b..bcd1eb39e8ef 100644 --- a/src/Controls/src/Core/Platform/Tizen/Shell/ShellView.cs +++ b/src/Controls/src/Core/Platform/Tizen/Shell/ShellView.cs @@ -1,63 +1,73 @@ -using System; +#nullable enable + +using System; +using System.Collections; using System.Collections.Generic; using System.ComponentModel; using ElmSharp; +using Microsoft.Extensions.DependencyInjection; using Tizen.UIExtensions.Common; using Tizen.UIExtensions.ElmSharp; +using EBox = ElmSharp.Box; using EColor = ElmSharp.Color; -using TINavigationView = Tizen.UIExtensions.ElmSharp.INavigationView; -using TNavigationView = Tizen.UIExtensions.ElmSharp.NavigationView; -using TThemeConstants = Tizen.UIExtensions.ElmSharp.ThemeConstants; +using ITNavigationView = Tizen.UIExtensions.ElmSharp.INavigationView; using TCollectionView = Tizen.UIExtensions.ElmSharp.CollectionView; +using TImage = Tizen.UIExtensions.ElmSharp.Image; +using TNavigationView = Tizen.UIExtensions.ElmSharp.NavigationView; using TSelectedItemChangedEventArgs = Tizen.UIExtensions.ElmSharp.SelectedItemChangedEventArgs; +using TThemeConstants = Tizen.UIExtensions.ElmSharp.ThemeConstants; namespace Microsoft.Maui.Controls.Platform { - public class ShellView : NavigationDrawer, IFlyoutBehaviorObserver + public class ShellView : EBox, IFlyoutBehaviorObserver { - TINavigationView _navigationView; - FlyoutHeaderBehavior _headerBehavior; - - List> _cachedGroups; - - View _headerView; - View _footerView; - TCollectionView _itemsView; - - Element _lastSelected; - ShellItemView _currentShellItem; - public static readonly EColor DefaultBackgroundColor = TThemeConstants.Shell.ColorClass.DefaultBackgroundColor; public static readonly EColor DefaultForegroundColor = TThemeConstants.Shell.ColorClass.DefaultForegroundColor; public static readonly EColor DefaultTitleColor = TThemeConstants.Shell.ColorClass.DefaultTitleColor; - // The source of icon resources is https://materialdesignicons.com/ - public const string MenuIcon = ""; - - protected EvasObject NativeParent { get; private set; } - - protected Shell Element { get; private set; } + INavigationDrawer _navigationDrawer; + ITNavigationView _navigationView; + FlyoutHeaderBehavior _headerBehavior; - public IMauiContext MauiContext { get; private set; } + List>? _cachedGroups; - public NavigationDrawer NativeView => this; + View? _headerView; + View? _footerView; + TCollectionView _itemsView; - bool HeaderOnMenu => _headerBehavior == FlyoutHeaderBehavior.Scroll || - _headerBehavior == FlyoutHeaderBehavior.CollapseOnScroll; + Element? _lastSelected; + ShellItemView? _currentShellItem; public ShellView(EvasObject parent) : base(parent) { NativeParent = parent; + _navigationDrawer = CreateNavigationDrawer(); _navigationView = CreateNavigationView(); _navigationView.LayoutUpdated += OnNavigationViewLayoutUpdated; + _navigationView.Content = _itemsView = CreateItemsView(); - NavigationView = _navigationView.TargetView; - Toggled += OnDrawerToggled; + _navigationDrawer.NavigationView = _navigationView.TargetView; + _navigationDrawer.Toggled += OnDrawerToggled; - _navigationView.Content = CreateItemsView(); + _navigationDrawer.TargetView.SetAlignment(-1.0, -1.0); + _navigationDrawer.TargetView.SetWeight(1.0, 1.0); + _navigationDrawer.TargetView.Show(); + PackEnd(_navigationDrawer.TargetView); } - internal void SetElement(Shell shell, IMauiContext context) + public IMauiContext? MauiContext { get; private set; } + + protected EvasObject? NativeParent { get; private set; } + + protected Shell? Element { get; private set; } + + protected TCollectionView ItemsView => _itemsView; + + protected ITNavigationView NavigationView => _navigationView; + + protected bool HeaderOnMenu => _headerBehavior == FlyoutHeaderBehavior.Scroll || _headerBehavior == FlyoutHeaderBehavior.CollapseOnScroll; + + public virtual void SetElement(Shell shell, IMauiContext context) { Element = shell; Element.PropertyChanged += OnElementPropertyChanged; @@ -67,6 +77,8 @@ internal void SetElement(Shell shell, IMauiContext context) _lastSelected = null; UpdateFlyoutIsPresented(); + UpdateFlyoutBackgroundColor(); + UpdateFlyoutBackgroundImage(); UpdateCurrentItem(); UpdateFlyoutHeader(); UpdateFooter(); @@ -74,17 +86,30 @@ internal void SetElement(Shell shell, IMauiContext context) protected virtual ShellItemView CreateShellItemView(ShellItem item) { + _ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); + return new ShellItemView(item, MauiContext); } - protected virtual TINavigationView CreateNavigationView() + protected virtual INavigationDrawer CreateNavigationDrawer() + { + _ = NativeParent ?? throw new InvalidOperationException($"{nameof(NativeParent)} should have been set by base class."); + + return new NavigationDrawer(NativeParent); + } + + protected virtual ITNavigationView CreateNavigationView() { + _ = NativeParent ?? throw new InvalidOperationException($"{nameof(NativeParent)} should have been set by base class."); + return new TNavigationView(NativeParent); } - protected virtual EvasObject CreateItemsView() + protected virtual TCollectionView CreateItemsView() { - _itemsView = new TCollectionView(NativeParent) + _ = NativeParent ?? throw new InvalidOperationException($"{nameof(NativeParent)} should have been set by base class."); + + return new TCollectionView(NativeParent) { AlignmentX = -1, AlignmentY = -1, @@ -95,11 +120,17 @@ protected virtual EvasObject CreateItemsView() VerticalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible, LayoutManager = new LinearLayoutManager(false, Tizen.UIExtensions.ElmSharp.ItemSizingStrategy.MeasureFirstItem) }; + } + + protected virtual ItemAdaptor GetItemAdaptor(IEnumerable items) + { + _ = Element ?? throw new InvalidOperationException($"{nameof(Element)} should have been set by base class."); + _ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); - return _itemsView; + return new ShellFlyoutItemAdaptor(Element, MauiContext, items, HeaderOnMenu); } - protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + protected virtual void OnElementPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == Shell.CurrentItemProperty.PropertyName) { @@ -113,6 +144,14 @@ protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEv { UpdateFlyoutBackgroundColor(); } + else if (e.PropertyName == Shell.FlyoutBackgroundImageProperty.PropertyName) + { + UpdateFlyoutBackgroundImage(); + } + else if (e.PropertyName == Shell.FlyoutBackgroundImageProperty.PropertyName) + { + UpdateFlyoutBackgroundImageAspect(); + } else if (e.PropertyName == Shell.FlyoutHeaderProperty.PropertyName) { UpdateFlyoutHeader(); @@ -133,32 +172,33 @@ protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEv protected virtual void UpdateFlyoutIsPresented() { + _ = Element ?? throw new InvalidOperationException($"{nameof(Element)} should have been set by base class."); + // It is workaround of Panel.IsOpen bug, Panel.IsOpen property is not working when layouting was triggered Device.BeginInvokeOnMainThread(() => { - IsOpen = Element.FlyoutIsPresented; + _navigationDrawer.IsOpen = Element.FlyoutIsPresented; }); } - protected void OnDrawerToggled(object sender, EventArgs e) + protected void OnDrawerToggled(object? sender, EventArgs e) { - Element.SetValueFromRenderer(Shell.FlyoutIsPresentedProperty, IsOpen); + _ = Element ?? throw new InvalidOperationException($"{nameof(Element)} should have been set by base class."); + + Element.SetValueFromRenderer(Shell.FlyoutIsPresentedProperty, _navigationDrawer.IsOpen); } protected virtual void UpdateFlyoutBehavior() { - if (Element.FlyoutBehavior == FlyoutBehavior.Locked) - { - IsSplit = true; - } - else - { - IsSplit = false; - } + _ = Element ?? throw new InvalidOperationException($"{nameof(Element)} should have been set by base class."); + + _navigationDrawer.IsSplit = (Element.FlyoutBehavior == FlyoutBehavior.Locked) ? true : false; } protected virtual void BuildMenu() { + _ = Element ?? throw new InvalidOperationException($"{nameof(Element)} should have been set by base class."); + var groups = ((IShellController)Element).GenerateFlyoutGrouping(); if (!IsItemChanged(groups) && !HeaderOnMenu) @@ -181,12 +221,15 @@ protected virtual void BuildMenu() } } - _itemsView.Adaptor = new ShellFlyoutItemAdaptor(Element, MauiContext, items, HeaderOnMenu); - _itemsView.Adaptor.ItemSelected += OnItemSelected; + ItemsView.Adaptor = GetItemAdaptor(items); + ItemsView.Adaptor.ItemSelected += OnItemSelected; } protected virtual void UpdateFlyoutHeader() { + _ = Element ?? throw new InvalidOperationException($"{nameof(Element)} should have been set by base class."); + _ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); + if (_headerView != null) { _headerView.MeasureInvalidated -= OnHeaderSizeChanged; @@ -206,7 +249,7 @@ protected virtual void UpdateFlyoutHeader() } else { - _navigationView.Header = _headerView?.ToNative(MauiContext); + _navigationView.Header = _headerView.ToNative(MauiContext); _headerView.MeasureInvalidated += OnHeaderSizeChanged; } } @@ -218,6 +261,9 @@ protected virtual void UpdateFlyoutHeader() protected virtual void UpdateFooter() { + _ = Element ?? throw new InvalidOperationException($"{nameof(Element)} should have been set by base class."); + _ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); + if (_footerView != null) { _footerView.MeasureInvalidated -= OnFooterSizeChanged; @@ -228,7 +274,7 @@ protected virtual void UpdateFooter() if (_footerView != null) { - _navigationView.Footer = _footerView?.ToNative(MauiContext); + _navigationView.Footer = _footerView.ToNative(MauiContext); _footerView.MeasureInvalidated += OnFooterSizeChanged; } else @@ -237,13 +283,15 @@ protected virtual void UpdateFooter() } } - void OnShellStructureChanged(object sender, EventArgs e) + void OnShellStructureChanged(object? sender, EventArgs e) { BuildMenu(); } - void OnItemSelected(object sender, TSelectedItemChangedEventArgs e) + void OnItemSelected(object? sender, TSelectedItemChangedEventArgs e) { + _ = Element ?? throw new InvalidOperationException($"{nameof(Element)} should have been set by base class."); + _lastSelected = e.SelectedItem as Element; ((IShellController)Element).OnFlyoutItemSelected(_lastSelected); } @@ -274,42 +322,79 @@ bool IsItemChanged(List> groups) void UpdateCurrentItem() { + _ = Element ?? throw new InvalidOperationException($"{nameof(Element)} should have been set by base class."); + _currentShellItem?.Dispose(); if (Element.CurrentItem != null) { _currentShellItem = CreateShellItemView(Element.CurrentItem); - Main = _currentShellItem.NativeView; + _navigationDrawer.Main = _currentShellItem.NativeView; } else { - Main = null; + _navigationDrawer.Main = null; } } void UpdateFlyoutBackgroundColor() { + _ = Element ?? throw new InvalidOperationException($"{nameof(Element)} should have been set by base class."); + _navigationView.BackgroundColor = Element.FlyoutBackgroundColor.ToNativeEFL(); } - void OnNavigationViewLayoutUpdated(object sender, LayoutEventArgs args) + async void UpdateFlyoutBackgroundImage() + { + _ = Element ?? throw new InvalidOperationException($"{nameof(Element)} should have been set by base class."); + _ = NativeParent ?? throw new InvalidOperationException($"{nameof(NativeParent)} should have been set by base class."); + _ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); + + if (Element.FlyoutBackgroundImage != null) + { + var image = new TImage(NativeParent); + var imageSource = Element.FlyoutBackgroundImage; + var provider = MauiContext.Services.GetRequiredService(); + var service = provider.GetRequiredImageSourceService(imageSource); + image.Aspect = Element.FlyoutBackgroundImageAspect.ToNative(); + _navigationView.BackgroundImage = image; + + await service.GetImageAsync(imageSource, image); + } + else + { + _navigationView.BackgroundImage = null; + } + } + + void UpdateFlyoutBackgroundImageAspect() + { + _ = Element ?? throw new InvalidOperationException($"{nameof(Element)} should have been set by base class."); + + if (_navigationView.BackgroundImage is TImage image) + { + image.Aspect = Element.FlyoutBackgroundImageAspect.ToNative(); + } + } + + void OnNavigationViewLayoutUpdated(object? sender, LayoutEventArgs args) { UpdateHeaderLayout(args.Geometry.Width, args.Geometry.Height); UpdateFooterLayout(args.Geometry.Width, args.Geometry.Height); } - void OnHeaderSizeChanged(object sender, EventArgs e) + void OnHeaderSizeChanged(object? sender, EventArgs e) { - var bound = (_navigationView as EvasObject).Geometry; + var bound = (_navigationView as EvasObject)?.Geometry; Device.BeginInvokeOnMainThread(()=> { - UpdateHeaderLayout(bound.Width, bound.Height); + UpdateHeaderLayout((bound?.Width).GetValueOrDefault(), (bound?.Height).GetValueOrDefault()); }); } - void OnFooterSizeChanged(object sender, EventArgs e) + void OnFooterSizeChanged(object? sender, EventArgs e) { - var bound = (_navigationView as EvasObject).Geometry; + var bound = (_navigationView as EvasObject)?.Geometry; Device.BeginInvokeOnMainThread(() => { - UpdateFooterLayout(bound.Width, bound.Height); + UpdateFooterLayout((bound?.Width).GetValueOrDefault(), (bound?.Height).GetValueOrDefault()); }); } @@ -318,7 +403,8 @@ void UpdateHeaderLayout(double widthConstraint, double heightConstraint) if ((!HeaderOnMenu) && (_headerView != null)) { var requestSize = _headerView.Measure(widthConstraint, heightConstraint); - _navigationView.Header.MinimumHeight = DPExtensions.ConvertToScaledPixel(requestSize.Request.Height); + if(_navigationView.Header != null) + _navigationView.Header.MinimumHeight = DPExtensions.ConvertToScaledPixel(requestSize.Request.Height); } } @@ -327,7 +413,8 @@ void UpdateFooterLayout(double widthConstraint, double heightConstraint) if (_footerView != null) { var requestSize = _footerView.Measure(widthConstraint, heightConstraint); - _navigationView.Footer.MinimumHeight = DPExtensions.ConvertToScaledPixel(requestSize.Request.Height); + if (_navigationView.Footer != null) + _navigationView.Footer.MinimumHeight = DPExtensions.ConvertToScaledPixel(requestSize.Request.Height); } } diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/TVNavigationDrawer.cs b/src/Controls/src/Core/Platform/Tizen/Shell/TVNavigationDrawer.cs new file mode 100644 index 000000000000..2c15c235f8d8 --- /dev/null +++ b/src/Controls/src/Core/Platform/Tizen/Shell/TVNavigationDrawer.cs @@ -0,0 +1,256 @@ +# nullable enable + +using System; +using ElmSharp; +using Tizen.UIExtensions.ElmSharp; +using EBox = ElmSharp.Box; +using EColor = ElmSharp.Color; +using TButton = Tizen.UIExtensions.ElmSharp.Button; +using ITNavigationView = Tizen.UIExtensions.ElmSharp.INavigationView; + +namespace Microsoft.Maui.Controls.Platform +{ + public class TVNavigationDrawer : EBox, INavigationDrawer, IFlyoutBehaviorObserver + { + EBox _drawerBox; + EBox _mainBox; + EvasObject? _main; + EvasObject? _drawer; + TButton _focusControlArea; + + FlyoutBehavior _behavior; + bool _isOpen; + + double _openRatio; + + public TVNavigationDrawer(EvasObject parent) : base(parent) + { + SetLayoutCallback(OnLayout); + + _drawerBox = new EBox(parent); + _drawerBox.Show(); + PackEnd(_drawerBox); + + _mainBox = new EBox(parent); + _mainBox.SetLayoutCallback(OnMainBoxLayout); + _mainBox.Show(); + PackEnd(_mainBox); + + _focusControlArea = new TButton(parent) + { + Color = EColor.Transparent, + BackgroundColor = EColor.Transparent + }; + _focusControlArea.SetEffectColor(EColor.Transparent); + _focusControlArea.Show(); + _mainBox.PackEnd(_focusControlArea); + + _behavior = FlyoutBehavior.Flyout; + + _drawerBox.KeyUp += (s, e) => + { + if (e.KeyName == "Return" || e.KeyName == "Right") + { + IsOpen = false; + } + }; + + _mainBox.KeyUp += (s, e) => + { + if (e.KeyName == "Left") + { + if (_focusControlArea.IsFocused) + IsOpen = true; + } + else + { + // Workaround to prevent unexpected movement of the focus to drawer during page pushing. + if (_behavior == FlyoutBehavior.Locked) + _drawerBox.AllowTreeFocus = true; + } + }; + + _mainBox.KeyDown += (s, e) => + { + if (e.KeyName != "Left") + { + // Workaround to prevent unexpected movement of the focus to drawer during page pushing. + if (_behavior == FlyoutBehavior.Locked) + _drawerBox.AllowTreeFocus = false; + } + }; + + UpdateFocusPolicy(); + } + + public event EventHandler? Toggled; + + public EvasObject TargetView => this; + + public EvasObject? NavigationView + { + get => _drawer; + set => UpdateNavigationView(value); + } + + public EvasObject? Main + { + get => _main; + set => UpdateMain(value); + } + + public bool IsOpen + { + get => _isOpen; + set => UpdateOpenState(value); + } + + public bool IsSplit { get; set; } + + public void UpdateBehavior(FlyoutBehavior behavior) + { + _behavior = behavior; + _focusControlArea.IsEnabled = _behavior == FlyoutBehavior.Flyout; + + var open = false; + + if (_behavior == FlyoutBehavior.Locked) + open = true; + else if (_behavior == FlyoutBehavior.Disabled) + open = false; + else + open = _drawerBox.IsFocused; + + UpdateOpenState(open); + } + + void UpdateNavigationView(EvasObject? navigationView) + { + if (_drawer != null) + { + _drawerBox.UnPack(_drawer); + _drawer.Hide(); + } + + _drawer = navigationView; + + if (_drawer != null) + { + _drawer.SetAlignment(-1, -1); + _drawer.SetWeight(1, 1); + _drawer.Show(); + _drawerBox.PackEnd(_drawer); + } + } + + void UpdateMain(EvasObject? main) + { + if (_main != null) + { + _mainBox.UnPack(_main); + _main.Hide(); + } + _main = main; + + if (_main != null) + { + _main.SetAlignment(-1, -1); + _main.SetWeight(1, 1); + _main.Show(); + _mainBox.PackStart(_main); + } + } + + void OnMainBoxLayout() + { + if (_main != null) + { + _main.Geometry = _mainBox.Geometry; + } + + var focusedButtonGeometry = _mainBox.Geometry; + focusedButtonGeometry.X = focusedButtonGeometry.X - 100; + focusedButtonGeometry.Width = 0; + focusedButtonGeometry.Height = (int)((_drawer as ITNavigationView)?.GetTvFlyoutItemHeight()).GetValueOrDefault(); + _focusControlArea.Geometry = focusedButtonGeometry; + } + + void OnLayout() + { + if (Geometry.Width == 0 || Geometry.Height == 0) + return; + + var bound = Geometry; + + var ratioMax = this.GetTvFlyoutRatio(Geometry.Width, Geometry.Height); + var ratioMin = (_behavior == FlyoutBehavior.Disabled) ? 0 : this.GetTvFlyoutRatioMin(); + var drawerWidthMax = (int)(bound.Width * ratioMax); + var drawerWidthMin = (int)(bound.Width * ratioMin); + + var drawerWidthOutBound = (int)((drawerWidthMax - drawerWidthMin) * (1 - _openRatio)); + var drawerWidthInBound = drawerWidthMax - drawerWidthOutBound; + + var drawerGeometry = bound; + drawerGeometry.Width = drawerWidthInBound; + _drawerBox.Geometry = drawerGeometry; + + var containerGeometry = bound; + containerGeometry.X = drawerWidthInBound; + containerGeometry.Width = (_behavior == FlyoutBehavior.Locked) ? (bound.Width - drawerWidthInBound) : (bound.Width - drawerWidthMin); + _mainBox.Geometry = containerGeometry; + } + + void UpdateOpenState(bool isOpen) + { + if (_behavior == FlyoutBehavior.Locked && !isOpen) + return; + + double endState = ((_behavior != FlyoutBehavior.Disabled) && isOpen) ? 1 : 0; + new Animation((r) => + { + _openRatio = r; + OnLayout(); + }, _openRatio, endState, Easing.SinOut).Commit(Shell.Current, "DrawerMove", finished: (f, aborted) => + { + if (!aborted) + { + if (_isOpen != isOpen) + { + _isOpen = isOpen; + UpdateFocusPolicy(); + Toggled?.Invoke(this, EventArgs.Empty); + } + } + }); + } + + void UpdateFocusPolicy() + { + if (_isOpen) + { + if (_behavior == FlyoutBehavior.Locked) + { + _drawerBox.AllowTreeFocus = true; + _mainBox.AllowTreeFocus = true; + } + else + { + _mainBox.AllowTreeFocus = false; + _drawerBox.AllowTreeFocus = true; + _drawerBox.SetFocus(true); + } + } + else + { + _mainBox.AllowTreeFocus = true; + _drawerBox.AllowTreeFocus = false; + _mainBox.SetFocus(true); + } + } + + void IFlyoutBehaviorObserver.OnFlyoutBehaviorChanged(FlyoutBehavior behavior) + { + UpdateBehavior(behavior); + } + } +} diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/TVNavigationView.cs b/src/Controls/src/Core/Platform/Tizen/Shell/TVNavigationView.cs new file mode 100644 index 000000000000..e6b847ffa0d6 --- /dev/null +++ b/src/Controls/src/Core/Platform/Tizen/Shell/TVNavigationView.cs @@ -0,0 +1,27 @@ +using ElmSharp; +using EColor = ElmSharp.Color; +using TNavigationView = Tizen.UIExtensions.ElmSharp.NavigationView; + +namespace Microsoft.Maui.Controls.Platform +{ + public class TVNavigationView : TNavigationView + { + EColor _backgroundColor; + + public TVNavigationView(EvasObject parent) : base(parent) + { + BackgroundColor = this.GetTvDefaultBackgroundColor().ToNativeEFL(); + } + + public override EColor BackgroundColor + { + get => _backgroundColor; + set + { + _backgroundColor = value; + base.BackgroundColor = _backgroundColor.IsDefault ? this.GetTvDefaultBackgroundColor().ToNativeEFL() : _backgroundColor; + } + } + + } +} diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/TVShellItemAdaptor.cs b/src/Controls/src/Core/Platform/Tizen/Shell/TVShellItemAdaptor.cs new file mode 100644 index 000000000000..9bfda7461515 --- /dev/null +++ b/src/Controls/src/Core/Platform/Tizen/Shell/TVShellItemAdaptor.cs @@ -0,0 +1,357 @@ +#nullable enable + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using ElmSharp; +using Microsoft.Maui.Controls.Internals; +using Tizen.UIExtensions.ElmSharp; +using ITNavigtaionView = Tizen.UIExtensions.ElmSharp.INavigationView; +using TViewHolderState = Tizen.UIExtensions.ElmSharp.ViewHolderState; + +namespace Microsoft.Maui.Controls.Platform +{ + public class TVShellItemAdaptor : ItemAdaptor + { + Dictionary _nativeFormsTable = new Dictionary(); + Dictionary _dataBindedViewTable = new Dictionary(); + + Element _element; + IMauiContext _context; + ITNavigtaionView? _navigationView; + + protected virtual bool IsSelectable { get; } + + public DataTemplate DefaultTemplate { get; private set; } + + public TVShellItemAdaptor(Element element, ITNavigtaionView? nv, IMauiContext context, IEnumerable items, bool isCollapsed) : base(items) + { + _element = element; + _context = context; + _navigationView = nv; + IsSelectable = true; + DefaultTemplate = CreateDafaultTemplate(nv, isCollapsed); + } + + public override EvasObject? CreateNativeView(EvasObject parent) + { + return CreateNativeView(0, parent); + } + + public override EvasObject? CreateNativeView(int index, EvasObject parent) + { + View? view = GetTemplatedView(index); + if (view != null) + { + var native = view.ToNative(_context); + _nativeFormsTable[native] = view; + return native; + } + + return null; + } + + public override EvasObject? GetFooterView(EvasObject parent) + { + return null; + } + + public override EvasObject? GetHeaderView(EvasObject parent) + { + return null; + } + + public override Size MeasureFooter(int widthConstraint, int heightConstraint) + { + return new Size(0, 0); + } + + public override Size MeasureHeader(int widthConstraint, int heightConstraint) + { + return new Size(0, 0); + } + + public override Size MeasureItem(int widthConstraint, int heightConstraint) + { + return MeasureItem(0, widthConstraint, heightConstraint); + } + + public override Size MeasureItem(int index, int widthConstraint, int heightConstraint) + { + View? view = GetTemplatedView(index); + if (view != null) + { + var native = view.ToNative(_context); + view.Parent = _element; + + if (Count > index) + view.BindingContext = this[index]; + + var size = view.Measure(DPExtensions.ConvertToScaledDP(widthConstraint), DPExtensions.ConvertToScaledDP(heightConstraint), MeasureFlags.IncludeMargins).Request; + native.Unrealize(); + + return size.ToEFLPixel(); + } + + return new Size(0, 0); + } + + public override void RemoveNativeView(EvasObject native) + { + native?.Unrealize(); + } + + public override void SetBinding(EvasObject native, int index) + { + if (_nativeFormsTable.TryGetValue(native, out View? view)) + { + ResetBindedView(view); + var item = this[index]; + view.BindingContext = item; + if (item != null) + _dataBindedViewTable[item] = view; + + view.MeasureInvalidated += OnItemMeasureInvalidated; + view.Parent = _element; + } + } + + public override void UnBinding(EvasObject native) + { + if (_nativeFormsTable.TryGetValue(native, out View? view)) + { + view.MeasureInvalidated -= OnItemMeasureInvalidated; + ResetBindedView(view); + } + } + + public override void UpdateViewState(EvasObject native, TViewHolderState state) + { + base.UpdateViewState(native, state); + if (_nativeFormsTable.TryGetValue(native, out View? view)) + { + switch (state) + { + case TViewHolderState.Focused: + VisualStateManager.GoToState(view, VisualStateManager.CommonStates.Focused); + view.SetValue(VisualElement.IsFocusedPropertyKey, true); + break; + case TViewHolderState.Normal: + VisualStateManager.GoToState(view, VisualStateManager.CommonStates.Normal); + view.SetValue(VisualElement.IsFocusedPropertyKey, false); + break; + case TViewHolderState.Selected: + if (IsSelectable) + VisualStateManager.GoToState(view, VisualStateManager.CommonStates.Selected); + break; + } + } + } + + DataTemplate CreateDafaultTemplate(ITNavigtaionView? nv, bool isCollapsed) + { + return new DataTemplate(() => + { + var grid = new GridLayout + { + HeightRequest = nv.GetTvFlyoutItemHeight(), + WidthRequest = nv.GetTvFlyoutItemWidth(), + BackgroundColor = Graphics.Colors.Transparent + }; + + ColumnDefinitionCollection columnDefinitions = new ColumnDefinitionCollection(); + columnDefinitions.Add(new ColumnDefinition { Width = nv.GetTvFlyoutIconColumnSize() }); + columnDefinitions.Add(new ColumnDefinition { Width = GridLength.Star }); + grid.ColumnDefinitions = columnDefinitions; + + var image = new Image + { + VerticalOptions = LayoutOptions.Center, + HorizontalOptions = LayoutOptions.Center, + HeightRequest = nv.GetTvFlyoutIconSize(), + WidthRequest = nv.GetTvFlyoutIconSize(), + Margin = new Thickness(nv.GetTvFlyoutMargin(), 0, 0, 0), + }; + image.SetBinding(Image.SourceProperty, new Binding("FlyoutIcon")); + grid.Add(image); + grid.SetColumn(image, 0); + + var label = new Label + { + FontSize = nv.GetTvFlyoutItemFontSize(), + VerticalTextAlignment = TextAlignment.Center, + Margin = new Thickness(nv.GetTvFlyoutMargin(), 0, 0, 0), + }; + + label.SetBinding(Label.TextProperty, new Binding("Title")); + label.SetBinding(Label.TextColorProperty, new Binding("BackgroundColor", converter: new TextColorConverter(), source: grid)); + + if (isCollapsed) + { + label.Opacity = 0; + label.SetBinding(Label.OpacityProperty, new Binding("Width", converter: new OpacityConverter(label.Opacity), source: label)); + } + + grid.Add(label); + grid.SetColumn(label, 1); + + var groups = new VisualStateGroupList(); + + var commonGroup = new VisualStateGroup(); + commonGroup.Name = "CommonStates"; + groups.Add(commonGroup); + + var normalState = new VisualState(); + normalState.Name = "Normal"; + normalState.Setters.Add(new Setter + { + Property = VisualElement.BackgroundColorProperty, + Value = nv.GetTvFlyoutItemColor(), + }); + + var focusedState = new VisualState(); + focusedState.Name = "Focused"; + focusedState.Setters.Add(new Setter + { + Property = VisualElement.BackgroundColorProperty, + Value = nv.GetTvFlyoutItemFocusedColor() + }); + + var selectedState = new VisualState(); + selectedState.Name = "Selected"; + selectedState.Setters.Add(new Setter + { + Property = VisualElement.BackgroundColorProperty, + Value = nv.GetTvFlyoutItemColor() + }); + + commonGroup.States.Add(normalState); + commonGroup.States.Add(focusedState); + commonGroup.States.Add(selectedState); + + VisualStateManager.SetVisualStateGroups(grid, groups); + return grid; + }); + } + + View? GetTemplatedView(int index) + { + var dataTemplate = DefaultTemplate; + var item = this[index]; + if (item is BindableObject bo) + { + dataTemplate = (_element as IShellController)?.GetFlyoutItemDataTemplate(bo); + if (dataTemplate != null) + { + if (item is IMenuItemController) + { + if (_element is Shell shell && shell.MenuItemTemplate != dataTemplate) + dataTemplate = DefaultTemplate; + } + else + { + if (_element is Shell shell && shell.ItemTemplate != dataTemplate) + dataTemplate = DefaultTemplate; + } + } + else + { + dataTemplate = DefaultTemplate; + } + } + + var view = dataTemplate.SelectDataTemplate(this[index], _element).CreateContent() as View; + return view; + } + + void ResetBindedView(View view) + { + if (view.BindingContext != null && _dataBindedViewTable.ContainsKey(view.BindingContext)) + { + _dataBindedViewTable[view.BindingContext] = null; + view.Parent = null; + view.BindingContext = null; + } + } + + void OnItemMeasureInvalidated(object? sender, EventArgs e) + { + var data = (sender as View)?.BindingContext ?? null; + if (data != null) + { + int index = GetItemIndex(data); + if (index != -1) + { + CollectionView?.ItemMeasureInvalidated(index); + } + } + } + + class TextColorConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is Graphics.Color c && c == Graphics.Colors.Transparent) + { + return Graphics.Colors.White; + } + else + { + return Graphics.Colors.Black; + } + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is Graphics.Color c && c == Graphics.Colors.White) + { + return Graphics.Colors.Transparent; + } + else + { + return Graphics.Colors.Black; + } + } + } + + class OpacityConverter : IValueConverter + { + double _opacity; + double _itemWidth = -1; + + public OpacityConverter(double opacity) + { + _opacity = opacity; + } + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var width = (double)value; + + if (_itemWidth == -1) + { + _itemWidth = width; + return _opacity; + } + + _itemWidth = (_itemWidth < width) ? width : _itemWidth; + return ((width / _itemWidth) > 1) ? 1 : (width / _itemWidth); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + var width = (double)value; + + if (_itemWidth == -1) + { + _itemWidth = width; + return _opacity; + } + + _itemWidth = (_itemWidth < width) ? width : _itemWidth; + return width * _itemWidth; + } + } + } +} diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/TVShellItemView.cs b/src/Controls/src/Core/Platform/Tizen/Shell/TVShellItemView.cs new file mode 100644 index 000000000000..4662d3232ee8 --- /dev/null +++ b/src/Controls/src/Core/Platform/Tizen/Shell/TVShellItemView.cs @@ -0,0 +1,18 @@ +namespace Microsoft.Maui.Controls.Platform +{ + public class TVShellItemView : ShellItemView + { + public TVShellItemView(ShellItem item, IMauiContext context) : base(item, context) + { + } + + protected override ShellSectionStack CreateShellSectionStack(ShellSection section) + { + return new TVShellSectionStack(section, MauiContext); + } + + protected override void UpdateTabsItems() + { + } + } +} diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/TVShellSectionHandler.cs b/src/Controls/src/Core/Platform/Tizen/Shell/TVShellSectionHandler.cs new file mode 100644 index 000000000000..84eb60b0d539 --- /dev/null +++ b/src/Controls/src/Core/Platform/Tizen/Shell/TVShellSectionHandler.cs @@ -0,0 +1,285 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using ElmSharp; +using Tizen.UIExtensions.Common; +using Tizen.UIExtensions.ElmSharp; +using EBox = ElmSharp.Box; +using TCollectionView = Tizen.UIExtensions.ElmSharp.CollectionView; +using TNavigationView = Tizen.UIExtensions.ElmSharp.NavigationView; +using ITCollectionViewController = Tizen.UIExtensions.ElmSharp.ICollectionViewController; +using TSelectedItemChangedEventArgs = Tizen.UIExtensions.ElmSharp.SelectedItemChangedEventArgs; + + +namespace Microsoft.Maui.Controls.Platform +{ + public class TVShellSectionHandler : IShellSectionHandler, IDisposable + { + EBox _mainLayout; + EBox _contentArea; + EvasObject? _currentContent = null; + + TNavigationView? _navigationView; + TCollectionView? _itemsView; + + IList? _cachedItems; + Dictionary _contentCache = new Dictionary(); + + bool _disposed = false; + + bool _drawerIsVisible => (ShellSection != null) ? (ShellSection.Items.Count > 1) : false; + + public TVShellSectionHandler(ShellSection section, IMauiContext context) + { + ShellSection = section; + MauiContext = context; + ShellSection.PropertyChanged += OnSectionPropertyChanged; + if (ShellSection.Items is INotifyCollectionChanged collection) + { + collection.CollectionChanged += OnShellSectionCollectionChanged; + } + + _mainLayout = new EBox(NativeParent); + _mainLayout.SetLayoutCallback(OnLayout); + + _contentArea = new EBox(NativeParent); + _contentArea.Show(); + _mainLayout.PackEnd(_contentArea); + + UpdateSectionItems(); + UpdateCurrentItem(ShellSection.CurrentItem); + } + + public ShellSection ShellSection { get; } + + public EvasObject NativeView + { + get + { + return _mainLayout; + } + } + + protected IMauiContext MauiContext { get; private set; } + + protected TCollectionView? ItemsView => _itemsView; + + protected EvasObject? NativeParent + { + get => MauiContext?.Context?.BaseLayout; + } + + ~TVShellSectionHandler() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + if (ShellSection != null) + { + ShellSection.PropertyChanged -= OnSectionPropertyChanged; + } + + NativeView.Unrealize(); + } + _disposed = true; + } + + protected virtual TCollectionView CreateItemsView() + { + _ = NativeParent ?? throw new InvalidOperationException($"{nameof(NativeParent)} should have been set by base class."); + + return new TCollectionView(NativeParent) + { + AlignmentX = -1, + AlignmentY = -1, + WeightX = 1, + WeightY = 1, + SelectionMode = CollectionViewSelectionMode.Single, + HorizontalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible, + VerticalScrollBarVisiblePolicy = ScrollBarVisiblePolicy.Invisible, + LayoutManager = new LinearLayoutManager(false) + }; + } + + void OnNavigationViewSelectedItemChanged(object sender, ItemSelectedEventArgs e) + { + if (e.SelectedItem == null) + return; + + var content = e.SelectedItem; + if (ShellSection.CurrentItem != content) + { + ShellSection.SetValueFromRenderer(ShellSection.CurrentItemProperty, content); + } + } + + void OnSectionPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == ShellSection.CurrentItemProperty.PropertyName) + { + UpdateCurrentItem(ShellSection.CurrentItem); + } + } + + void UpdateSectionItems() + { + if (!_drawerIsVisible) + { + return; + } + + if (_navigationView == null && NativeParent != null) + { + _navigationView = new TVNavigationView(NativeParent); + _navigationView.SetAlignment(-1, -1); + _navigationView.SetWeight(1, 1); + _navigationView.Show(); + _mainLayout.PackStart(_navigationView); + + _navigationView.LayoutUpdated += (s, e) => + { + var drawerBound = e.Geometry; + var drawerWidth = GetDrawerWidth(); + }; + + _navigationView.Content = _itemsView = CreateItemsView(); + } + + BuildMenu(); + } + + bool IsItemChanged(IList items) + { + if (_cachedItems == null) + return true; + + if (_cachedItems.Count != items.Count) + return true; + + for (int i = 0; i < items.Count; i++) + { + if (_cachedItems[i] != items[i]) + return true; + } + + _cachedItems = items; + return false; + } + + void BuildMenu() + { + var items = ShellSection.Items; + + if (!IsItemChanged(items)) + return; + + _cachedItems = items; + + if (ItemsView != null) + { + ItemsView.Adaptor = new TVShellItemAdaptor(ShellSection, _navigationView, MauiContext, items, false); + ItemsView.Adaptor.ItemSelected += OnItemSelected; + } + } + + void UpdateCurrentItem(ShellContent content) + { + if (_currentContent != null) + { + _currentContent.Hide(); + _contentArea.UnPack(_currentContent); + _currentContent = null; + } + + if (content == null) + { + return; + } + + if (!_contentCache.ContainsKey(content)) + { + var native = CreateShellContent(content); + native.SetAlignment(-1, -1); + native.SetWeight(1, 1); + _contentCache[content] = native; + } + _currentContent = _contentCache[content]; + _currentContent.Show(); + _contentArea.PackEnd(_currentContent); + } + + EvasObject CreateShellContent(ShellContent content) + { + Page xpage = ((IShellContentController)content).GetOrCreateContent(); + return xpage.ToNative(MauiContext); + } + + void OnLayout() + { + if (NativeView.Geometry.Width == 0 || NativeView.Geometry.Height == 0) + return; + + var bound = NativeView.Geometry; + var drawerWidth = 0; + + if (_drawerIsVisible && _navigationView != null) + { + var drawerBound = bound; + drawerWidth = GetDrawerWidth(); + drawerBound.Width = drawerWidth; + + _navigationView.Geometry = drawerBound; + } + + var contentBound = bound; + + contentBound.X += drawerWidth; + contentBound.Width -= drawerWidth; + _contentArea.Geometry = contentBound; + } + + int GetDrawerWidth() + { + _ = NativeParent ?? throw new InvalidOperationException($"{nameof(NativeParent)} should have been set by base class."); + + int width = 0; + if (ItemsView is ITCollectionViewController controller) + width = controller.GetItemSize((NativeParent.Geometry.Width / 2), NativeParent.Geometry.Height).Width; + + return width; + } + + void OnItemSelected(object? sender, TSelectedItemChangedEventArgs e) + { + if (e.SelectedItem == null) + return; + + var content = e.SelectedItem; + if (ShellSection.CurrentItem != content) + { + ShellSection.SetValueFromRenderer(ShellSection.CurrentItemProperty, content); + } + } + + void OnShellSectionCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + UpdateSectionItems(); + } + } +} \ No newline at end of file diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/TVShellSectionStack.cs b/src/Controls/src/Core/Platform/Tizen/Shell/TVShellSectionStack.cs new file mode 100644 index 000000000000..6b60ac79db5f --- /dev/null +++ b/src/Controls/src/Core/Platform/Tizen/Shell/TVShellSectionStack.cs @@ -0,0 +1,16 @@ +namespace Microsoft.Maui.Controls.Platform +{ + public class TVShellSectionStack : ShellSectionStack + { + public TVShellSectionStack(ShellSection section, IMauiContext context) : base(section, context) + { + } + + public override bool NavBarIsVisible => false; + + protected override IShellSectionHandler CreateShellSectionView(ShellSection section) + { + return new TVShellSectionHandler(section, MauiContext); + } + } +} diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/TVShellView.cs b/src/Controls/src/Core/Platform/Tizen/Shell/TVShellView.cs new file mode 100644 index 000000000000..9ebaada57ed2 --- /dev/null +++ b/src/Controls/src/Core/Platform/Tizen/Shell/TVShellView.cs @@ -0,0 +1,43 @@ +using System.Collections; +using ElmSharp; +using Microsoft.Maui.Graphics; +using Tizen.UIExtensions.ElmSharp; +using ITNavigationView = Tizen.UIExtensions.ElmSharp.INavigationView; + +namespace Microsoft.Maui.Controls.Platform +{ + public class TVShellView : ShellView + { + public TVShellView(EvasObject parent) : base(parent) + { + } + + public override void SetElement(Shell shell, IMauiContext context) + { + base.SetElement(shell, context); + + // Workaround to set to use a default color for TV different from the mobile + shell.SetAppThemeColor(Shell.FlyoutBackgroundColorProperty, Colors.Black, Colors.Black); + } + + protected override INavigationDrawer CreateNavigationDrawer() + { + return new TVNavigationDrawer(NativeParent); + } + + protected override ITNavigationView CreateNavigationView() + { + return new TVNavigationView(NativeParent); + } + + protected override ShellItemView CreateShellItemView(ShellItem item) + { + return new TVShellItemView(item, MauiContext); + } + + protected override ItemAdaptor GetItemAdaptor(IEnumerable items) + { + return new TVShellItemAdaptor(Element, NavigationView, MauiContext, items, !Element.FlyoutIsPresented); + } + } +} diff --git a/src/Controls/src/Core/Platform/Tizen/Shell/ThemeConstants.cs b/src/Controls/src/Core/Platform/Tizen/Shell/ThemeConstants.cs new file mode 100644 index 000000000000..1bf9fac34ed1 --- /dev/null +++ b/src/Controls/src/Core/Platform/Tizen/Shell/ThemeConstants.cs @@ -0,0 +1,48 @@ +namespace Microsoft.Maui.Controls.Platform +{ + public class ThemeConstants + { + public class Shell + { + public class Resources + { + public const int DefaultMargin = 10; + public const int DefaultNavBarHeight = 70; + public const int DefaultMenuSize = 40; + + public const int DefaultTitleFontSize = 23; + public const int DefaultTitleMargin = 23; + + public const int DefaultIconSize = 30; + public const int DefaultIconPadding = 15; + + public const double DefaultFlyoutRatio = 0.83; + public const int DefaultFlyoutItemHeight = 60; + public const int DefaultFlyoutItemWidth = 250; + + public class TV + { + public const int DefaultMenuSize = 70; + + public const double DefaultFlyoutRatio = 0.3; + public const double DefaultFlyoutRatioMin = 0.05; + + public const int DefaultFlyoutIconColumnSize = 40; + public const int DefaultFlyoutIconSize = 25; + + public const int DefaultFlyoutItemfontSize = 25; + } + } + + public class ColorClass + { + public class TV + { + public static readonly Graphics.Color DefaultBackgroundColor = Graphics.Colors.Black; + public static readonly Graphics.Color DefaultFlyoutItemColor = Graphics.Colors.Transparent; + public static readonly Graphics.Color DefaultFlyoutItemFocusedColor = new Graphics.Color(0.95f); + } + } + } + } +} diff --git a/src/Controls/src/Core/Platform/Tizen/Extensions/ShellExtensions.cs b/src/Controls/src/Core/Platform/Tizen/Shell/ThemeManager.cs similarity index 51% rename from src/Controls/src/Core/Platform/Tizen/Extensions/ShellExtensions.cs rename to src/Controls/src/Core/Platform/Tizen/Shell/ThemeManager.cs index 012c1d01108b..9d5c7b547ed7 100644 --- a/src/Controls/src/Core/Platform/Tizen/Extensions/ShellExtensions.cs +++ b/src/Controls/src/Core/Platform/Tizen/Shell/ThemeManager.cs @@ -1,116 +1,147 @@ using Tizen.UIExtensions.Common; -using TINavigtaionView = Tizen.UIExtensions.ElmSharp.INavigationView; +using Tizen.UIExtensions.ElmSharp; +using ITNavigtaionView = Tizen.UIExtensions.ElmSharp.INavigationView; namespace Microsoft.Maui.Controls.Platform { - public static class ShellExtensions + public static class ThemeManager { + #region ShellMoreTabs + static double s_shellMoreToolBarIconPadding = -1; + public static double GetDefaultIconPadding(this ShellMoreTabs self) + { + if (s_shellMoreToolBarIconPadding > 0) + return s_shellMoreToolBarIconPadding; + return s_shellMoreToolBarIconPadding = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(ThemeConstants.Shell.Resources.DefaultIconPadding); + } + + static double s_shellMoreToolBarIconSize = -1; + public static double GetDefaultIconSize(this ShellMoreTabs self) + { + if (s_shellMoreToolBarIconSize > 0) + return s_shellMoreToolBarIconSize; + return s_shellMoreToolBarIconSize = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(ThemeConstants.Shell.Resources.DefaultIconSize); + } + #endregion + + #region ShellNavBar + static double s_shellNavBarDefaultHeight = -1; + public static double GetDefaultNavBarHeight(this ShellNavBar navBar) + { + if (s_shellNavBarDefaultHeight > 0) + return s_shellNavBarDefaultHeight; + return s_shellNavBarDefaultHeight = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(ThemeConstants.Shell.Resources.DefaultNavBarHeight); + } + + static double s_shellNavBarDefaultMenuSize = -1; + public static double GetDefaultMenuSize(this ShellNavBar navBar) + { + if (s_shellNavBarDefaultMenuSize > 0) + return s_shellNavBarDefaultMenuSize; + return s_shellNavBarDefaultMenuSize = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen( + Device.Idiom == TargetIdiom.TV ? ThemeConstants.Shell.Resources.TV.DefaultMenuSize : ThemeConstants.Shell.Resources.DefaultMenuSize); + } + + static double s_shellNavBarDefaultMargin = -1; + public static double GetDefaultMargin(this ShellNavBar navBar) + { + if (s_shellNavBarDefaultMargin > 0) + return s_shellNavBarDefaultMargin; + return s_shellNavBarDefaultMargin = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(ThemeConstants.Shell.Resources.DefaultMargin); + } + + static double s_shellNavBarTitleVDefaultMargin = -1; + public static double GetDefaultTitleVMargin(this ShellNavBar navBar) + { + if (s_shellNavBarTitleVDefaultMargin > 0) + return s_shellNavBarTitleVDefaultMargin; + return s_shellNavBarTitleVDefaultMargin = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(ThemeConstants.Shell.Resources.DefaultTitleMargin); + } + + static double s_shellNavBarTitleFontSize = -1; + public static double GetDefaultTitleFontSize(this ShellNavBar navBar) + { + if (s_shellNavBarTitleFontSize > 0) + return s_shellNavBarTitleFontSize; + return s_shellNavBarTitleFontSize = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(ThemeConstants.Shell.Resources.DefaultTitleFontSize); + } + #endregion + + #region TVShell static double s_navigationViewFlyoutItemHeight = -1; - public static double GetFlyoutItemHeight(this TINavigtaionView nav) + public static double GetTvFlyoutItemHeight(this ITNavigtaionView nav) { if (s_navigationViewFlyoutItemHeight > 0) return s_navigationViewFlyoutItemHeight; - return s_navigationViewFlyoutItemHeight = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(60); + return s_navigationViewFlyoutItemHeight = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(ThemeConstants.Shell.Resources.DefaultFlyoutItemHeight); } static double s_navigationViewFlyoutItemWidth = -1; - public static double GetFlyoutItemWidth(this TINavigtaionView nav) + public static double GetTvFlyoutItemWidth(this ITNavigtaionView nav) { if (s_navigationViewFlyoutItemWidth > 0) return s_navigationViewFlyoutItemWidth; - return s_navigationViewFlyoutItemWidth = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(200); + return s_navigationViewFlyoutItemWidth = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(ThemeConstants.Shell.Resources.DefaultFlyoutItemWidth); } static double s_navigationViewFlyoutIconColumnSize = -1; - public static double GetFlyoutIconColumnSize(this TINavigtaionView nav) + public static double GetTvFlyoutIconColumnSize(this ITNavigtaionView nav) { if (s_navigationViewFlyoutIconColumnSize > 0) return s_navigationViewFlyoutIconColumnSize; - return s_navigationViewFlyoutIconColumnSize = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(40); + return s_navigationViewFlyoutIconColumnSize = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(ThemeConstants.Shell.Resources.TV.DefaultFlyoutIconColumnSize); } static double s_navigationViewFlyoutIconSize = -1; - public static double GetFlyoutIconSize(this TINavigtaionView nav) + public static double GetTvFlyoutIconSize(this ITNavigtaionView nav) { if (s_navigationViewFlyoutIconSize > 0) return s_navigationViewFlyoutIconSize; - return s_navigationViewFlyoutIconSize = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(25); + return s_navigationViewFlyoutIconSize = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(ThemeConstants.Shell.Resources.TV.DefaultFlyoutIconSize); } static double s_navigationViewFlyoutMargin = -1; - public static double GetFlyoutMargin(this TINavigtaionView nav) + public static double GetTvFlyoutMargin(this ITNavigtaionView nav) { if (s_navigationViewFlyoutMargin > 0) return s_navigationViewFlyoutMargin; - return s_navigationViewFlyoutMargin = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(10); + return s_navigationViewFlyoutMargin = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(ThemeConstants.Shell.Resources.DefaultMargin); } static double s_navigationViewFlyoutItemFontSize = -1; - public static double GetFlyoutItemFontSize(this TINavigtaionView nav) + public static double GetTvFlyoutItemFontSize(this ITNavigtaionView nav) { if (s_navigationViewFlyoutItemFontSize > 0) return s_navigationViewFlyoutItemFontSize; - return s_navigationViewFlyoutItemFontSize = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(25); + return s_navigationViewFlyoutItemFontSize = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(ThemeConstants.Shell.Resources.TV.DefaultFlyoutItemfontSize); } - #region ShellMoreToolbar - - static double s_shellMoreToolBarIconPadding = -1; - public static double GetIconPadding(this ShellMoreTabs self) - { - if (s_shellMoreToolBarIconPadding > 0) - return s_shellMoreToolBarIconPadding; - return s_shellMoreToolBarIconPadding = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(15); - } - - static double s_shellMoreToolBarIconSize = -1; - public static double GetIconSize(this ShellMoreTabs self) - { - if (s_shellMoreToolBarIconSize > 0) - return s_shellMoreToolBarIconSize; - return s_shellMoreToolBarIconSize = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(30); - } - #endregion - - #region ShellNavBar - static double s_shellNavBarDefaultHeight = -1; - public static double GetDefaultHeight(this ShellNavBar navBar) + public static double GetTvFlyoutRatioMin(this INavigationDrawer drawer) { - if (s_shellNavBarDefaultHeight > 0) - return s_shellNavBarDefaultHeight; - return s_shellNavBarDefaultHeight = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(70); + return ThemeConstants.Shell.Resources.TV.DefaultFlyoutRatioMin; } - static double s_shellNavBarDefaultMenuSize = -1; - public static double GetDefaultMenuSize(this ShellNavBar navBar) + public static Graphics.Color GetTvDefaultBackgroundColor(this ITNavigtaionView nav) { - if (s_shellNavBarDefaultMenuSize > 0) - return s_shellNavBarDefaultMenuSize; - return s_shellNavBarDefaultMenuSize = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(Device.Idiom == TargetIdiom.TV ? 70 : 40); + return ThemeConstants.Shell.ColorClass.TV.DefaultBackgroundColor; } - static double s_shellNavBarDefaultMargin = -1; - public static double GetDefaultMargin(this ShellNavBar navBar) + public static Graphics.Color GetTvFlyoutItemColor(this ITNavigtaionView nav) { - if (s_shellNavBarDefaultMargin > 0) - return s_shellNavBarDefaultMargin; - return s_shellNavBarDefaultMargin = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(10); + return ThemeConstants.Shell.ColorClass.TV.DefaultFlyoutItemColor; } - static double s_shellNavBarTitleVDefaultMargin = -1; - public static double GetDefaultTitleVMargin(this ShellNavBar navBar) + public static Graphics.Color GetTvFlyoutItemFocusedColor(this ITNavigtaionView nav) { - if (s_shellNavBarTitleVDefaultMargin > 0) - return s_shellNavBarTitleVDefaultMargin; - return s_shellNavBarTitleVDefaultMargin = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(23); + return ThemeConstants.Shell.ColorClass.TV.DefaultFlyoutItemFocusedColor; } + #endregion - static double s_shellNavBarTitleFontSize = -1; - public static double GetDefaultTitleFontSize(this ShellNavBar navBar) + #region TVNavigationDrawer + static double s_navigationDrawerRatio = -1; + public static double GetTvFlyoutRatio(this INavigationDrawer drawer, int width, int height) { - if (s_shellNavBarTitleFontSize > 0) - return s_shellNavBarTitleFontSize; - return s_shellNavBarTitleFontSize = DeviceInfo.CalculateDoubleScaledSizeInLargeScreen(23); + return s_navigationDrawerRatio = (width > height) ? ThemeConstants.Shell.Resources.TV.DefaultFlyoutRatio : ThemeConstants.Shell.Resources.DefaultFlyoutRatio; } #endregion } diff --git a/src/Core/src/Handlers/ContentView/ContentViewHandler.Tizen.cs b/src/Core/src/Handlers/ContentView/ContentViewHandler.Tizen.cs index d967c57d63e2..2d2ae24710ca 100644 --- a/src/Core/src/Handlers/ContentView/ContentViewHandler.Tizen.cs +++ b/src/Core/src/Handlers/ContentView/ContentViewHandler.Tizen.cs @@ -1,6 +1,7 @@ using System; using NativeView = ElmSharp.EvasObject; using EColor = ElmSharp.Color; +using Tizen.UIExtensions.Common; namespace Microsoft.Maui.Handlers { @@ -15,10 +16,12 @@ protected override ContentCanvas CreateNativeView() var view = new ContentCanvas(NativeParent, VirtualView) { - BackgroundColor = EColor.White, CrossPlatformMeasure = VirtualView.CrossPlatformMeasure, CrossPlatformArrange = VirtualView.CrossPlatformArrange }; + + view.BackgroundColor = (DeviceInfo.GetDeviceType() == DeviceType.TV) ? EColor.Transparent : EColor.White; + view.Show(); return view; }