[NUI] Add Menu, MenuItem, MenuItemGroup classes
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Menu.cs
1 /*
2  * Copyright(c) 2021 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
18 using System.Collections.Generic;
19 using System.ComponentModel;
20 using Tizen.NUI.BaseComponents;
21
22 namespace Tizen.NUI.Components
23 {
24     /// <summary>
25     /// Menu is a class which contains a set of MenuItems and has one of them selected.
26     /// </summary>
27     [EditorBrowsable(EditorBrowsableState.Never)]
28     public class Menu : Control
29     {
30         private ScrollableBase scrollableBase = null;
31         private IEnumerable<MenuItem> menuItems = null;
32         private MenuItemGroup menuItemGroup = null;
33         private Position2D anchorPosition = new Position2D(0, 0);
34
35         /// <summary>
36         /// Creates a new instance of Menu.
37         /// </summary>
38         [EditorBrowsable(EditorBrowsableState.Never)]
39         public Menu() : base()
40         {
41             Initialize();
42         }
43
44         /// <summary>
45         /// Dispose Menu and all children on it.
46         /// </summary>
47         /// <param name="type">Dispose type.</param>
48         [EditorBrowsable(EditorBrowsableState.Never)]
49         protected override void Dispose(DisposeTypes type)
50         {
51             if (disposed)
52             {
53                 return;
54             }
55
56             if (type == DisposeTypes.Explicit)
57             {
58                 if (menuItems != null)
59                 {
60                     foreach (MenuItem menuItem in menuItems)
61                     {
62                         Utility.Dispose(menuItem);
63                     }
64                 }
65
66                 menuItemGroup = null;
67
68                 if (anchorPosition != null)
69                 {
70                     anchorPosition.Dispose();
71                     anchorPosition = null;
72                 }
73
74                 Utility.Dispose(scrollableBase);
75             }
76
77             base.Dispose(type);
78         }
79
80         /// <summary>
81         /// Menu items in Menu.
82         /// </summary>
83         public IEnumerable<MenuItem> Items
84         {
85             get
86             {
87                 return menuItems;
88             }
89
90             set
91             {
92                 if (menuItems != null)
93                 {
94                     foreach (var oldItem in menuItems)
95                     {
96                         if (scrollableBase.Children?.Contains(oldItem) == true)
97                         {
98                             scrollableBase.Children.Remove(oldItem);
99                         }
100                     }
101                 }
102
103                 menuItems = value;
104
105                 if (menuItems == null)
106                 {
107                     return;
108                 }
109
110                 foreach (var item in menuItems)
111                 {
112                     scrollableBase.Add(item);
113                     menuItemGroup.Add(item);
114                 }
115             }
116         }
117
118         /// <summary>
119         /// Anchor position of Menu.
120         /// Menu is displayed at the anchor position.
121         /// If there is no enough space to display menu at the anchor position,
122         /// then menu is displayed at the proper position near anchor position.
123         /// </summary>
124         public Position2D AnchorPosition
125         {
126             get
127             {
128                 return anchorPosition;
129             }
130
131             set
132             {
133                 if (anchorPosition == value)
134                 {
135                     return;
136                 }
137
138                 anchorPosition = value;
139                 if (anchorPosition == null)
140                 {
141                     return;
142                 }
143
144                 CalculateSizeAndPosition();
145             }
146         }
147
148         /// <inheritdoc/>
149         public override void OnRelayout(Vector2 size, RelayoutContainer container)
150         {
151             base.OnRelayout(size, container);
152
153             CalculateSizeAndPosition();
154         }
155
156         private void Initialize()
157         {
158             Layout = new AbsoluteLayout();
159
160             WidthSpecification = LayoutParamPolicies.WrapContent;
161
162             scrollableBase = new ScrollableBase()
163             {
164                 Layout = new LinearLayout()
165                 {
166                     LinearOrientation = LinearLayout.Orientation.Vertical,
167                 },
168                 ScrollingDirection = ScrollableBase.Direction.Vertical,
169                 ScrollEnabled = true,
170                 HideScrollbar = false,
171             };
172             Add(scrollableBase);
173
174             menuItemGroup = new MenuItemGroup();
175         }
176
177         private void CalculateSizeAndPosition()
178         {
179             var parent = GetParent() as View;
180
181             if ((AnchorPosition == null) || (parent == null))
182             {
183                 return;
184             }
185
186             if (Items == null)
187             {
188                 return;
189             }
190
191             if ((Size2D.Width == 0) && (Size2D.Height == 0))
192             {
193                 return;
194             }
195
196             var parentPosition = new Position2D((int)parent.ScreenPosition.X, (int)parent.ScreenPosition.Y);
197             var parentSize = new Size2D(parent.Size2D.Width, parent.Size2D.Height);
198
199             int menuPosX = AnchorPosition.X;
200             int menuPosY = AnchorPosition.Y;
201             int menuSizeW = Size2D.Width;
202             int menuSizeH = Size2D.Height;
203
204             // Check if menu is not inside parent's boundary in x coordinate system.
205             if (AnchorPosition.X + Size2D.Width > parentPosition.X + parentSize.Width)
206             {
207                 menuPosX = parentPosition.X + parentSize.Width - Size2D.Width;
208
209                 if (menuPosX < parentPosition.X)
210                 {
211                     menuPosX = parentPosition.X;
212                     menuSizeW = parentSize.Width;
213                 }
214             }
215
216             // Check if menu is not inside parent's boundary in y coordinate system.
217             if (AnchorPosition.Y + Size2D.Height > parentPosition.Y + parentSize.Height)
218             {
219                 menuPosY = parentPosition.Y + parentSize.Height - Size2D.Height;
220
221                 if (menuPosY < parentPosition.Y)
222                 {
223                     menuPosY = parentPosition.Y;
224                     menuSizeH = parentSize.Height;
225                 }
226             }
227
228             Position2D = new Position2D(menuPosX, menuPosY);
229             Size2D = new Size2D(menuSizeW, menuSizeH);
230
231             parentPosition.Dispose();
232             parentSize.Dispose();
233         }
234     }
235 }