[NUI] Refactoring Theme and StyleManager (#1910)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Tab.cs
1 /*
2  * Copyright(c) 2019 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17 using System;
18 using System.Collections.Generic;
19 using Tizen.NUI.BaseComponents;
20 using System.ComponentModel;
21
22 namespace Tizen.NUI.Components
23 {
24     /// <summary>
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.
27     /// </summary>
28     /// <since_tizen> 6 </since_tizen>
29     [Obsolete("Deprecated in API8; Will be removed in API10")]
30     public class Tab : Control
31     {
32         private const int aniTime = 100; // will be defined in const file later
33         private List<TabItem> itemList = new List<TabItem>();
34         private int curIndex = 0;
35         private View underline = null;
36         private Animation underlineAni = null;
37         private bool isNeedAnimation = false;
38         private Extents space;
39         private TabStyle tabStyle => ViewStyle as TabStyle;
40
41         static Tab() { }
42
43         /// <summary>
44         /// Creates a new instance of a Tab.
45         /// </summary>
46         /// <since_tizen> 6 </since_tizen>
47         [Obsolete("Deprecated in API8; Will be removed in API10")]
48         public Tab() : base()
49         {
50             Initialize();
51         }
52
53         /// <summary>
54         /// Creates a new instance of a Tab with style.
55         /// </summary>
56         /// <param name="style">Create Tab by special style defined in UX.</param>
57         [EditorBrowsable(EditorBrowsableState.Never)]
58         public Tab(string style) : base(style)
59         {
60             Initialize();
61         }
62
63         /// <summary>
64         /// Creates a new instance of a Tab with style.
65         /// </summary>
66         /// <param name="tabStyle">Create Tab by style customized by user.</param>
67         [EditorBrowsable(EditorBrowsableState.Never)]
68         public Tab(TabStyle tabStyle) : base(tabStyle)
69         {
70             Initialize();
71         }
72
73         /// <summary>
74         /// An event for the item changed signal which can be used to subscribe or unsubscribe the event handler provided by the user.<br />
75         /// </summary>
76         /// <since_tizen> 6 </since_tizen>
77         [Obsolete("Deprecated in API8; Will be removed in API10")]
78         public event EventHandler<ItemChangedEventArgs> ItemChangedEvent;
79
80         /// <summary>
81         /// Return a copied Style instance of Tab
82         /// </summary>
83         /// <remarks>
84         /// It returns copied Style instance and changing it does not effect to the Tab.
85         /// Style setting is possible by using constructor or the function of ApplyStyle(ViewStyle viewStyle)
86         /// </remarks>
87         /// <since_tizen> 8 </since_tizen>
88         public new TabStyle Style
89         {
90             get
91             {
92                 var result = new TabStyle(tabStyle);
93                 result.CopyPropertiesFromView(this);
94                 result.UnderLine.CopyPropertiesFromView(underline);
95                 return result;
96             }
97         }
98
99         [EditorBrowsable(EditorBrowsableState.Never)]
100         public View Underline
101         {
102             get
103             {
104                 if (null == underline)
105                 {
106                     underline = new View()
107                     {
108                         PositionUsesPivotPoint = true,
109                         ParentOrigin = Tizen.NUI.ParentOrigin.BottomLeft,
110                         PivotPoint = Tizen.NUI.PivotPoint.BottomLeft,
111                     };
112                     Add(underline);
113                 }
114                 return underline;
115             }
116             internal set
117             {
118                 underline = value;
119             }
120         }
121
122         /// <summary>
123         /// Selected item's index in Tab.
124         /// </summary>
125         /// <since_tizen> 6 </since_tizen>
126         [Obsolete("Deprecated in API8; Will be removed in API10")]
127         public int SelectedItemIndex
128         {
129             get
130             {
131                 return curIndex;
132             }
133             set
134             {
135                 if (value < itemList.Count)
136                 {
137                     UpdateSelectedItem(itemList[value]);
138                 }
139             }
140         }
141
142         /// <summary>
143         /// Flag to decide if TabItem is adjusted by text's natural width.
144         /// 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.
145         /// </summary>
146         /// <since_tizen> 6 </since_tizen>
147         [Obsolete("Deprecated in API8; Will be removed in API10")]
148         public bool UseTextNaturalSize
149         {
150             get
151             {
152                 return tabStyle?.UseTextNaturalSize ?? false;
153             }
154             set
155             {
156                 if (null != tabStyle)
157                 {
158                     tabStyle.UseTextNaturalSize = value;
159                     RelayoutRequest();
160                 }
161             }
162         }
163
164         /// <summary>
165         /// Gap between items.
166         /// </summary>
167         /// <since_tizen> 6 </since_tizen>
168         [Obsolete("Deprecated in API8; Will be removed in API10")]
169         public int ItemSpace
170         {
171             get
172             {
173                 return tabStyle?.ItemSpace ?? 0;
174             }
175             set
176             {
177                 if (null != tabStyle)
178                 {
179                     tabStyle.ItemSpace = value;
180                     RelayoutRequest();
181                 }
182             }
183         }
184
185         /// <summary>
186         /// Space in Tab. Sequence as Left, Right, Top, Bottom
187         /// </summary>
188         /// <since_tizen> 6 </since_tizen>
189         [Obsolete("Deprecated in API8; Will be removed in API10")]
190         public Extents Space
191         {
192             get
193             {
194                 return ItemPadding;
195             }
196             set
197             {
198                 ItemPadding = value;
199             }
200         }
201
202         /// <summary>
203         /// Item paddings in Tab. Sequence as Left, Right, Top, Bottom
204         /// </summary>
205         /// <since_tizen> 6 </since_tizen>
206         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
207         [EditorBrowsable(EditorBrowsableState.Never)]
208         public Extents ItemPadding
209         {
210             get
211             {
212                 return space;
213             }
214             set
215             {
216                 if(null != value && null != tabStyle?.ItemPadding)
217                 {
218                     tabStyle.ItemPadding.CopyFrom(value);
219
220                     if (null == space)
221                     {
222                         space = new Extents((ushort start, ushort end, ushort top, ushort bottom) =>
223                         {
224                             tabStyle.ItemPadding.Start = start;
225                             tabStyle.ItemPadding.End = end;
226                             tabStyle.ItemPadding.Top = top;
227                             tabStyle.ItemPadding.Bottom = bottom;
228                             RelayoutRequest();
229                         }, value.Start, value.End, value.Top, value.Bottom);
230                     }
231                     else
232                     {
233                         space.CopyFrom(value);
234                     }
235
236                     RelayoutRequest();
237                 }
238             }
239         }
240
241         /// <summary>
242         /// UnderLine view's size in Tab.
243         /// </summary>
244         /// <since_tizen> 6 </since_tizen>
245         [Obsolete("Deprecated in API8; Will be removed in API10")]
246         public Size UnderLineSize
247         {
248             get
249             {
250                 return Underline.Size;
251             }
252             set
253             {
254                 if (null != tabStyle?.UnderLine)
255                 {
256                     tabStyle.UnderLine.Size = value;
257                 }
258                 Underline.Size = value;
259             }
260         }
261
262         /// <summary>
263         /// UnderLine view's background in Tab.
264         /// </summary>
265         /// <since_tizen> 6 </since_tizen>
266         [Obsolete("Deprecated in API8; Will be removed in API10")]
267         public Color UnderLineBackgroundColor
268         {
269             get
270             {
271                 return Underline.BackgroundColor;
272             }
273             set
274             {
275                 if (null != tabStyle?.BackgroundColor)
276                 {
277                     tabStyle.UnderLine.BackgroundColor = value;
278                 }
279                 Underline.BackgroundColor = value;
280             }
281         }
282
283         /// <summary>
284         /// Text point size in Tab.
285         /// </summary>
286         /// <since_tizen> 6 </since_tizen>
287         [Obsolete("Deprecated in API8; Will be removed in API10")]
288         public float PointSize
289         {
290             get
291             {
292                 return tabStyle?.Text?.PointSize?.All ?? 0;
293             }
294             set
295             {
296                 if (null != tabStyle?.Text)
297                 {
298                     tabStyle.Text.PointSize = value;
299                     RelayoutRequest();
300                 }
301             }
302         }
303
304         /// <summary>
305         /// Text font family in Tab.
306         /// </summary>
307         /// <since_tizen> 6 </since_tizen>
308         [Obsolete("Deprecated in API8; Will be removed in API10")]
309         public string FontFamily
310         {
311             get
312             {
313                 return tabStyle?.Text?.FontFamily?.All;
314             }
315             set
316             {
317                 if (null != tabStyle?.Text)
318                 {
319                     tabStyle.Text.FontFamily = value;
320                                         RelayoutRequest();
321                 }
322             }
323         }
324
325         /// <summary>
326         /// Text color in Tab.
327         /// </summary>
328         /// <since_tizen> 6 </since_tizen>
329         [Obsolete("Deprecated in API8; Will be removed in API10")]
330         public Color TextColor
331         {
332             get
333             {
334                 return tabStyle?.Text?.TextColor?.All;
335             }
336             set
337             {
338                 if (null != tabStyle?.Text)
339                 {
340                     tabStyle.Text.TextColor = value;
341                                         RelayoutRequest();
342                 }
343             }
344         }
345
346         private ColorSelector textColorSelector = new ColorSelector();
347         /// <summary>
348         /// Text color selector in Tab.
349         /// </summary>
350         /// <since_tizen> 6 </since_tizen>
351         [Obsolete("Deprecated in API8; Will be removed in API10")]
352         public ColorSelector TextColorSelector
353         {
354             get
355             {
356                 return textColorSelector;
357             }
358             set
359             {
360                 if (value == null || textColorSelector == null)
361                 {
362                     Tizen.Log.Fatal("NUI", "[Exception] Tab.TextColorSelector is null");
363                     throw new NullReferenceException("Tab.TextColorSelector is null");
364                 }
365                 else
366                 {
367                     textColorSelector.Clone(value);
368                 }
369             }
370         }
371
372         /// <summary>
373         /// Add tab item by item data. The added item will be added to end of all items automatically.
374         /// </summary>
375         /// <param name="itemData">Item data which will apply to tab item view.</param>
376         /// <since_tizen> 6 </since_tizen>
377         [Obsolete("Deprecated in API8; Will be removed in API10")]
378         public void AddItem(TabItemData itemData)
379         {
380             AddItemByIndex(itemData, itemList.Count);
381         }
382
383         /// <summary>
384         /// Insert tab item by item data. The inserted item will be added to the special position by index automatically.
385         /// </summary>
386         /// <param name="itemData">Item data which will apply to tab item view.</param>
387         /// <param name="index">Position index where will be inserted.</param>
388         /// <since_tizen> 6 </since_tizen>
389         [Obsolete("Deprecated in API8; Will be removed in API10")]
390         public void InsertItem(TabItemData itemData, int index)
391         {
392             AddItemByIndex(itemData, index);
393         }
394
395         /// <summary>
396         /// Delete tab item by index.
397         /// </summary>
398         /// <param name="itemIndex">Position index where will be deleted.</param>
399         /// <since_tizen> 6 </since_tizen>
400         [Obsolete("Deprecated in API8; Will be removed in API10")]
401         public void DeleteItem(int itemIndex)
402         {
403             if(itemList == null || itemIndex < 0 || itemIndex >= itemList.Count)
404             {
405                 return;
406             }
407
408             if (curIndex > itemIndex || (curIndex == itemIndex && itemIndex == itemList.Count - 1))
409             {
410                 curIndex--;
411             }
412
413             Remove(itemList[itemIndex]);
414             itemList[itemIndex].Dispose();
415             itemList.RemoveAt(itemIndex);
416
417             UpdateItems();
418         }
419
420         /// <summary>
421         /// Apply style to tab.
422         /// </summary>
423         /// <param name="viewStyle">The style to apply.</param>
424         [EditorBrowsable(EditorBrowsableState.Never)]
425         public override void ApplyStyle(ViewStyle viewStyle)
426         {
427             base.ApplyStyle(viewStyle);
428
429             TabStyle tabStyle = viewStyle as TabStyle;
430
431             if (null != tabStyle)
432             {
433                 Underline.ApplyStyle(tabStyle.UnderLine);
434                 CreateUnderLineAnimation();
435             }
436         }
437
438         /// <summary>
439         /// Dispose Tab and all children on it.
440         /// </summary>
441         /// <param name="type">Dispose type.</param>
442         /// <since_tizen> 6 </since_tizen>
443         [Obsolete("Deprecated in API8; Will be removed in API10")]
444         protected override void Dispose(DisposeTypes type)
445         {
446             if (disposed)
447             {
448                 return;
449             }
450
451             if (type == DisposeTypes.Explicit)
452             {
453                 if(underlineAni != null)
454                 {
455                     if(underlineAni.State == Animation.States.Playing)
456                     {
457                         underlineAni.Stop();
458                     }
459                     underlineAni.Dispose();
460                     underlineAni = null;
461                 }
462                 Utility.Dispose(underline);
463                 if(itemList != null)
464                 {
465                     for(int i = 0; i < itemList.Count; i++)
466                     {
467                         Remove(itemList[i]);
468                         itemList[i].Dispose();
469                         itemList[i] = null;
470                     }
471                     itemList.Clear();
472                     itemList = null;
473                 }
474             }
475
476             base.Dispose(type);
477         }
478
479         /// <summary>
480         /// Update Tab.
481         /// </summary>
482         /// <since_tizen> 6 </since_tizen>
483         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
484         [EditorBrowsable(EditorBrowsableState.Never)]
485         protected override void OnUpdate()
486         {
487             LayoutChild();
488         }
489
490         /// <summary>
491         /// Get Tab style.
492         /// </summary>
493         /// <returns>The default tab style.</returns>
494         [EditorBrowsable(EditorBrowsableState.Never)]
495         protected override ViewStyle CreateViewStyle()
496         {
497             return new TabStyle();
498         }
499
500         /// <summary>
501         /// Layout child in Tab and it can be override by user.
502         /// </summary>
503         /// <since_tizen> 6 </since_tizen>
504         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
505         [EditorBrowsable(EditorBrowsableState.Never)]
506         protected virtual void LayoutChild()
507         {
508             if (itemList == null)
509             {
510                 return;
511             }
512
513             int totalNum = itemList.Count;
514             if (totalNum == 0)
515             {
516                 return;
517             }
518
519             int preX = (int)tabStyle?.ItemPadding.Start;
520             int preW = 0;
521             int itemSpace = (null != tabStyle) ? tabStyle.ItemSpace : 0;
522
523             if (LayoutDirection == ViewLayoutDirectionType.LTR)
524             {
525                 if (tabStyle?.UseTextNaturalSize == true)
526                 {
527                     for (int i = 0; i < totalNum; i++)
528                     {
529                         preW = (itemList[i].TextItem.NaturalSize2D != null ? itemList[i].TextItem.NaturalSize2D.Width : 0);
530                         itemList[i].Position2D.X = preX;
531                         itemList[i].Size2D.Width = preW;
532                         preX = itemList[i].Position2D.X + preW + itemSpace;
533                         itemList[i].Index = i;
534                     }
535                 }
536                 else
537                 {
538                     preW = (Size2D.Width - (int)tabStyle?.ItemPadding.Start - (int)tabStyle?.ItemPadding.End) / totalNum;
539                     for (int i = 0; i < totalNum; i++)
540                     {
541                         itemList[i].Position2D.X = preX;
542                         itemList[i].Size2D.Width = preW;
543                         preX = itemList[i].Position2D.X + preW + itemSpace;
544                         itemList[i].Index = i;
545                     }
546                 }
547             }
548             else
549             {
550                 preX = (int)tabStyle?.ItemPadding.End;
551                 if (tabStyle?.UseTextNaturalSize == true)
552                 {
553                     int w = Size2D.Width;
554                     for (int i = 0; i < totalNum; i++)
555                     {
556                         preW = (itemList[i].NaturalSize2D != null ? itemList[i].NaturalSize2D.Width : 0);
557                         itemList[i].Position2D.X = w - preW - preX;
558                         itemList[i].Size2D.Width = preW;
559                         preX = w - itemList[i].Position2D.X + itemSpace;
560                         itemList[i].Index = i;
561                     }
562                 }
563                 else
564                 {
565                     preW = (Size2D.Width - (int)tabStyle?.ItemPadding.Start - (int)tabStyle?.ItemPadding.End) / totalNum;
566                     for (int i = totalNum - 1; i >= 0; i--)
567                     {
568                         itemList[i].Position2D.X = preX;
569                         itemList[i].Size2D.Width = preW;
570                         preX = itemList[i].Position2D.X + preW + itemSpace;
571                         itemList[i].Index = i;
572                     }
573                 }
574             }
575             UpdateUnderLinePos();
576         }
577
578         private void Initialize()
579         {
580             LayoutDirectionChanged += OnLayoutDirectionChanged;
581         }
582
583         private void OnLayoutDirectionChanged(object sender, LayoutDirectionChangedEventArgs e)
584         {
585             LayoutChild();
586         }
587
588         private void AddItemByIndex(TabItemData itemData, int index)
589         {
590             if (null == itemData || null == tabStyle) return;
591             int h = 0;
592             int topSpace = (int)tabStyle.ItemPadding.Top;
593             if (Underline.Size != null)
594             {
595                 h = (int)Underline.Size.Height;
596             }
597
598             Tab.TabItem item = new TabItem();
599             item.TextItem.ApplyStyle(tabStyle.Text);
600
601             item.Text = itemData.Text;
602             item.Size2D.Height = Size2D.Height - h - topSpace;
603             item.Position2D.Y = topSpace;
604             item.TouchEvent += ItemTouchEvent;
605             Add(item);
606
607             if (index >= itemList.Count)
608             {
609                 itemList.Add(item);
610             }
611             else
612             {
613                 itemList.Insert(index, item);
614             }
615
616             UpdateItems();
617         }
618
619         private void UpdateItems()
620         {
621             LayoutChild();
622             if (itemList != null && curIndex >= 0 && curIndex < itemList.Count)
623             {
624                 itemList[curIndex].IsSelected = true;
625                 UpdateUnderLinePos();
626             }
627             else
628             {
629                 if (underline != null)
630                 {
631                     underline.Hide();
632                 }
633             }
634         }
635
636         private void CreateUnderLineAnimation()
637         {
638             if (underlineAni == null)
639             {
640                 underlineAni = new Animation(aniTime);
641             }
642         }
643         
644         private void UpdateUnderLinePos()
645         {
646             if (underline == null || Underline.Size == null || itemList == null || itemList.Count <= 0)
647             {
648                 return;
649             }
650
651             Underline.Size.Width = itemList[curIndex].Size2D.Width;
652
653             underline.Size2D = new Size2D(itemList[curIndex].Size2D.Width, (int)Underline.Size.Height);
654             underline.BackgroundColor = tabStyle.UnderLine.BackgroundColor.All;
655             if (isNeedAnimation)
656             {
657                 CreateUnderLineAnimation();
658                 if (underlineAni.State == Animation.States.Playing)
659                 {
660                     underlineAni.Stop();
661                 }
662                 underlineAni.Clear();
663                 underlineAni.AnimateTo(underline, "PositionX", itemList[curIndex].Position2D.X);
664                 underlineAni.Play();
665             }
666             else
667             {
668                 underline.Position2D.X = itemList[curIndex].Position2D.X;
669                 isNeedAnimation = true;
670             }
671
672             underline.Show();
673         }
674
675         private void UpdateSelectedItem(TabItem item)
676         {
677             if(item == null || curIndex == item.Index)
678             {
679                 return;
680             }
681
682             ItemChangedEventArgs e = new ItemChangedEventArgs
683             {
684                 PreviousIndex = curIndex,
685                 CurrentIndex = item.Index
686             };
687             ItemChangedEvent?.Invoke(this, e);
688
689             itemList[curIndex].IsSelected = false;
690             curIndex = item.Index;
691             itemList[curIndex].IsSelected = true;
692
693             UpdateUnderLinePos();
694         }
695
696         private bool ItemTouchEvent(object source, TouchEventArgs e)
697         {
698             TabItem item = source as TabItem;
699             if(item == null)
700             {
701                 return false;
702             }
703             PointStateType state = e.Touch.GetState(0);
704             if (state == PointStateType.Up)
705             {
706                 UpdateSelectedItem(item);
707             }
708
709             return true;
710         }
711
712         internal class TabItem : View
713         {
714             private bool isSelected = false;
715
716             public TabItem() : base()
717             {
718                 TextItem = new TextLabel()
719                 {
720                     ParentOrigin = Tizen.NUI.ParentOrigin.Center,
721                     PivotPoint = Tizen.NUI.PivotPoint.Center,
722                     PositionUsesPivotPoint = true,
723                     WidthResizePolicy = ResizePolicyType.FillToParent,
724                     HeightResizePolicy = ResizePolicyType.FillToParent,
725                     HorizontalAlignment = HorizontalAlignment.Center,
726                     VerticalAlignment = VerticalAlignment.Center
727                 };
728                 Add(TextItem);
729
730                 EnableControlStatePropagation = true;
731             }
732
733             internal int Index
734             {
735                 get;
736                 set;
737             }
738
739             public string Text
740             {
741                 get
742                 {
743                     return TextItem.Text;
744                 }
745                 set
746                 {
747                     TextItem.Text = value;
748                 }
749             }
750
751             internal TextLabel TextItem
752             {
753                 get;
754                 set;
755             }
756
757             internal bool IsSelected
758             {
759                 get
760                 {
761                     return isSelected;
762                 }
763                 set
764                 {
765                     ControlState = value ? ControlState.Selected : ControlState.Normal;
766                     isSelected = value;
767                 }
768             }
769         }
770
771         /// <summary>
772         /// TabItemData is a class to record all data which will be applied to Tab item.
773         /// </summary>
774         /// <since_tizen> 6 </since_tizen>
775         [Obsolete("Deprecated in API8; Will be removed in API10")]
776         public class TabItemData
777         {
778             /// <summary>
779             /// Text string in tab item view.
780             /// </summary>
781             /// <since_tizen> 6 </since_tizen>
782             [Obsolete("Deprecated in API8; Will be removed in API10")]
783             public string Text
784             {
785                 get;
786                 set;
787             }
788         }
789
790         /// <summary>
791         /// ItemChangedEventArgs is a class to record item change event arguments which will sent to user.
792         /// </summary>
793         /// <since_tizen> 6 </since_tizen>
794         [Obsolete("Deprecated in API8; Will be removed in API10")]
795         public class ItemChangedEventArgs : EventArgs
796         {
797             /// <summary> Previous selected index of Tab </summary>
798             /// <since_tizen> 6 </since_tizen>
799             [Obsolete("Deprecated in API8; Will be removed in API10")]
800             public int PreviousIndex;
801             /// <summary> Current selected index of Tab </summary>
802             /// <since_tizen> 6 </since_tizen>
803             [Obsolete("Deprecated in API8; Will be removed in API10")]
804             public int CurrentIndex;
805         }
806     }
807 }