2 * Copyright(c) 2019 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 using System.Collections.Generic;
19 using Tizen.NUI.BaseComponents;
20 using System.ComponentModel;
22 namespace Tizen.NUI.Components
25 /// Tab is one kind of common component, it can be used as menu label.
26 /// User can handle Tab by adding/inserting/deleting TabItem.
28 /// <since_tizen> 6 </since_tizen>
29 public class Tab : Control
31 private const int aniTime = 100; // will be defined in const file later
32 private List<TabItem> itemList = new List<TabItem>();
33 private int curIndex = 0;
34 private View underline = null;
35 private Animation underlineAni = null;
36 private bool isNeedAnimation = false;
37 private Extents space;
41 /// Creates a new instance of a Tab.
43 /// <since_tizen> 6 </since_tizen>
50 /// Creates a new instance of a Tab with style.
52 /// <param name="style">Create Tab by special style defined in UX.</param>
53 /// <since_tizen> 8 </since_tizen>
54 public Tab(string style) : base(style)
60 /// Creates a new instance of a Tab with style.
62 /// <param name="tabStyle">Create Tab by style customized by user.</param>
63 /// <since_tizen> 8 </since_tizen>
64 public Tab(TabStyle tabStyle) : base(tabStyle)
70 /// An event for the item changed signal which can be used to subscribe or unsubscribe the event handler provided by the user.<br />
72 /// <since_tizen> 6 </since_tizen>
73 public event EventHandler<ItemChangedEventArgs> ItemChangedEvent;
78 /// <since_tizen> 8 </since_tizen>
79 public new TabStyle Style => ViewStyle as TabStyle;
81 [EditorBrowsable(EditorBrowsableState.Never)]
86 if (null == underline)
88 underline = new View()
90 PositionUsesPivotPoint = true,
91 ParentOrigin = Tizen.NUI.ParentOrigin.BottomLeft,
92 PivotPoint = Tizen.NUI.PivotPoint.BottomLeft,
105 /// Selected item's index in Tab.
107 /// <since_tizen> 6 </since_tizen>
108 public int SelectedItemIndex
116 if (value < itemList.Count)
118 UpdateSelectedItem(itemList[value]);
124 /// Flag to decide if TabItem is adjusted by text's natural width.
125 /// If true, TabItem's width will be equal as text's natural width, if false, it will be decided by Tab's width and tab item count.
127 /// <since_tizen> 6 </since_tizen>
128 public bool UseTextNaturalSize
132 return Style?.UseTextNaturalSize ?? false;
138 Style.UseTextNaturalSize = value;
145 /// Gap between items.
147 /// <since_tizen> 6 </since_tizen>
152 return Style?.ItemSpace ?? 0;
158 Style.ItemSpace = value;
165 /// Space in Tab. Sequence as Left, Right, Top, Bottom
167 /// <since_tizen> 6 </since_tizen>
181 /// Item paddings in Tab. Sequence as Left, Right, Top, Bottom
183 /// <since_tizen> 6 </since_tizen>
184 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
185 [EditorBrowsable(EditorBrowsableState.Never)]
186 public Extents ItemPadding
194 if(null != value && null != Style?.ItemPadding)
196 Style.ItemPadding.CopyFrom(value);
200 space = new Extents((ushort start, ushort end, ushort top, ushort bottom) =>
202 Style.ItemPadding.Start = start;
203 Style.ItemPadding.End = end;
204 Style.ItemPadding.Top = top;
205 Style.ItemPadding.Bottom = bottom;
207 }, value.Start, value.End, value.Top, value.Bottom);
211 space.CopyFrom(value);
220 /// UnderLine view's size in Tab.
222 /// <since_tizen> 6 </since_tizen>
223 public Size UnderLineSize
227 return Style?.UnderLine?.Size;
231 if (null != Style?.UnderLine)
233 Style.UnderLine.Size = value;
239 /// UnderLine view's background in Tab.
241 /// <since_tizen> 6 </since_tizen>
242 public Color UnderLineBackgroundColor
246 return Style?.UnderLine?.BackgroundColor?.All;
250 if (null != Style?.UnderLine)
252 Style.UnderLine.BackgroundColor = value;
258 /// Text point size in Tab.
260 /// <since_tizen> 6 </since_tizen>
261 public float PointSize
265 return Style?.Text?.PointSize?.All ?? 0;
269 if (null != Style?.Text)
271 Style.Text.PointSize = value;
277 /// Text font family in Tab.
279 /// <since_tizen> 6 </since_tizen>
280 public string FontFamily
284 return Style?.Text?.FontFamily?.All;
288 if (null != Style?.Text)
290 Style.Text.FontFamily = value;
296 /// Text color in Tab.
298 /// <since_tizen> 6 </since_tizen>
299 public Color TextColor
303 return Style?.Text?.TextColor?.All;
307 if (null != Style?.Text)
309 Style.Text.TextColor = value;
314 private ColorSelector textColorSelector = new ColorSelector();
316 /// Text color selector in Tab.
318 /// <since_tizen> 6 </since_tizen>
319 public ColorSelector TextColorSelector
323 return textColorSelector;
327 if (value == null || textColorSelector == null)
329 Tizen.Log.Fatal("NUI", "[Exception] Tab.TextColorSelector is null");
330 throw new NullReferenceException("Tab.TextColorSelector is null");
334 textColorSelector.Clone(value);
340 /// Add tab item by item data. The added item will be added to end of all items automatically.
342 /// <param name="itemData">Item data which will apply to tab item view.</param>
343 /// <since_tizen> 6 </since_tizen>
344 public void AddItem(TabItemData itemData)
346 AddItemByIndex(itemData, itemList.Count);
350 /// Insert tab item by item data. The inserted item will be added to the special position by index automatically.
352 /// <param name="itemData">Item data which will apply to tab item view.</param>
353 /// <param name="index">Position index where will be inserted.</param>
354 /// <since_tizen> 6 </since_tizen>
355 public void InsertItem(TabItemData itemData, int index)
357 AddItemByIndex(itemData, index);
361 /// Delete tab item by index.
363 /// <param name="itemIndex">Position index where will be deleted.</param>
364 /// <since_tizen> 6 </since_tizen>
365 public void DeleteItem(int itemIndex)
367 if(itemList == null || itemIndex < 0 || itemIndex >= itemList.Count)
372 if (curIndex > itemIndex || (curIndex == itemIndex && itemIndex == itemList.Count - 1))
377 Remove(itemList[itemIndex]);
378 itemList[itemIndex].Dispose();
379 itemList.RemoveAt(itemIndex);
385 /// Apply style to tab.
387 /// <param name="viewStyle">The style to apply.</param>
388 /// <since_tizen> 8 </since_tizen>
389 public override void ApplyStyle(ViewStyle viewStyle)
391 base.ApplyStyle(viewStyle);
393 TabStyle tabStyle = viewStyle as TabStyle;
395 if (null != tabStyle)
397 Underline.ApplyStyle(tabStyle.UnderLine);
398 CreateUnderLineAnimation();
403 /// Dispose Tab and all children on it.
405 /// <param name="type">Dispose type.</param>
406 /// <since_tizen> 6 </since_tizen>
407 protected override void Dispose(DisposeTypes type)
414 if (type == DisposeTypes.Explicit)
416 if(underlineAni != null)
418 if(underlineAni.State == Animation.States.Playing)
422 underlineAni.Dispose();
425 Utility.Dispose(underline);
428 for(int i = 0; i < itemList.Count; i++)
431 itemList[i].Dispose();
445 /// <since_tizen> 6 </since_tizen>
446 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
447 [EditorBrowsable(EditorBrowsableState.Never)]
448 protected override void OnUpdate()
456 /// <returns>The default tab style.</returns>
457 /// <since_tizen> 8 </since_tizen>
458 protected override ViewStyle CreateViewStyle()
460 return new TabStyle();
464 /// Theme change callback when theme is changed, this callback will be trigger.
466 /// <param name="sender">The sender</param>
467 /// <param name="e">The event data</param>
468 /// <since_tizen> 8 </since_tizen>
469 protected override void OnThemeChangedEvent(object sender, StyleManager.ThemeChangeEventArgs e)
471 TabStyle tabStyle = StyleManager.Instance.GetViewStyle(style) as TabStyle;
472 if (tabStyle != null)
474 Style.CopyFrom(tabStyle);
479 /// Layout child in Tab and it can be override by user.
481 /// <since_tizen> 6 </since_tizen>
482 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
483 [EditorBrowsable(EditorBrowsableState.Never)]
484 protected virtual void LayoutChild()
486 if (itemList == null)
491 int totalNum = itemList.Count;
497 int preX = (int)Style.ItemPadding.Start;
499 int itemSpace = Style.ItemSpace;
501 if (LayoutDirection == ViewLayoutDirectionType.LTR)
503 if (Style.UseTextNaturalSize == true)
505 for (int i = 0; i < totalNum; i++)
507 preW = (itemList[i].TextItem.NaturalSize2D != null ? itemList[i].TextItem.NaturalSize2D.Width : 0);
508 itemList[i].Position2D.X = preX;
509 itemList[i].Size2D.Width = preW;
510 preX = itemList[i].Position2D.X + preW + itemSpace;
511 itemList[i].Index = i;
516 preW = (Size2D.Width - (int)Style.ItemPadding.Start - (int)Style.ItemPadding.End) / totalNum;
517 for (int i = 0; i < totalNum; i++)
519 itemList[i].Position2D.X = preX;
520 itemList[i].Size2D.Width = preW;
521 preX = itemList[i].Position2D.X + preW + itemSpace;
522 itemList[i].Index = i;
528 preX = (int)Style.ItemPadding.End;
529 if (Style.UseTextNaturalSize == true)
531 int w = Size2D.Width;
532 for (int i = 0; i < totalNum; i++)
534 preW = (itemList[i].NaturalSize2D != null ? itemList[i].NaturalSize2D.Width : 0);
535 itemList[i].Position2D.X = w - preW - preX;
536 itemList[i].Size2D.Width = preW;
537 preX = w - itemList[i].Position2D.X + itemSpace;
538 itemList[i].Index = i;
543 preW = (Size2D.Width - (int)Style.ItemPadding.Start - (int)Style.ItemPadding.End) / totalNum;
544 for (int i = totalNum - 1; i >= 0; i--)
546 itemList[i].Position2D.X = preX;
547 itemList[i].Size2D.Width = preW;
548 preX = itemList[i].Position2D.X + preW + itemSpace;
549 itemList[i].Index = i;
553 UpdateUnderLinePos();
556 private void Initialize()
558 LayoutDirectionChanged += OnLayoutDirectionChanged;
561 private void OnLayoutDirectionChanged(object sender, LayoutDirectionChangedEventArgs e)
566 private void AddItemByIndex(TabItemData itemData, int index)
568 if (null == itemData) return;
570 int topSpace = (int)Style.ItemPadding.Top;
571 if (Style.UnderLine != null && Style.UnderLine.Size != null)
573 h = (int)Style.UnderLine.Size.Height;
576 Tab.TabItem item = new TabItem();
577 item.TextItem.ApplyStyle(Style.Text);
579 item.Text = itemData.Text;
580 item.Size2D.Height = Size2D.Height - h - topSpace;
581 item.Position2D.Y = topSpace;
582 item.TouchEvent += ItemTouchEvent;
585 if (index >= itemList.Count)
591 itemList.Insert(index, item);
597 private void UpdateItems()
600 if (itemList != null && curIndex >= 0 && curIndex < itemList.Count)
602 itemList[curIndex].ControlState = ControlStates.Selected;
603 UpdateUnderLinePos();
607 if (underline != null)
614 private void CreateUnderLineAnimation()
616 if (underlineAni == null)
618 underlineAni = new Animation(aniTime);
622 private void UpdateUnderLinePos()
624 if (underline == null || Style.UnderLine == null || Style.UnderLine.Size == null
625 || itemList == null || itemList.Count <= 0)
630 Style.UnderLine.Size.Width = itemList[curIndex].Size2D.Width;
632 underline.Size2D = new Size2D(itemList[curIndex].Size2D.Width, (int)Style.UnderLine.Size.Height);
633 underline.BackgroundColor = Style.UnderLine.BackgroundColor.All;
636 CreateUnderLineAnimation();
637 if (underlineAni.State == Animation.States.Playing)
641 underlineAni.Clear();
642 underlineAni.AnimateTo(underline, "PositionX", itemList[curIndex].Position2D.X);
647 underline.Position2D.X = itemList[curIndex].Position2D.X;
648 isNeedAnimation = true;
654 private void UpdateSelectedItem(TabItem item)
656 if(item == null || curIndex == item.Index)
661 ItemChangedEventArgs e = new ItemChangedEventArgs
663 PreviousIndex = curIndex,
664 CurrentIndex = item.Index
666 ItemChangedEvent?.Invoke(this, e);
668 itemList[curIndex].ControlState = ControlStates.Normal;
669 curIndex = item.Index;
670 itemList[curIndex].ControlState = ControlStates.Selected;
672 UpdateUnderLinePos();
675 private bool ItemTouchEvent(object source, TouchEventArgs e)
677 TabItem item = source as TabItem;
682 PointStateType state = e.Touch.GetState(0);
683 if (state == PointStateType.Up)
685 UpdateSelectedItem(item);
691 internal class TabItem : View
693 public TabItem() : base()
695 TextItem = new TextLabel()
697 ParentOrigin = Tizen.NUI.ParentOrigin.Center,
698 PivotPoint = Tizen.NUI.PivotPoint.Center,
699 PositionUsesPivotPoint = true,
700 WidthResizePolicy = ResizePolicyType.FillToParent,
701 HeightResizePolicy = ResizePolicyType.FillToParent,
702 HorizontalAlignment = HorizontalAlignment.Center,
703 VerticalAlignment = VerticalAlignment.Center
707 EnableControlStatePropagation = true;
720 return TextItem.Text;
724 TextItem.Text = value;
728 internal TextLabel TextItem
736 /// TabItemData is a class to record all data which will be applied to Tab item.
738 /// <since_tizen> 6 </since_tizen>
739 public class TabItemData
742 /// Text string in tab item view.
744 /// <since_tizen> 6 </since_tizen>
753 /// ItemChangedEventArgs is a class to record item change event arguments which will sent to user.
755 /// <since_tizen> 6 </since_tizen>
756 public class ItemChangedEventArgs : EventArgs
758 /// <summary> Previous selected index of Tab </summary>
759 /// <since_tizen> 6 </since_tizen>
760 public int PreviousIndex;
761 /// <summary> Current selected index of Tab </summary>
762 /// <since_tizen> 6 </since_tizen>
763 public int CurrentIndex;