[NUI.Components]Improve SAM score of NUI.Components (#1713)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / DropDown.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 using Tizen.NUI.Binding;
22
23 namespace Tizen.NUI.Components
24 {
25     /// <summary>
26     /// DropDown is one kind of common component, a dropdown allows the user click dropdown button to choose one value from a list.
27     /// </summary>
28     /// <since_tizen> 6 </since_tizen>
29     /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
30     [EditorBrowsable(EditorBrowsableState.Never)]
31     public partial class DropDown : Control
32     {
33         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
34         [EditorBrowsable(EditorBrowsableState.Never)]
35         public static readonly BindableProperty ListPaddingProperty = BindableProperty.Create(nameof(ListPadding), typeof(Extents), typeof(DropDown), null, propertyChanged: (bindable, oldValue, newValue) =>
36         {
37             var instance = (DropDown)bindable;
38             if (newValue != null)
39             {
40                 instance.listPadding.CopyFrom((Extents)newValue);
41                 instance.UpdateDropDown();
42             }
43         },
44         defaultValueCreator: (bindable) =>
45         {
46             var instance = (DropDown)bindable;
47             return instance.listPadding;
48         });
49         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
50         [EditorBrowsable(EditorBrowsableState.Never)]
51         public static readonly BindableProperty SelectedItemIndexProperty = BindableProperty.Create(nameof(SelectedItemIndex), typeof(int), typeof(DropDown), 0, propertyChanged: (bindable, oldValue, newValue) =>
52         {
53             var instance = (DropDown)bindable;
54             if (newValue != null)
55             {
56                 int selectedItemIndex = (int)newValue;
57                 if (selectedItemIndex == instance.selectedItemIndex || instance.adapter == null || selectedItemIndex < 0 || selectedItemIndex >= instance.adapter.GetItemCount())
58                 {
59                     return;
60                 }
61                 instance.SetListItemToSelected((uint)selectedItemIndex);
62             }
63         },
64         defaultValueCreator: (bindable) =>
65         {
66             var instance = (DropDown)bindable;
67             return instance.selectedItemIndex;
68         });
69         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
70         [EditorBrowsable(EditorBrowsableState.Never)]
71         public static readonly BindableProperty ListMarginProperty = BindableProperty.Create(nameof(ListMargin), typeof(Extents), typeof(DropDown), null, propertyChanged: (bindable, oldValue, newValue) =>
72         {
73             var instance = (DropDown)bindable;
74             if (newValue != null)
75             {
76                 instance.listMargin.CopyFrom((Extents)newValue);
77                 instance.UpdateDropDown();
78             }
79         },
80         defaultValueCreator: (bindable) =>
81         {
82             var instance = (DropDown)bindable;
83             return instance.listMargin;
84         });
85         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
86         [EditorBrowsable(EditorBrowsableState.Never)]
87         public static readonly BindableProperty ListRelativeOrientationProperty = BindableProperty.Create(nameof(ListRelativeOrientation), typeof(ListOrientation), typeof(DropDown), ListOrientation.Left, propertyChanged: (bindable, oldValue, newValue) =>
88         {
89             var instance = (DropDown)bindable;
90             if (newValue != null)
91             {
92                 instance.listRelativeOrientation = (ListOrientation)newValue;
93                 instance.UpdateDropDown();
94             }
95         },
96         defaultValueCreator: (bindable) =>
97         {
98             var instance = (DropDown)bindable;
99             return instance.listRelativeOrientation;
100         });
101         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
102         [EditorBrowsable(EditorBrowsableState.Never)]
103         public static readonly BindableProperty SpaceBetweenButtonTextAndIconProperty = BindableProperty.Create(nameof(SpaceBetweenButtonTextAndIcon), typeof(int), typeof(DropDown), 0, propertyChanged: (bindable, oldValue, newValue) =>
104         {
105             var instance = (DropDown)bindable;
106             if (newValue != null)
107             {
108                 instance.spaceBetweenButtonTextAndIcon = (int)newValue;
109             }
110         },
111         defaultValueCreator: (bindable) =>
112         {
113             var instance = (DropDown)bindable;
114             return instance.spaceBetweenButtonTextAndIcon;
115         });
116
117         #region DropDown
118         private Button button = null;
119         private TextLabel headerText = null;
120         private TextLabel buttonText = null;
121         private ImageView listBackgroundImage = null;
122         // Component that scrolls the child added to it.
123         private ScrollableBase scrollableBase = null;
124
125         // The LinearLayout container to house the items in the drop down list.
126         private View dropDownMenuFullList = null;
127         private DropDownListBridge adapter = new DropDownListBridge();
128         private DropDownItemView selectedItemView = null;
129         private TapGestureDetector tapGestureDetector = null;
130
131         private Extents listMargin = new Extents(0, 0, 0, 0);
132         private Extents listPadding = new Extents(0, 0, 0, 0);
133         private ListOrientation listRelativeOrientation = ListOrientation.Left;
134         private int selectedItemIndex = -1;
135         private int spaceBetweenButtonTextAndIcon = 0;
136         private bool itemPressed = false;
137
138         static DropDown() { }
139
140         /// <summary>
141         /// Creates a new instance of a DropDown.
142         /// </summary>
143         /// <since_tizen> 6 </since_tizen>
144         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
145         [EditorBrowsable(EditorBrowsableState.Never)]
146         public DropDown() : base() { }
147
148         /// <summary>
149         /// Creates a new instance of a DropDown with style.
150         /// </summary>
151         /// <param name="style">Create DropDown by special style defined in UX.</param>
152         /// <since_tizen> 6 </since_tizen>
153         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
154         [EditorBrowsable(EditorBrowsableState.Never)]
155         public DropDown(string style) : base(style) { }
156
157         /// <summary>
158         /// Creates a new instance of a DropDown with style.
159         /// </summary>
160         /// <param name="dropDownStyle">Create DropDown by style customized by user.</param>
161         /// <since_tizen> 6 </since_tizen>
162         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
163         [EditorBrowsable(EditorBrowsableState.Never)]
164         public DropDown(DropDownStyle dropDownStyle) : base(dropDownStyle)
165         {
166         }
167
168         /// <summary>
169         /// An event for the button clicked signal which can be used to subscribe or unsubscribe the event handler provided by the user.<br />
170         /// </summary>
171         /// <since_tizen> 6 </since_tizen>
172         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
173         [EditorBrowsable(EditorBrowsableState.Never)]
174         public delegate void ClickEventHandler<ClickEventArgs>(object sender, ClickEventArgs e);
175
176         /// <summary>
177         /// An event for the item clicked signal which can be used to subscribe or unsubscribe the event handler provided by the user.<br />
178         /// </summary>
179         /// <since_tizen> 6 </since_tizen>
180         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
181         [EditorBrowsable(EditorBrowsableState.Never)]
182         public event ClickEventHandler<ItemClickEventArgs> ItemClickEvent;
183
184         /// <summary>
185         /// List position in relation to the main button.
186         /// </summary>
187         /// <since_tizen> 6 </since_tizen>
188         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
189         [EditorBrowsable(EditorBrowsableState.Never)]
190         public enum ListOrientation
191         {
192             /// <summary>
193             /// Left.
194             /// </summary>
195             /// <since_tizen> 6 </since_tizen>
196             /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
197             [EditorBrowsable(EditorBrowsableState.Never)]
198             Left,
199             /// <summary>
200             /// Right.
201             /// </summary>
202             /// <since_tizen> 6 </since_tizen>
203             /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
204             [EditorBrowsable(EditorBrowsableState.Never)]
205             Right,
206         }
207
208         /// <summary>
209         /// Get or set header text.
210         /// </summary>
211         /// This will be public opened in tizen_next after ACR done. Before ACR, need to be hidden as inhouse API.
212         [EditorBrowsable(EditorBrowsableState.Never)]
213         public TextLabel HeaderText
214         {
215             get
216             {
217                 if (null == headerText)
218                 {
219                     headerText = new TextLabel()
220                     {
221                         WidthResizePolicy = ResizePolicyType.UseNaturalSize,
222                         HeightResizePolicy = ResizePolicyType.UseNaturalSize,
223                         HorizontalAlignment = HorizontalAlignment.Center,
224                         VerticalAlignment = VerticalAlignment.Center,
225                         ParentOrigin = NUI.ParentOrigin.Center,
226                         PivotPoint = NUI.ParentOrigin.Center,
227                         PositionUsesPivotPoint = true,
228                         Name = "DropDownHeaderText"
229                     };
230                     Add(headerText);
231                 }
232                 return headerText;
233             }
234             internal set
235             {
236                 headerText = value;
237             }
238         }
239
240         /// <summary>
241         /// Get or set button.
242         /// </summary>
243         /// This will be public opened in tizen_next after ACR done. Before ACR, need to be hidden as inhouse API.
244         [EditorBrowsable(EditorBrowsableState.Never)]
245         public Button Button
246         {
247             get
248             {
249                 if (null == button)
250                 {
251                     button = new Button()
252                     {
253                         ParentOrigin = NUI.ParentOrigin.CenterLeft,
254                         PivotPoint = NUI.PivotPoint.CenterLeft,
255                         PositionUsesPivotPoint = true,
256                         HeightResizePolicy = ResizePolicyType.FitToChildren,
257                         IconRelativeOrientation = Button.IconOrientation.Right,
258                         Name = "DropDownButton"
259                     };
260                     button.ClickEvent += ButtonClickEvent;
261                     Add(button);
262
263                     if (null == buttonText)
264                     {
265                         buttonText = new TextLabel();
266                     }
267                 }
268                 return button;
269             }
270             internal set
271             {
272                 button = value;
273             }
274         }
275
276         /// <summary>
277         /// Get or set the background image of list.
278         /// </summary>
279         /// This will be public opened in tizen_next after ACR done. Before ACR, need to be hidden as inhouse API.
280         [EditorBrowsable(EditorBrowsableState.Never)]
281         public ImageView ListBackgroundImage
282         {
283             get
284             {
285                 if (null == listBackgroundImage)
286                 {
287                     listBackgroundImage = new ImageView()
288                     {
289                         Name = "ListBackgroundImage",
290                         PositionUsesPivotPoint = true,
291                         ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft,
292                         PivotPoint = Tizen.NUI.PivotPoint.TopLeft,
293                         WidthResizePolicy = ResizePolicyType.FitToChildren,
294                         HeightResizePolicy = ResizePolicyType.FitToChildren,
295                     };
296                     Add(listBackgroundImage);
297
298                     if (null == scrollableBase) // scrollableBase used to test of ListContainer Setup invoked already
299                     {
300                         SetUpListContainer();
301                     }
302                 }
303                 return listBackgroundImage;
304             }
305             internal set
306             {
307                 listBackgroundImage = value;
308             }
309         }
310
311         /// <summary>
312         /// Return a copied Style instance of DropDown
313         /// </summary>
314         /// <remarks>
315         /// It returns copied Style instance and changing it does not effect to the DropDown.
316         /// Style setting is possible by using constructor or the function of ApplyStyle(ViewStyle viewStyle)
317         /// </remarks>
318         /// This will be public opened in tizen_next after ACR done. Before ACR, need to be hidden as inhouse API.
319         [EditorBrowsable(EditorBrowsableState.Never)]
320         //public new DropDownStyle Style
321         //{
322         //    get
323         //    {
324         //        return new DropDownStyle(ViewStyle as DropDownStyle);
325         //    }
326         //}
327         public new DropDownStyle Style => ViewStyle as DropDownStyle;
328
329         /// <summary>
330         /// Space between button text and button icon in DropDown.
331         /// </summary>
332         /// <since_tizen> 6 </since_tizen>
333         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
334         public int SpaceBetweenButtonTextAndIcon
335         {
336             get => (int)GetValue(SpaceBetweenButtonTextAndIconProperty);
337             set => SetValue(SpaceBetweenButtonTextAndIconProperty, value);
338         }
339
340         /// <summary>
341         /// List relative orientation in DropDown.
342         /// </summary>
343         /// <since_tizen> 6 </since_tizen>
344         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
345         public ListOrientation ListRelativeOrientation
346         {
347             get => (ListOrientation)GetValue(ListRelativeOrientationProperty);
348             set => SetValue(ListRelativeOrientationProperty, value);
349         }
350
351         /// <summary>
352         /// Space in list.
353         /// </summary>
354         /// <since_tizen> 6 </since_tizen>
355         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
356         public Extents ListMargin
357         {
358             get
359             {
360                 Extents tmp = (Extents)GetValue(ListMarginProperty);
361                 return new Extents((ushort start, ushort end, ushort top, ushort bottom) => { ListMargin = new Extents(start, end, top, bottom); }, tmp.Start, tmp.End, tmp.Top, tmp.Bottom);
362             }
363             set => SetValue(ListMarginProperty, value);
364         }
365
366         /// <summary>
367         /// Selected item index in list.
368         /// </summary>
369         /// <since_tizen> 6 </since_tizen>
370         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
371         public int SelectedItemIndex
372         {
373             get => (int)GetValue(SelectedItemIndexProperty);
374             set => SetValue(SelectedItemIndexProperty, value);
375         }
376
377         /// <summary>
378         /// List padding in DropDown.
379         /// </summary>
380         /// <since_tizen> 6 </since_tizen>
381         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
382         public Extents ListPadding
383         {
384             get
385             {
386                 Extents tmp = (Extents)GetValue(ListPaddingProperty);
387                 return new Extents((ushort start, ushort end, ushort top, ushort bottom) => { ListPadding = new Extents(start, end, top, bottom); }, tmp.Start, tmp.End, tmp.Top, tmp.Bottom);
388             }
389             set => SetValue(ListPaddingProperty, value);
390         }
391
392         /// <summary>
393         /// Add list item by item data. The added item will be added to end of all items automatically.
394         /// </summary>
395         /// <param name="itemData">Item data which will apply to tab item view.</param>
396         /// <since_tizen> 6 </since_tizen>
397         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
398         [EditorBrowsable(EditorBrowsableState.Never)]
399         public void AddItem(DropDownDataItem itemData)
400         {
401            // Add item to adaptor, will be added to list via AddItemAt during OnUpdate()
402            int insertionPosition = adapter.GetItemCount();
403            adapter.InsertData(insertionPosition, itemData);
404         }
405
406         /// <summary>
407         /// Delete list item by index.
408         /// </summary>
409         /// <param name="index">Position index where will be deleted.</param>
410         /// <since_tizen> 6 </since_tizen>
411         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
412         [EditorBrowsable(EditorBrowsableState.Never)]
413         public void DeleteItem(int index)
414         {
415             if (index < 0 || index >= adapter?.GetItemCount()) return;
416             if (null == dropDownMenuFullList) return;
417
418             if (selectedItemIndex == index)
419             {
420                 selectedItemIndex = -1;
421             }
422             else if(selectedItemIndex > index)
423             {
424                 selectedItemIndex--;
425             }
426
427             adapter?.RemoveData(index);
428
429             if(index < dropDownMenuFullList.ChildCount)
430             {
431                 View childToRemove = dropDownMenuFullList.GetChildAt((uint)index);
432                 if (childToRemove)
433                 {
434                     childToRemove.TouchEvent -= ListItemTouchEvent;
435                     dropDownMenuFullList.Remove(childToRemove);
436                     dropDownMenuFullList?.Layout?.RequestLayout();
437                 }
438             }
439         }
440
441         /// <summary>
442         /// Insert list item by item data. The inserted item will be added to the special position by index automatically.
443         /// </summary>
444         /// <param name="item">Item data which will apply to tab item view.</param>
445         /// <param name="index">Position index where will be inserted.</param>
446         /// <since_tizen> 6 </since_tizen>
447         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
448         [EditorBrowsable(EditorBrowsableState.Never)]
449         public void InsertItem(DropDownDataItem item, int index)
450         {
451             if (index < 0 || index >= adapter.GetItemCount())
452             {
453                 return;
454             }
455
456             if (selectedItemIndex >= index)
457             {
458                 selectedItemIndex++;
459             }
460
461             adapter.InsertData(index, item);
462         }
463
464         /// <summary>
465         /// Add scroll bar to list.
466         /// </summary>
467         /// <param name="scrollBar">Scroll bar defined by user which will be added to list.</param>
468         /// <since_tizen> 6 </since_tizen>
469         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
470         [EditorBrowsable(EditorBrowsableState.Never)]
471         public void AttachScrollBar(ScrollBar scrollBar)
472         {
473             if (scrollableBase == null)
474             {
475                 return;
476             }
477             Tizen.Log.Error("DropDown","Feature unsupported");
478         }
479
480         /// <summary>
481         /// Detach scroll bar to list.
482         /// </summary>
483         /// <since_tizen> 6 </since_tizen>
484         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
485         [EditorBrowsable(EditorBrowsableState.Never)]
486         public void DetachScrollBar()
487         {
488             if (scrollableBase == null)
489             {
490                 return;
491             }
492             Tizen.Log.Error("DropDown","Feature unsupported");
493         }
494
495         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
496         [EditorBrowsable(EditorBrowsableState.Never)]
497         public override void ApplyStyle(ViewStyle viewStyle)
498         {
499             base.ApplyStyle(viewStyle);
500
501             DropDownStyle dropDownStyle = viewStyle as DropDownStyle;
502             if (null != dropDownStyle)
503             {
504                 if (null != dropDownStyle.Button)
505                 {
506                     Button.ApplyStyle(dropDownStyle.Button);
507                 }
508                 if (null != dropDownStyle.HeaderText)
509                 {
510                     HeaderText.ApplyStyle(dropDownStyle.HeaderText);
511                 }
512                 if (null != dropDownStyle.ListBackgroundImage)
513                 {
514                     ListBackgroundImage.ApplyStyle(dropDownStyle.ListBackgroundImage);
515                 }
516                 UpdateDropDown();
517             }
518         }
519
520         /// <summary>
521         /// Update DropDown by style.
522         /// </summary>
523         /// <since_tizen> 6 </since_tizen>
524         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
525         [EditorBrowsable(EditorBrowsableState.Never)]
526         protected void UpdateDropDown()
527         {
528             if (null == scrollableBase || null == listBackgroundImage || null == dropDownMenuFullList) return;
529             if (null == Style.ListBackgroundImage.Size) return;
530             // Resize and position scrolling list within the drop down list container.  Can be used to position list in relation to the background image.
531             scrollableBase.Size = Style.ListBackgroundImage.Size - new Size((listPadding.Start + listPadding.End), (listPadding.Top + listPadding.Bottom), 0);
532             scrollableBase.Position2D = new Position2D(listPadding.Start, listPadding.Top);
533
534             int listBackgroundImageX = 0;
535             int listBackgroundImageY = 0;
536             if (listRelativeOrientation == ListOrientation.Left)
537             {
538                 listBackgroundImageX = (int)listMargin.Start;
539                 listBackgroundImageY = (int)listMargin.Top;
540             }
541             else if (listRelativeOrientation == ListOrientation.Right)
542             {
543                 listBackgroundImageX = -(int)listMargin.End;
544                 listBackgroundImageY = (int)listMargin.Top;
545             }
546             listBackgroundImage.Position2D = new Position2D(listBackgroundImageX, listBackgroundImageY);
547             dropDownMenuFullList?.Layout?.RequestLayout();
548         }
549
550         /// <summary>
551         /// update.
552         /// </summary>
553         protected override void OnUpdate()
554         {
555             float buttonTextWidth = 0;
556             if (null != buttonText)
557             {
558                 buttonText.Text = Style.Button.Text.Text.All;
559                 buttonText.PointSize = Style.Button.Text.PointSize?.GetValue(ControlState) ?? StyleManager.PointSizeNormal;
560                 buttonTextWidth = buttonText.NaturalSize.Width;
561             }
562             float fitWidth = (Style.Button.Icon.Size?.Width ?? 48) + Style.SpaceBetweenButtonTextAndIcon + buttonTextWidth;
563             fitWidth += (button.IconPadding.Start + button.IconPadding.End);
564             button.Size.Width = Math.Max(button.Size.Width, fitWidth);
565             RelayoutRequest();
566
567             int numberOfItemsToAdd = adapter.GetItemCount();
568
569             if (adapter.AdapterPurge == true)
570             {
571                 adapter.AdapterPurge = false;
572                 for (int i = 0; i < numberOfItemsToAdd; i++)
573                 {
574                     AddItemAt(adapter.GetData(i), i);
575                 }
576             }
577             // Set selection icon on View
578             if (selectedItemIndex > 0)
579             {
580                 SetListItemToSelected((uint)selectedItemIndex, selectedItemView);
581             }
582         }
583
584         /// <summary>
585         /// Dispose DropDown and all children on it.
586         /// </summary>
587         /// <param name="type">Dispose type.</param>
588         /// <since_tizen> 6 </since_tizen>
589         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
590         [EditorBrowsable(EditorBrowsableState.Never)]
591         protected override void Dispose(DisposeTypes type)
592         {
593             if (disposed)
594             {
595                 return;
596             }
597
598             if (type == DisposeTypes.Explicit)
599             {
600                 Utility.Dispose(headerText);
601                 Utility.Dispose(buttonText);
602                 Utility.Dispose(button);
603                 Utility.Dispose(scrollableBase);
604                 Utility.Dispose(dropDownMenuFullList);
605                 Utility.Dispose(listBackgroundImage);
606             }
607
608             base.Dispose(type);
609         }
610
611         /// <summary>
612         /// Get DropDown style.
613         /// </summary>
614         /// <returns>The default dropdown style.</returns>
615         /// <since_tizen> 6 </since_tizen>
616         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
617         [EditorBrowsable(EditorBrowsableState.Never)]
618         protected override ViewStyle CreateViewStyle()
619         {
620             return new DropDownStyle();
621         }
622
623         private void AddItemAt(DropDownDataItem itemData,int index)
624         {
625             ViewHolder viewHolder = adapter.OnCreateViewHolder();
626             if (!viewHolder.IsBound)
627             {
628                 adapter.BindViewHolder(viewHolder, index);
629                 viewHolder.IsBound = true;
630             }
631
632             if (tapGestureDetector == null)
633             {
634                 tapGestureDetector = new TapGestureDetector();
635             }
636             View view = viewHolder.ItemView;
637             view.ApplyStyle(itemData.itemDataStyle);
638             view.TouchEvent += ListItemTouchEvent;
639             dropDownMenuFullList.Add(view);
640         }
641
642         private void OnClickEvent(object sender, ItemClickEventArgs e)
643         {
644             ItemClickEvent?.Invoke(sender, e);
645         }
646
647         private void CreateButtonText()
648         {
649             if (null == buttonText)
650             {
651                 buttonText = new TextLabel();
652             }
653         }
654
655         private void CreateButton()
656         {
657             if (null == button)
658             {
659                 button = new Button()
660                 {
661                     ParentOrigin = NUI.ParentOrigin.CenterLeft,
662                     PivotPoint = NUI.PivotPoint.CenterLeft,
663                     PositionUsesPivotPoint = true,
664                     HeightResizePolicy = ResizePolicyType.FitToChildren,
665                     IconRelativeOrientation = Button.IconOrientation.Right,
666                 };
667                 button.Name = "DropDownButton";
668                 button.ClickEvent += ButtonClickEvent;
669                 Add(button);
670             }
671         }
672
673         private void SetUpListContainer()
674         {
675             LinearLayout linear = new LinearLayout()
676             {
677                 LinearOrientation = LinearLayout.Orientation.Vertical,
678             };
679
680             dropDownMenuFullList = new View()
681             {
682                 Layout = linear,
683                 Name = "DropDownMenuList",
684                 WidthSpecification = LayoutParamPolicies.MatchParent,
685                 HeightSpecification = LayoutParamPolicies.WrapContent,
686                 Focusable = true,
687             };
688
689             scrollableBase = new ScrollableBase()
690             {
691                 Name = "Scrollable",
692             };
693             scrollableBase.Add(dropDownMenuFullList);
694
695             listBackgroundImage.Add(scrollableBase);
696             listBackgroundImage.Hide();
697         }
698
699         private View GetViewFromIndex(uint index)
700         {
701             if ((index < dropDownMenuFullList.ChildCount) && (index >=0) )
702             {
703                 return dropDownMenuFullList.GetChildAt(index);
704             }
705             else
706             {
707                 return null;
708             }
709         }
710
711         private void SetListItemToSelected(DropDownItemView view)
712         {
713             if (dropDownMenuFullList == null || view == null || view == selectedItemView)
714             {
715                 return;
716             }
717
718             uint newSelectedIndex = 0;
719             for (; newSelectedIndex < dropDownMenuFullList.ChildCount; newSelectedIndex++)
720             {
721                 var itemView = dropDownMenuFullList.GetChildAt(newSelectedIndex) as DropDownItemView;
722                 if (itemView == view)
723                 {
724                     SetListItemToSelected(newSelectedIndex, view);
725                     return;
726                 }
727             }
728         }
729
730         private void SetListItemToSelected(uint index)
731         {
732             if (dropDownMenuFullList == null || index == selectedItemIndex)
733             {
734                 return;
735             }
736
737             SetListItemToSelected(index, GetViewFromIndex(index) as DropDownItemView);
738         }
739
740         private void SetListItemToSelected(uint index, DropDownItemView view)
741         {
742             if (adapter == null)
743             {
744                 return;
745             }
746
747             if (selectedItemView != null)
748             {
749                 selectedItemView.IsSelected = false;
750                 selectedItemView.ControlState = ControlStates.Normal;
751                 adapter.GetData(selectedItemIndex).IsSelected = false;
752             }
753
754             if (view == null || index >= dropDownMenuFullList.ChildCount)
755             {
756                 selectedItemIndex = -1;
757                 selectedItemView = null;
758                 return;
759             }
760
761             selectedItemIndex = (int)index;
762             selectedItemView = view;
763             selectedItemView.ControlState = ControlStates.Selected;
764             selectedItemView.IsSelected = true;
765             adapter.GetData(selectedItemIndex).IsSelected = true;
766             dropDownMenuFullList.Layout?.RequestLayout();
767         }
768
769         private bool ListItemTouchEvent(object sender, TouchEventArgs e)
770         {
771             PointStateType state = e.Touch.GetState(0);
772             DropDownItemView touchedView = sender as DropDownItemView;;
773             switch (state)
774             {
775                 case PointStateType.Down:
776                     if (touchedView != null)
777                     {
778                         touchedView.ControlState = ControlStates.Pressed;
779                     }
780                     itemPressed = true;  // if matched with a Up then a click event.
781                     break;
782                 case PointStateType.Motion:
783                     if (touchedView != null)
784                     {
785                         touchedView.ControlState = ControlStates.Normal;
786                     }
787                     itemPressed = false;
788                     break;
789                 case PointStateType.Up:
790                     if (touchedView != null)
791                     {
792                         if (itemPressed)  // if Down was previously sent without motion (Scrolling) in-between then a clicked event occurred.
793                         {
794                             // List item clicked
795                             Console.WriteLine("Tapped{0}", touchedView.Name);
796                             SetListItemToSelected(touchedView);
797                             button.Text = touchedView.Text;
798                             button.Show();
799                             listBackgroundImage.Hide();
800                         }
801                     }
802                     break;
803                 default:
804                     break;
805             }
806             return true;
807         }
808
809         private void ButtonClickEvent(object sender, Button.ClickEventArgs e)
810         {
811             button.Hide();
812             listBackgroundImage.Show();
813             dropDownMenuFullList?.Layout?.RequestLayout();
814             listBackgroundImage.RaiseToTop();
815         }
816
817         #endregion
818
819         #region ItemClickEventArgs
820         /// <summary>
821         /// ItemClickEventArgs is a class to record item click event arguments which will sent to user.
822         /// </summary>
823         /// <since_tizen> 6 </since_tizen>
824         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
825         [EditorBrowsable(EditorBrowsableState.Never)]
826         public class ItemClickEventArgs : EventArgs
827         {
828             /// <summary> Clicked item index of DropDown's list </summary>
829             /// <since_tizen> 6 </since_tizen>
830             /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
831             [EditorBrowsable(EditorBrowsableState.Never)]
832             public int Index { get; set; }
833             /// <summary> Clicked item text string of DropDown's list </summary>
834             /// <since_tizen> 6 </since_tizen>
835             /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
836             [EditorBrowsable(EditorBrowsableState.Never)]
837             public string Text { get; set; }
838         }
839         #endregion
840
841         #region ViewHolder
842
843         /// <summary>
844         /// A ViewHolder is a class that holds a View created from DropDownListBridge data.
845         /// </summary>
846         /// <since_tizen> 6 </since_tizen>
847         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
848         [EditorBrowsable(EditorBrowsableState.Never)]
849         public class ViewHolder
850         {
851             /// <summary>
852             /// ViewHolder constructor.
853             /// </summary>
854             /// <param name="itemView">View</param>
855             /// <since_tizen> 6 </since_tizen>
856             /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
857             [EditorBrowsable(EditorBrowsableState.Never)]
858             public ViewHolder(View itemView)
859             {
860                 if (itemView == null)
861                 {
862                     throw new ArgumentNullException("itemView may not be null");
863                 }
864                 this.ItemView = itemView;
865             }
866
867             /// <summary>
868             /// Returns the view.
869             /// </summary>
870             /// <since_tizen> 6 </since_tizen>
871             /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
872             [EditorBrowsable(EditorBrowsableState.Never)]
873             public View ItemView { get; }
874
875             internal bool IsBound { get; set; }
876         }
877
878         #endregion
879     }
880 }