diff --git a/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/TabView/TabView.shared.cs b/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/TabView/TabView.shared.cs index 0acd7ec4..4031938f 100644 --- a/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/TabView/TabView.shared.cs +++ b/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/TabView/TabView.shared.cs @@ -518,7 +518,7 @@ namespace Xamarin.CommunityToolkit.UI.Views tabStripContent.Children.Remove(tabViewItem); } - void AddTabViewItem(TabViewItem tabViewItem, int index = -1) + void (TabViewItem tabViewItem, int index = -1) { tabViewItem.PropertyChanged -= OnTabViewItemPropertyChanged; tabViewItem.PropertyChanged += OnTabViewItemPropertyChanged; @@ -535,544 +535,544 @@ namespace Xamarin.CommunityToolkit.UI.Views { // Default ControlTemplate for other platforms tabViewItem.ControlTemplate = new ControlTemplate(typeof(MaterialTabViewItemTemplate)); - } - } + } +} - AddSelectionTapRecognizer(tabViewItem); +AddSelectionTapRecognizer(tabViewItem); - AddTabViewItemToTabStrip(tabViewItem, index); +AddTabViewItemToTabStrip(tabViewItem, index); - UpdateTabContentSize(); - UpdateTabStripSize(); +UpdateTabContentSize(); +UpdateTabStripSize(); - if (SelectedIndex != 0) - UpdateSelectedIndex(0); +if (SelectedIndex != 0) + UpdateSelectedIndex(0); } void UpdateTabStripSize() - { - var tabStripSize = tabStripContent.Measure(double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins); +{ + var tabStripSize = tabStripContent.Measure(double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins); - if (tabStripContainer.HeightRequest != tabStripSize.Request.Height) - tabStripContainer.HeightRequest = tabStripSize.Request.Height; + if (tabStripContainer.HeightRequest != tabStripSize.Request.Height) + tabStripContainer.HeightRequest = tabStripSize.Request.Height; +} + +void UpdateTabContentSize() +{ + var items = contentContainer.ItemsSource; + + var count = 0; + + var enumerator = items.GetEnumerator(); + + while (enumerator.MoveNext()) + count++; + + VerticalOptions = count != 0 ? LayoutOptions.FillAndExpand : LayoutOptions.Start; + mainContainer.HeightRequest = count != 0 ? (TabContentHeight + TabStripHeight) : TabStripHeight; + UpdateTabContentHeight(count != 0 ? TabContentHeight : 0); +} + +void AddTabViewItemFromTemplate(object? item, int index = -1) => AddTabViewItemFromTemplateToTabStrip(item, index); + +void UpdateTabViewItemBindingContext(TabViewItem tabViewItem) +{ + if (tabViewItem == null || tabViewItem.Content == null) + return; + + tabViewItem.Content.BindingContext = BindingContext; +} + +void AddSelectionTapRecognizer(View view) +{ + var tapRecognizer = new TapGestureRecognizer(); + + tapRecognizer.Tapped += (object? sender, EventArgs args) => + { + if (sender is not View view) + return; + + var capturedIndex = tabStripContent.Children.IndexOf(view); + + if (view is TabViewItem tabViewItem) + { + var tabTappedEventArgs = new TabTappedEventArgs(capturedIndex); + tabViewItem.OnTabTapped(tabTappedEventArgs); } - void UpdateTabContentSize() + if (CanUpdateSelectedIndex(capturedIndex)) { - var items = contentContainer.ItemsSource; - - var count = 0; - - var enumerator = items.GetEnumerator(); - - while (enumerator.MoveNext()) - count++; - - VerticalOptions = count != 0 ? LayoutOptions.FillAndExpand : LayoutOptions.Start; - mainContainer.HeightRequest = count != 0 ? (TabContentHeight + TabStripHeight) : TabStripHeight; - UpdateTabContentHeight(count != 0 ? TabContentHeight : 0); + if (SelectedIndex != capturedIndex) + UpdateSelectedIndex(capturedIndex); } + }; - void AddTabViewItemFromTemplate(object? item, int index = -1) => AddTabViewItemFromTemplateToTabStrip(item, index); + view.GestureRecognizers.Add(tapRecognizer); +} - void UpdateTabViewItemBindingContext(TabViewItem tabViewItem) +void AddTabViewItemToTabStrip(View item, int index = -1) +{ + var tabViewItemSizeRequest = item.Measure(double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins); + + if (tabViewItemSizeRequest.Request.Height < TabStripHeight) + { + item.HeightRequest = TabStripHeight; + item.VerticalOptions = TabStripPlacement == TabStripPlacement.Top ? LayoutOptions.Start : LayoutOptions.End; + } + + tabStripContent.ColumnDefinitions.Add(new ColumnDefinition() + { + Width = (item is TabViewItem tabViewItem && tabViewItem.TabWidth > 0) ? tabViewItem.TabWidth : GridLength.Star + }); + + if (index >= 0) + { + tabStripContent.Children.Insert(index, item); + + for (var i = index; i < tabStripContent.Children.Count; i++) + Grid.SetColumn(tabStripContent.Children[i], i); + } + else + { + tabStripContent.Children.Add(item); + var count = tabStripContent.Children.Count - 1; + item.SetValue(Grid.ColumnProperty, count); + } + + UpdateTabViewItemTabWidth(item as TabViewItem); +} + +void AddTabViewItemFromTemplateToTabStrip(object? item, int index = -1) +{ + var view = TabViewItemDataTemplate is not DataTemplateSelector tabItemDataTemplate + ? (View)(TabViewItemDataTemplate?.CreateContent() ?? throw new NullReferenceException()) + : (View)tabItemDataTemplate.SelectTemplate(item, this).CreateContent(); + + view.BindingContext = item; + + view.Effects.Add(new VisualFeedbackEffect()); + + AddSelectionTapRecognizer(view); + AddTabViewItemToTabStrip(view, index); +} + +void UpdateIsEnabled() +{ + if (IsEnabled) + { + contentContainer.PropertyChanged += OnContentContainerPropertyChanged; + contentContainer.Scrolled += OnContentContainerScrolled; + + TabItems.CollectionChanged += OnTabItemsCollectionChanged; + } + else + { + contentContainer.PropertyChanged -= OnContentContainerPropertyChanged; + contentContainer.Scrolled -= OnContentContainerScrolled; + + TabItems.CollectionChanged -= OnTabItemsCollectionChanged; + } + + tabStripContent.IsEnabled = IsEnabled; + contentContainer.IsEnabled = IsEnabled; +} + +void UpdateTabViewItemTabWidth(TabViewItem? tabViewItem) +{ + if (tabViewItem == null) + return; + + var index = tabStripContent.Children.IndexOf(tabViewItem); + var colummns = tabStripContent.ColumnDefinitions; + + ColumnDefinition? column = null; + + if (index < colummns.Count) + column = colummns[index]; + + if (column == null) + return; + + column.Width = tabViewItem.TabWidth > 0 ? tabViewItem.TabWidth : GridLength.Star; + UpdateTabIndicatorPosition(SelectedIndex); +} + +void UpdateTabItemsSource() +{ + if (TabItemsSource == null || TabViewItemDataTemplate == null) + return; + + if (tabItemsSource is INotifyCollectionChanged oldnNotifyTabItemsSource) + oldnNotifyTabItemsSource.CollectionChanged -= OnTabItemsSourceCollectionChanged; + + tabItemsSource = TabItemsSource; + + if (tabItemsSource is INotifyCollectionChanged newNotifyTabItemsSource) + newNotifyTabItemsSource.CollectionChanged += OnTabItemsSourceCollectionChanged; + + ClearTabStrip(); + + contentContainer.ItemTemplate = TabContentDataTemplate; + contentContainer.ItemsSource = TabItemsSource; + + foreach (var item in TabItemsSource) + { + AddTabViewItemFromTemplate(item); + } + + UpdateTabContentSize(); + UpdateTabStripSize(); + if (SelectedIndex != 0) + UpdateSelectedIndex(0); +} + +void OnTabItemsSourceCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) => UpdateTabItemsSource(); + +void UpdateItemsSource(IEnumerable items) +{ + contentWidthCollection.Clear(); + + if (contentContainer.VisibleViews.Count == 0) + return; + + var contentWidth = contentContainer.VisibleViews.FirstOrDefault().Width; + var tabItemsCount = items.Cast().Count(); + + for (var i = 0; i < tabItemsCount; i++) + contentWidthCollection.Add(contentWidth * i); +} + +bool CanUpdateSelectedIndex(int selectedIndex) +{ + if (TabItems == null || TabItems.Count == 0) + return true; + + var tabItem = TabItems[selectedIndex]; + + if (tabItem != null && tabItem.Content == null) + { + var itemsCount = TabItems.Count; + var contentItemsCount = TabItems.Count(t => t.Content == null); + + return itemsCount == contentItemsCount; + } + + return true; +} + +void UpdateSelectedIndex(int position, bool hasCurrentItem = false) +{ + if (position < 0) + return; + var oldposition = SelectedIndex; + + var newPosition = position; + + Device.BeginInvokeOnMainThread(async () => + { + if (contentTabItems == null || contentTabItems.Count != TabItems.Count) + contentTabItems = new ObservableCollection(TabItems.Where(t => t.Content != null)); + + var contentIndex = position; + var tabStripIndex = position; + + if (TabItems.Count > 0) { - if (tabViewItem == null || tabViewItem.Content == null) - return; + TabViewItem? currentItem = null; - tabViewItem.Content.BindingContext = BindingContext; - } + if (hasCurrentItem) + currentItem = (TabViewItem)contentContainer.CurrentItem; - void AddSelectionTapRecognizer(View view) - { - var tapRecognizer = new TapGestureRecognizer(); + var tabViewItem = TabItems[position]; - tapRecognizer.Tapped += (object? sender, EventArgs args) => + var lazyView = (currentItem?.Content as BaseLazyView) ?? (tabViewItem.Content as BaseLazyView); + + contentIndex = contentTabItems.IndexOf(currentItem ?? tabViewItem); + tabStripIndex = TabItems.IndexOf(currentItem ?? tabViewItem); + + position = tabStripIndex; + + for (var index = 0; index < TabItems.Count; index++) { - if (sender is not View view) - return; + if (index == position) + TabItems[position].IsSelected = true; + else + TabItems[index].IsSelected = false; + } - var capturedIndex = tabStripContent.Children.IndexOf(view); + if (lazyView != null && !lazyView.IsLoaded) + await lazyView.LoadViewAsync(); - if (view is TabViewItem tabViewItem) - { - var tabTappedEventArgs = new TabTappedEventArgs(capturedIndex); - tabViewItem.OnTabTapped(tabTappedEventArgs); - } + var currentTabItem = TabItems[position]; + currentTabItem.SizeChanged += OnCurrentTabItemSizeChanged; + UpdateTabIndicatorPosition(currentTabItem); + } + else + UpdateTabIndicatorPosition(position); - if (CanUpdateSelectedIndex(capturedIndex)) - { - if (SelectedIndex != capturedIndex) - UpdateSelectedIndex(capturedIndex); - } + if (contentIndex >= 0) + contentContainer.Position = contentIndex; + + if (tabStripContent.Children.Count > 0) + await tabStripContainerScroll.ScrollToAsync(tabStripContent.Children[tabStripIndex], ScrollToPosition.MakeVisible, false); + + SelectedIndex = position; + if (oldposition != SelectedIndex) + { + var selectionChangedArgs = new TabSelectionChangedEventArgs() + { + NewPosition = newPosition, + OldPosition = SelectedIndex }; - view.GestureRecognizers.Add(tapRecognizer); + OnTabSelectionChanged(selectionChangedArgs); } + }); +} - void AddTabViewItemToTabStrip(View item, int index = -1) +void OnCurrentTabItemSizeChanged(object? sender, EventArgs e) +{ + if (sender is not View view) + return; + + var currentTabItem = view; + UpdateTabIndicatorWidth(TabIndicatorWidth > 0 ? TabIndicatorWidth : currentTabItem.Width); + UpdateTabIndicatorPosition(currentTabItem); + currentTabItem.SizeChanged -= OnCurrentTabItemSizeChanged; +} + +void UpdateTabStripPlacement(TabStripPlacement tabStripPlacement) +{ + if (tabStripPlacement == TabStripPlacement.Top) + { + tabStripBackground.VerticalOptions = LayoutOptions.Start; + + Grid.SetRow(tabStripContainer, 0); + Grid.SetRowSpan(tabStripContainer, 2); + + mainContainer.RowDefinitions[0].Height = TabStripHeight > 0 ? TabStripHeight : GridLength.Auto; + mainContainer.RowDefinitions[1].Height = GridLength.Auto; + mainContainer.RowDefinitions[2].Height = GridLength.Star; + + tabStripBorder.VerticalOptions = LayoutOptions.End; + } + + if (tabStripPlacement == TabStripPlacement.Bottom) + { + tabStripBackground.VerticalOptions = LayoutOptions.End; + + Grid.SetRow(tabStripContainer, 1); + Grid.SetRowSpan(tabStripContainer, 2); + + mainContainer.RowDefinitions[0].Height = GridLength.Star; + mainContainer.RowDefinitions[1].Height = GridLength.Auto; + mainContainer.RowDefinitions[2].Height = TabStripHeight > 0 ? TabStripHeight : GridLength.Auto; + + tabStripBorder.VerticalOptions = LayoutOptions.Start; + } + + UpdateTabContentLayout(); + UpdateTabIndicatorMargin(); +} + +void UpdateTabContentLayout() +{ + if (tabStripContainer.IsVisible) + { + if (TabStripPlacement == TabStripPlacement.Top) { - var tabViewItemSizeRequest = item.Measure(double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins); - - if (tabViewItemSizeRequest.Request.Height < TabStripHeight) - { - item.HeightRequest = TabStripHeight; - item.VerticalOptions = TabStripPlacement == TabStripPlacement.Top ? LayoutOptions.Start : LayoutOptions.End; - } - - tabStripContent.ColumnDefinitions.Add(new ColumnDefinition() - { - Width = (item is TabViewItem tabViewItem && tabViewItem.TabWidth > 0) ? tabViewItem.TabWidth : GridLength.Star - }); - - if (index >= 0) - { - tabStripContent.Children.Insert(index, item); - - for (var i = index; i < tabStripContent.Children.Count; i++) - Grid.SetColumn(tabStripContent.Children[i], i); - } - else - { - tabStripContent.Children.Add(item); - var count = tabStripContent.Children.Count - 1; - item.SetValue(Grid.ColumnProperty, count); - } - - UpdateTabViewItemTabWidth(item as TabViewItem); + Grid.SetRow(contentContainer, 1); + Grid.SetRowSpan(contentContainer, 2); } - - void AddTabViewItemFromTemplateToTabStrip(object? item, int index = -1) + else { - var view = TabViewItemDataTemplate is not DataTemplateSelector tabItemDataTemplate - ? (View)(TabViewItemDataTemplate?.CreateContent() ?? throw new NullReferenceException()) - : (View)tabItemDataTemplate.SelectTemplate(item, this).CreateContent(); - - view.BindingContext = item; - - view.Effects.Add(new VisualFeedbackEffect()); - - AddSelectionTapRecognizer(view); - AddTabViewItemToTabStrip(view, index); - } - - void UpdateIsEnabled() - { - if (IsEnabled) - { - contentContainer.PropertyChanged += OnContentContainerPropertyChanged; - contentContainer.Scrolled += OnContentContainerScrolled; - - TabItems.CollectionChanged += OnTabItemsCollectionChanged; - } - else - { - contentContainer.PropertyChanged -= OnContentContainerPropertyChanged; - contentContainer.Scrolled -= OnContentContainerScrolled; - - TabItems.CollectionChanged -= OnTabItemsCollectionChanged; - } - - tabStripContent.IsEnabled = IsEnabled; - contentContainer.IsEnabled = IsEnabled; - } - - void UpdateTabViewItemTabWidth(TabViewItem? tabViewItem) - { - if (tabViewItem == null) - return; - - var index = tabStripContent.Children.IndexOf(tabViewItem); - var colummns = tabStripContent.ColumnDefinitions; - - ColumnDefinition? column = null; - - if (index < colummns.Count) - column = colummns[index]; - - if (column == null) - return; - - column.Width = tabViewItem.TabWidth > 0 ? tabViewItem.TabWidth : GridLength.Star; - UpdateTabIndicatorPosition(SelectedIndex); - } - - void UpdateTabItemsSource() - { - if (TabItemsSource == null || TabViewItemDataTemplate == null) - return; - - if (tabItemsSource is INotifyCollectionChanged oldnNotifyTabItemsSource) - oldnNotifyTabItemsSource.CollectionChanged -= OnTabItemsSourceCollectionChanged; - - tabItemsSource = TabItemsSource; - - if (tabItemsSource is INotifyCollectionChanged newNotifyTabItemsSource) - newNotifyTabItemsSource.CollectionChanged += OnTabItemsSourceCollectionChanged; - - ClearTabStrip(); - - contentContainer.ItemTemplate = TabContentDataTemplate; - contentContainer.ItemsSource = TabItemsSource; - - foreach (var item in TabItemsSource) - { - AddTabViewItemFromTemplate(item); - } - - UpdateTabContentSize(); - UpdateTabStripSize(); - if (SelectedIndex != 0) - UpdateSelectedIndex(0); - } - - void OnTabItemsSourceCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) => UpdateTabItemsSource(); - - void UpdateItemsSource(IEnumerable items) - { - contentWidthCollection.Clear(); - - if (contentContainer.VisibleViews.Count == 0) - return; - - var contentWidth = contentContainer.VisibleViews.FirstOrDefault().Width; - var tabItemsCount = items.Cast().Count(); - - for (var i = 0; i < tabItemsCount; i++) - contentWidthCollection.Add(contentWidth * i); - } - - bool CanUpdateSelectedIndex(int selectedIndex) - { - if (TabItems == null || TabItems.Count == 0) - return true; - - var tabItem = TabItems[selectedIndex]; - - if (tabItem != null && tabItem.Content == null) - { - var itemsCount = TabItems.Count; - var contentItemsCount = TabItems.Count(t => t.Content == null); - - return itemsCount == contentItemsCount; - } - - return true; - } - - void UpdateSelectedIndex(int position, bool hasCurrentItem = false) - { - if (position < 0) - return; - var oldposition = SelectedIndex; - - var newPosition = position; - - Device.BeginInvokeOnMainThread(async () => - { - if (contentTabItems == null || contentTabItems.Count != TabItems.Count) - contentTabItems = new ObservableCollection(TabItems.Where(t => t.Content != null)); - - var contentIndex = position; - var tabStripIndex = position; - - if (TabItems.Count > 0) - { - TabViewItem? currentItem = null; - - if (hasCurrentItem) - currentItem = (TabViewItem)contentContainer.CurrentItem; - - var tabViewItem = TabItems[position]; - - var lazyView = (currentItem?.Content as BaseLazyView) ?? (tabViewItem.Content as BaseLazyView); - - contentIndex = contentTabItems.IndexOf(currentItem ?? tabViewItem); - tabStripIndex = TabItems.IndexOf(currentItem ?? tabViewItem); - - position = tabStripIndex; - - for (var index = 0; index < TabItems.Count; index++) - { - if (index == position) - TabItems[position].IsSelected = true; - else - TabItems[index].IsSelected = false; - } - - if (lazyView != null && !lazyView.IsLoaded) - await lazyView.LoadViewAsync(); - - var currentTabItem = TabItems[position]; - currentTabItem.SizeChanged += OnCurrentTabItemSizeChanged; - UpdateTabIndicatorPosition(currentTabItem); - } - else - UpdateTabIndicatorPosition(position); - - if (contentIndex >= 0) - contentContainer.Position = contentIndex; - - if (tabStripContent.Children.Count > 0) - await tabStripContainerScroll.ScrollToAsync(tabStripContent.Children[tabStripIndex], ScrollToPosition.MakeVisible, false); - - SelectedIndex = position; - if (oldposition != SelectedIndex) - { - var selectionChangedArgs = new TabSelectionChangedEventArgs() - { - NewPosition = newPosition, - OldPosition = SelectedIndex - }; - - OnTabSelectionChanged(selectionChangedArgs); - } - }); - } - - void OnCurrentTabItemSizeChanged(object? sender, EventArgs e) - { - if (sender is not View view) - return; - - var currentTabItem = view; - UpdateTabIndicatorWidth(TabIndicatorWidth > 0 ? TabIndicatorWidth : currentTabItem.Width); - UpdateTabIndicatorPosition(currentTabItem); - currentTabItem.SizeChanged -= OnCurrentTabItemSizeChanged; - } - - void UpdateTabStripPlacement(TabStripPlacement tabStripPlacement) - { - if (tabStripPlacement == TabStripPlacement.Top) - { - tabStripBackground.VerticalOptions = LayoutOptions.Start; - - Grid.SetRow(tabStripContainer, 0); - Grid.SetRowSpan(tabStripContainer, 2); - - mainContainer.RowDefinitions[0].Height = TabStripHeight > 0 ? TabStripHeight : GridLength.Auto; - mainContainer.RowDefinitions[1].Height = GridLength.Auto; - mainContainer.RowDefinitions[2].Height = GridLength.Star; - - tabStripBorder.VerticalOptions = LayoutOptions.End; - } - - if (tabStripPlacement == TabStripPlacement.Bottom) - { - tabStripBackground.VerticalOptions = LayoutOptions.End; - - Grid.SetRow(tabStripContainer, 1); - Grid.SetRowSpan(tabStripContainer, 2); - - mainContainer.RowDefinitions[0].Height = GridLength.Star; - mainContainer.RowDefinitions[1].Height = GridLength.Auto; - mainContainer.RowDefinitions[2].Height = TabStripHeight > 0 ? TabStripHeight : GridLength.Auto; - - tabStripBorder.VerticalOptions = LayoutOptions.Start; - } - - UpdateTabContentLayout(); - UpdateTabIndicatorMargin(); - } - - void UpdateTabContentLayout() - { - if (tabStripContainer.IsVisible) - { - if (TabStripPlacement == TabStripPlacement.Top) - { - Grid.SetRow(contentContainer, 1); - Grid.SetRowSpan(contentContainer, 2); - } - else - { - Grid.SetRow(contentContainer, 0); - Grid.SetRowSpan(contentContainer, 2); - } - } - else - { - Grid.SetRow(contentContainer, 0); - Grid.SetRowSpan(contentContainer, 3); - } - - if (TabStripBackgroundView != null) - { - var tabStripBackgroundViewHasCornerRadius = - (TabStripBackgroundView is IBorderElement borderElement && borderElement.CornerRadius != default) || - (TabStripBackgroundView is BoxView boxView && boxView.CornerRadius != default); - - if (tabStripBackgroundViewHasCornerRadius) - { - Grid.SetRow(contentContainer, 0); - Grid.SetRowSpan(contentContainer, 3); - } - } - } - - void UpdateTabStripBackgroundColor(Color tabStripBackgroundColor) - { - tabStripBackground.BackgroundColor = tabStripBackgroundColor; - - if (Device.RuntimePlatform == Device.macOS) - tabStripContainerScroll.BackgroundColor = tabStripBackgroundColor; - } - - void UpdateTabStripBackgroundView(View tabStripBackgroundView) - { - if (tabStripBackgroundView != null) - tabStripBackground.Children.Add(tabStripBackgroundView); - else - tabStripBackground.Children.Clear(); - - UpdateTabContentLayout(); - } - - void UpdateTabStripBorderColor(Color tabStripBorderColor) - { - tabStripBorder.Color = tabStripBorderColor; - - UpdateTabIndicatorMargin(); - } - - void UpdateTabIndicatorMargin() - { - if (TabStripBorderColor == Color.Default) - return; - - if (TabStripPlacement == TabStripPlacement.Top && TabIndicatorPlacement == TabIndicatorPlacement.Bottom) - tabStripIndicator.Margin = new Thickness(0, 0, 0, 1); - - if (TabStripPlacement == TabStripPlacement.Bottom && TabIndicatorPlacement == TabIndicatorPlacement.Top) - tabStripIndicator.Margin = new Thickness(0, 1, 0, 0); - } - - void UpdateTabContentBackgroundColor(Color tabContentBackgroundColor) => contentContainer.BackgroundColor = tabContentBackgroundColor; - - void UpdateTabStripHeight(double tabStripHeight) => tabStripBackground.HeightRequest = tabStripHeight; - - void UpdateIsTabStripVisible(bool isTabStripVisible) - { - tabStripContainer.IsVisible = isTabStripVisible; - - UpdateTabContentLayout(); - } - - void UpdateTabContentHeight(double tabContentHeight) => contentContainer.HeightRequest = tabContentHeight; - - void UpdateTabIndicatorColor(Color tabIndicatorColor) => tabStripIndicator.BackgroundColor = tabIndicatorColor; - - void UpdateTabIndicatorHeight(double tabIndicatorHeight) => tabStripIndicator.HeightRequest = tabIndicatorHeight; - - void UpdateTabIndicatorWidth(double tabIndicatorWidth) => tabStripIndicator.WidthRequest = tabIndicatorWidth; - - void UpdateTabIndicatorView(View tabIndicatorView) - { - if (tabIndicatorView != null) - tabStripIndicator.Children.Add(tabIndicatorView); - else - tabStripIndicator.Children.Clear(); - } - - void UpdateTabIndicatorPlacement(TabIndicatorPlacement tabIndicatorPlacement) - { - switch (tabIndicatorPlacement) - { - case TabIndicatorPlacement.Top: - tabStripIndicator.VerticalOptions = LayoutOptions.Start; - break; - case TabIndicatorPlacement.Center: - tabStripIndicator.VerticalOptions = LayoutOptions.Center; - break; - case TabIndicatorPlacement.Bottom: - default: - tabStripIndicator.VerticalOptions = LayoutOptions.End; - break; - } - - UpdateTabIndicatorMargin(); - } - - void UpdateIsSwipeEnabled(bool isSwipeEnabled) => contentContainer.IsSwipeEnabled = isSwipeEnabled; - - void UpdateIsTabTransitionEnabled(bool isTabTransitionEnabled) => contentContainer.IsScrollAnimated = isTabTransitionEnabled; - - void UpdateTabIndicatorPosition(int tabViewItemIndex) - { - if (tabStripContent == null || tabStripContent.Children.Count == 0 || tabViewItemIndex == -1) - return; - - var currentTabViewItem = tabStripContent.Children[tabViewItemIndex]; - - if (currentTabViewItem.Width <= 0) - currentTabViewItem.SizeChanged += OnCurrentTabItemSizeChanged; - else - UpdateTabIndicatorWidth(currentTabViewItem.Width); - - UpdateTabIndicatorPosition(currentTabViewItem); - } - - void UpdateTabIndicatorPosition(ItemsViewScrolledEventArgs args) - { - if (args.HorizontalOffset == 0) - { - UpdateTabIndicatorPosition(SelectedIndex); - return; - } - - if (tabStripContent == null || TabItems.Count == 0) - return; - - if (contentWidthCollection.Count == 0) - UpdateItemsSource(contentContainer.ItemsSource); - - var offset = args.HorizontalOffset; - var toRight = args.HorizontalDelta > 0; - - var nextIndex = toRight ? contentWidthCollection.FindIndex(c => c > offset) : contentWidthCollection.FindLastIndex(c => c < offset); - var previousIndex = toRight ? nextIndex - 1 : nextIndex + 1; - - if (previousIndex < 0 || nextIndex < 0) - return; - - var itemsCount = TabItems.Count; - - if (previousIndex >= 0 && previousIndex < itemsCount) - { - var currentTabViewItem = TabItems[previousIndex]; - var currentTabViewItemWidth = currentTabViewItem.Width; - - UpdateTabIndicatorWidth(currentTabViewItemWidth); - - var contentItemsCount = contentWidthCollection.Count; - - if (previousIndex >= 0 && previousIndex < contentItemsCount) - { - var progress = (offset - contentWidthCollection[previousIndex]) / (contentWidthCollection[nextIndex] - contentWidthCollection[previousIndex]); - var position = toRight ? currentTabViewItem.X + (currentTabViewItemWidth * progress) : currentTabViewItem.X - (currentTabViewItemWidth * progress); - - tabStripIndicator.TranslateTo(position, 0, tabIndicatorAnimationDuration, Easing.Linear); - } - } - } - - void UpdateTabIndicatorPosition(View currentTabViewItem) - { - var width = TabIndicatorWidth > 0 ? (currentTabViewItem.Width - tabStripIndicator.Width) : 0; - var position = currentTabViewItem.X + (width / 2); - tabStripIndicator.TranslateTo(position, 0, tabIndicatorAnimationDuration, Easing.Linear); - } - - internal virtual void OnTabSelectionChanged(TabSelectionChangedEventArgs e) - { - var handler = SelectionChanged; - handler?.Invoke(this, e); - } - - internal virtual void OnTabViewScrolled(ItemsViewScrolledEventArgs e) - { - var handler = Scrolled; - handler?.Invoke(this, e); + Grid.SetRow(contentContainer, 0); + Grid.SetRowSpan(contentContainer, 2); } } + else + { + Grid.SetRow(contentContainer, 0); + Grid.SetRowSpan(contentContainer, 3); + } + + if (TabStripBackgroundView != null) + { + var tabStripBackgroundViewHasCornerRadius = + (TabStripBackgroundView is IBorderElement borderElement && borderElement.CornerRadius != default) || + (TabStripBackgroundView is BoxView boxView && boxView.CornerRadius != default); + + if (tabStripBackgroundViewHasCornerRadius) + { + Grid.SetRow(contentContainer, 0); + Grid.SetRowSpan(contentContainer, 3); + } + } +} + +void UpdateTabStripBackgroundColor(Color tabStripBackgroundColor) +{ + tabStripBackground.BackgroundColor = tabStripBackgroundColor; + + if (Device.RuntimePlatform == Device.macOS) + tabStripContainerScroll.BackgroundColor = tabStripBackgroundColor; +} + +void UpdateTabStripBackgroundView(View tabStripBackgroundView) +{ + if (tabStripBackgroundView != null) + tabStripBackground.Children.Add(tabStripBackgroundView); + else + tabStripBackground.Children.Clear(); + + UpdateTabContentLayout(); +} + +void UpdateTabStripBorderColor(Color tabStripBorderColor) +{ + tabStripBorder.Color = tabStripBorderColor; + + UpdateTabIndicatorMargin(); +} + +void UpdateTabIndicatorMargin() +{ + if (TabStripBorderColor == Color.Default) + return; + + if (TabStripPlacement == TabStripPlacement.Top && TabIndicatorPlacement == TabIndicatorPlacement.Bottom) + tabStripIndicator.Margin = new Thickness(0, 0, 0, 1); + + if (TabStripPlacement == TabStripPlacement.Bottom && TabIndicatorPlacement == TabIndicatorPlacement.Top) + tabStripIndicator.Margin = new Thickness(0, 1, 0, 0); +} + +void UpdateTabContentBackgroundColor(Color tabContentBackgroundColor) => contentContainer.BackgroundColor = tabContentBackgroundColor; + +void UpdateTabStripHeight(double tabStripHeight) => tabStripBackground.HeightRequest = tabStripHeight; + +void UpdateIsTabStripVisible(bool isTabStripVisible) +{ + tabStripContainer.IsVisible = isTabStripVisible; + + UpdateTabContentLayout(); +} + +void UpdateTabContentHeight(double tabContentHeight) => contentContainer.HeightRequest = tabContentHeight; + +void UpdateTabIndicatorColor(Color tabIndicatorColor) => tabStripIndicator.BackgroundColor = tabIndicatorColor; + +void UpdateTabIndicatorHeight(double tabIndicatorHeight) => tabStripIndicator.HeightRequest = tabIndicatorHeight; + +void UpdateTabIndicatorWidth(double tabIndicatorWidth) => tabStripIndicator.WidthRequest = tabIndicatorWidth; + +void UpdateTabIndicatorView(View tabIndicatorView) +{ + if (tabIndicatorView != null) + tabStripIndicator.Children.Add(tabIndicatorView); + else + tabStripIndicator.Children.Clear(); +} + +void UpdateTabIndicatorPlacement(TabIndicatorPlacement tabIndicatorPlacement) +{ + switch (tabIndicatorPlacement) + { + case TabIndicatorPlacement.Top: + tabStripIndicator.VerticalOptions = LayoutOptions.Start; + break; + case TabIndicatorPlacement.Center: + tabStripIndicator.VerticalOptions = LayoutOptions.Center; + break; + case TabIndicatorPlacement.Bottom: + default: + tabStripIndicator.VerticalOptions = LayoutOptions.End; + break; + } + + UpdateTabIndicatorMargin(); +} + +void UpdateIsSwipeEnabled(bool isSwipeEnabled) => contentContainer.IsSwipeEnabled = isSwipeEnabled; + +void UpdateIsTabTransitionEnabled(bool isTabTransitionEnabled) => contentContainer.IsScrollAnimated = isTabTransitionEnabled; + +void UpdateTabIndicatorPosition(int tabViewItemIndex) +{ + if (tabStripContent == null || tabStripContent.Children.Count == 0 || tabViewItemIndex == -1) + return; + + var currentTabViewItem = tabStripContent.Children[tabViewItemIndex]; + + if (currentTabViewItem.Width <= 0) + currentTabViewItem.SizeChanged += OnCurrentTabItemSizeChanged; + else + UpdateTabIndicatorWidth(currentTabViewItem.Width); + + UpdateTabIndicatorPosition(currentTabViewItem); +} + +void UpdateTabIndicatorPosition(ItemsViewScrolledEventArgs args) +{ + if (args.HorizontalOffset == 0) + { + UpdateTabIndicatorPosition(SelectedIndex); + return; + } + + if (tabStripContent == null || TabItems.Count == 0) + return; + + if (contentWidthCollection.Count == 0) + UpdateItemsSource(contentContainer.ItemsSource); + + var offset = args.HorizontalOffset; + var toRight = args.HorizontalDelta > 0; + + var nextIndex = toRight ? contentWidthCollection.FindIndex(c => c > offset) : contentWidthCollection.FindLastIndex(c => c < offset); + var previousIndex = toRight ? nextIndex - 1 : nextIndex + 1; + + if (previousIndex < 0 || nextIndex < 0) + return; + + var itemsCount = TabItems.Count; + + if (previousIndex >= 0 && previousIndex < itemsCount) + { + var currentTabViewItem = TabItems[previousIndex]; + var currentTabViewItemWidth = currentTabViewItem.Width; + + UpdateTabIndicatorWidth(currentTabViewItemWidth); + + var contentItemsCount = contentWidthCollection.Count; + + if (previousIndex >= 0 && previousIndex < contentItemsCount) + { + var progress = (offset - contentWidthCollection[previousIndex]) / (contentWidthCollection[nextIndex] - contentWidthCollection[previousIndex]); + var position = toRight ? currentTabViewItem.X + (currentTabViewItemWidth * progress) : currentTabViewItem.X - (currentTabViewItemWidth * progress); + + tabStripIndicator.TranslateTo(position, 0, tabIndicatorAnimationDuration, Easing.Linear); + } + } +} + +void UpdateTabIndicatorPosition(View currentTabViewItem) +{ + var width = TabIndicatorWidth > 0 ? (currentTabViewItem.Width - tabStripIndicator.Width) : 0; + var position = currentTabViewItem.X + (width / 2); + tabStripIndicator.TranslateTo(position, 0, tabIndicatorAnimationDuration, Easing.Linear); +} + +internal virtual void OnTabSelectionChanged(TabSelectionChangedEventArgs e) +{ + var handler = SelectionChanged; + handler?.Invoke(this, e); +} + +internal virtual void OnTabViewScrolled(ItemsViewScrolledEventArgs e) +{ + var handler = Scrolled; + handler?.Invoke(this, e); +} + } } \ No newline at end of file