[NUI] FlexLayout Interop and Undefined fixes (#1134)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / Layouting / FlexLayout.cs
1 /* Copyright (c) 2019 Samsung Electronics Co., Ltd.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  *
15  */
16
17 using System;
18 using System.ComponentModel;
19 using Tizen.NUI.BaseComponents;
20 using System.Runtime.InteropServices;
21 using System.Diagnostics;
22
23 namespace Tizen.NUI
24 {
25     /// <summary>
26     /// [Draft] This class implements a flex layout.
27     /// The flex layout implementation is based on open source Facebook Yoga layout engine.
28     /// For more information about the flex layout API and how to use it please refer to https://yogalayout.com/docs/
29     /// We implement the subset of the API in the class below.
30     /// </summary>
31     public class FlexLayout : LayoutGroup,  global::System.IDisposable
32     {
33         private static bool LayoutDebugFlex = false; // Debug flag
34         float Flex{ get; set;}
35         int AlignSelf{get; set;}
36
37         private global::System.Runtime.InteropServices.HandleRef swigCPtr;
38         private bool swigCMemOwn;
39         private bool disposed;
40         private bool isDisposeQueued = false;
41
42         private IntPtr _rootFlex;  // Pointer to the unmanged flex layout class.
43
44         internal const float FlexUndefined = 10E20F; // Auto setting which is equivalent to WrapContent.
45
46         internal struct MeasuredSize
47         {
48           public MeasuredSize(float x, float y)
49           {
50             width = x;
51             height = y;
52           }
53           float width;
54           float height;
55         };
56
57         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
58         internal delegate MeasuredSize ChildMeasureCallback( global::System.IntPtr child, float width, int measureModeWidth, float height, int measureModeHeight );
59
60         event ChildMeasureCallback measureChildDelegate; // Stores a delegate to the child measure callback. Used for all children of this FlexLayout.
61
62         internal FlexLayout(global::System.IntPtr cPtr, bool cMemoryOwn)
63         {
64             swigCMemOwn = cMemoryOwn;
65             swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
66             _rootFlex = Interop.FlexLayout.FlexLayout_New();
67             measureChildDelegate = new ChildMeasureCallback(measureChild);
68         }
69
70         internal static global::System.Runtime.InteropServices.HandleRef getCPtr(FlexLayout obj)
71         {
72             return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr;
73         }
74
75         /// <summary>
76         /// Dispose.
77         /// </summary>
78         /// <since_tizen> 6 </since_tizen>
79         public void Dispose()
80         {
81             // Throw exception if Dispose() is called in separate thread.
82             if (!Window.IsInstalled())
83             {
84                 throw new System.InvalidOperationException("This API called from separate thread. This API must be called from MainThread.");
85             }
86
87             if (isDisposeQueued)
88             {
89                 Dispose(DisposeTypes.Implicit);
90             }
91             else
92             {
93                 Dispose(DisposeTypes.Explicit);
94                 System.GC.SuppressFinalize(this);
95             }
96         }
97
98         /// <summary>
99         /// Dispose.
100         /// </summary>
101         /// <since_tizen> 6 </since_tizen>
102         protected virtual void Dispose(DisposeTypes type)
103         {
104             if (disposed)
105             {
106                 return;
107             }
108
109             if (type == DisposeTypes.Explicit)
110             {
111                 // Called by User
112                 // Release your own managed resources here.
113                 // You should release all of your own disposable objects here.
114
115             }
116
117             // Release your own unmanaged resources here.
118             // You should not access any managed member here except static instance.
119             // because the execution order of Finalizes is non-deterministic.
120             if (swigCPtr.Handle != global::System.IntPtr.Zero)
121             {
122                 if (swigCMemOwn)
123                 {
124                     swigCMemOwn = false;
125                     Interop.FlexLayout.delete_FlexLayout(swigCPtr);
126                 }
127                 swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
128             }
129             disposed = true;
130         }
131
132         /// <summary>
133         /// [Draft] Creates a FlexLayout object.
134         /// </summary>
135         /// <since_tizen> 6 </since_tizen>
136         public FlexLayout() : this(Interop.FlexLayout.FlexLayout_New(), true)
137         {
138             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
139         }
140
141         internal static FlexLayout DownCast(BaseHandle handle)
142         {
143             FlexLayout ret = new FlexLayout(Interop.FlexLayout.FlexLayout_DownCast(BaseHandle.getCPtr(handle)), true);
144             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
145             return ret;
146         }
147
148         internal FlexLayout(FlexLayout other) : this(Interop.FlexLayout.new_FlexLayout__SWIG_1(FlexLayout.getCPtr(other)), true)
149         {
150             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
151         }
152
153         internal FlexLayout Assign(FlexLayout other)
154         {
155             FlexLayout ret = new FlexLayout(Interop.FlexLayout.FlexLayout_Assign(swigCPtr, FlexLayout.getCPtr(other)), false);
156             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
157             return ret;
158         }
159
160         internal void SetFlexDirection(FlexLayout.FlexDirection flexDirection)
161         {
162             Interop.FlexLayout.FlexLayout_SetFlexDirection(swigCPtr, (int)flexDirection);
163             RequestLayout();
164         }
165
166         internal FlexLayout.FlexDirection GetFlexDirection()
167         {
168             FlexLayout.FlexDirection ret = (FlexLayout.FlexDirection)Interop.FlexLayout.FlexLayout_GetFlexDirection(swigCPtr);
169             return ret;
170
171         }
172
173         internal void SetFlexJustification(FlexLayout.FlexJustification flexJustification)
174         {
175             Interop.FlexLayout.FlexLayout_SetFlexJustification(swigCPtr, (int)flexJustification);
176             RequestLayout();
177         }
178
179         internal FlexLayout.FlexJustification GetFlexJustification()
180         {
181             FlexLayout.FlexJustification ret = (FlexLayout.FlexJustification)Interop.FlexLayout.FlexLayout_GetFlexJustification(swigCPtr);
182             return ret;
183         }
184
185         internal void SetFlexWrap(FlexLayout.FlexWrapType flexWrap)
186         {
187             Interop.FlexLayout.FlexLayout_SetFlexWrap(swigCPtr, (int)flexWrap);
188             RequestLayout();
189         }
190
191         internal FlexLayout.FlexWrapType GetFlexWrap()
192         {
193             FlexLayout.FlexWrapType ret = (FlexLayout.FlexWrapType)Interop.FlexLayout.FlexLayout_GetFlexWrap(swigCPtr);
194             return ret;
195         }
196
197         internal void SetFlexAlignment(FlexLayout.AlignmentType flexAlignment)
198         {
199             Interop.FlexLayout.FlexLayout_SetFlexAlignment(swigCPtr, (int)flexAlignment);
200             RequestLayout();
201         }
202
203         internal FlexLayout.AlignmentType GetFlexAlignment()
204         {
205             FlexLayout.AlignmentType ret = (FlexLayout.AlignmentType)Interop.FlexLayout.FlexLayout_GetFlexAlignment(swigCPtr);
206             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
207             return ret;
208         }
209
210         internal void SetFlexItemsAlignment(FlexLayout.AlignmentType flexAlignment)
211         {
212             Interop.FlexLayout.FlexLayout_SetFlexItemsAlignment(swigCPtr, (int)flexAlignment);
213             RequestLayout();
214         }
215
216         internal FlexLayout.AlignmentType GetFlexItemsAlignment()
217         {
218             FlexLayout.AlignmentType ret = (FlexLayout.AlignmentType)Interop.FlexLayout.FlexLayout_GetFlexItemsAlignment(swigCPtr);
219             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
220             return ret;
221         }
222
223         /// <summary>
224         /// [Draft] Get/Set the flex direction in the layout.
225         /// The direction of the main-axis which determines the direction that flex items are laid out.
226         /// </summary>
227         /// <since_tizen> 6 </since_tizen>
228         public FlexDirection Direction
229         {
230             get
231             {
232                 return GetFlexDirection();
233             }
234             set
235             {
236                 SetFlexDirection(value);
237             }
238         }
239
240         /// <summary>
241         /// [Draft] Get/Set the justification in the layout.
242         /// </summary>
243         /// <since_tizen> 6 </since_tizen>
244         public FlexJustification Justification
245         {
246             get
247             {
248                 return GetFlexJustification();
249             }
250             set
251             {
252                 SetFlexJustification(value);
253             }
254         }
255
256         /// <summary>
257         /// [Draft] Get/Set the wrap in the layout.
258         /// </summary>
259         /// <since_tizen> 6 </since_tizen>
260         public FlexWrapType WrapType
261         {
262             get
263             {
264                 return GetFlexWrap();
265             }
266             set
267             {
268                 SetFlexWrap(value);
269             }
270         }
271
272         /// <summary>
273         /// [Draft] Get/Set the alignment of the layout content.
274         /// </summary>
275         /// <since_tizen> 6 </since_tizen>
276         public AlignmentType Alignment
277         {
278             get
279             {
280                 return GetFlexAlignment();
281             }
282             set
283             {
284                 SetFlexAlignment(value);
285             }
286         }
287
288         /// <summary>
289         /// [Draft] Get/Set the alignment of the layout items.
290         /// </summary>
291         /// <since_tizen> 6 </since_tizen>
292         public AlignmentType ItemsAlignment
293         {
294             get
295             {
296                 return GetFlexItemsAlignment();
297             }
298             set
299             {
300                 SetFlexItemsAlignment(value);
301             }
302         }
303
304         /// <summary>
305         /// [Draft] Enumeration for the direction of the main axis in the flex container.
306         /// This determines the direction that flex items are laid out in the flex container.
307         /// </summary>
308         /// <since_tizen> 6 </since_tizen>
309         public enum FlexDirection
310         {
311             /// <summary>
312             /// The flexible items are displayed vertically as a column
313             /// </summary>
314             Column,
315             /// <summary>
316             /// The flexible items are displayed vertically as a column, but in reverse order
317             /// </summary>
318             ColumnReverse,
319             /// <summary>
320             /// The flexible items are displayed horizontally as a row
321             /// </summary>
322             Row,
323             /// <summary>
324             /// The flexible items are displayed horizontally as a row, but in reverse order
325             /// </summary>
326             RowReverse
327         }
328
329         /// <summary>
330         /// [Draft] Enumeration for the alignment of the flex items when the items do not use all available space on the main-axis.
331         /// </summary>
332         /// <since_tizen> 6 </since_tizen>
333         public enum FlexJustification
334         {
335             /// <summary>
336             /// Items are positioned at the beginning of the container
337             /// </summary>
338             FlexStart,
339             /// <summary>
340             /// Items are positioned at the center of the container
341             /// </summary>
342             Center,
343             /// <summary>
344             /// Items are positioned at the end of the container
345             /// </summary>
346             FlexEnd,
347             /// <summary>
348             /// Items are positioned with equal space between the lines
349             /// </summary>
350             SpaceBetween,
351             /// <summary>
352             /// Items are positioned with equal space before, between, and after the lines
353             /// </summary>
354             SpaceAround
355         }
356
357         /// <summary>
358         /// [Draft] Enumeration for the wrap type of the flex container when there is no enough room for all the items on one flex line.
359         /// </summary>
360         /// <since_tizen> 6 </since_tizen>
361         public enum FlexWrapType
362         {
363             /// <summary>
364             /// Flex items laid out in single line (shrunk to fit the flex container along the main axis)
365             /// </summary>
366             NoWrap,
367             /// <summary>
368             /// Flex items laid out in multiple lines if needed
369             /// </summary>
370             Wrap
371         }
372
373         /// <summary>
374         /// [Draft] Enumeration for the alignment of the flex items or lines when the items or lines do not use all the available space on the cross-axis.
375         /// </summary>
376         /// <since_tizen> 6 </since_tizen>
377         public enum AlignmentType
378         {
379             /// <summary>
380             /// Inherits the same alignment from the parent
381             /// </summary>
382             Auto,
383             /// <summary>
384             /// At the beginning of the container
385             /// </summary>
386             FlexStart,
387             /// <summary>
388             /// At the center of the container
389             /// </summary>
390             Center,
391             /// <summary>
392             /// At the end of the container
393             /// </summary>
394             FlexEnd,
395             /// <summary>
396             /// Stretch to fit the container
397             /// </summary>
398             Stretch
399         }
400
401         private MeasuredSize measureChild(global::System.IntPtr child, float width, int measureModeWidth, float height, int measureModeHeight)
402         {
403             View view = Registry.GetManagedBaseHandleFromNativePtr(child) as View;
404             Size2D viewSize = new Size2D(8,8);
405             if(view)
406             {
407                 viewSize = view.Size2D;
408             }
409
410             // If size not set on child then use NaturalSize
411             if(viewSize.Width ==0 && viewSize.Height==0)
412             {
413                 viewSize = view.NaturalSize2D;
414             }
415
416             Debug.WriteLineIf( LayoutDebugFlex, "FlexLayout measureChild View:" + view.Name
417                                                + "Size:" + viewSize.Width
418                                                + ","
419                                                + viewSize.Height);
420
421             return new MeasuredSize(viewSize.Width,viewSize.Height);
422         }
423
424         void InsertChild( LayoutItem child )
425         {
426             // Store created node for child
427             Interop.FlexLayout.FlexLayout_AddChild(swigCPtr, View.getCPtr(child.Owner), measureChildDelegate, LayoutChildren.Count-1);
428         }
429
430         /// <summary>
431         /// Callback when child is added to container.<br />
432         /// Derived classes can use this to set their own child properties on the child layout's owner.<br />
433         /// </summary>
434         /// <param name="child">The Layout child.</param>
435         /// <since_tizen> 6 </since_tizen>
436         protected override void OnChildAdd(LayoutItem child)
437         {
438             InsertChild(child);
439         }
440
441         /// <summary>
442         /// Callback when child is removed from container.<br />
443         /// </summary>
444         /// <param name="child">The Layout child.</param>
445         /// <since_tizen> 6 </since_tizen>
446         protected override void OnChildRemove(LayoutItem child)
447         {
448             // When child View is removed from it's parent View (that is a Layout) then remove it from the layout too.
449             // FlexLayout refers to the child as a View not LayoutItem.
450             Interop.FlexLayout.FlexLayout_RemoveChild(swigCPtr, child);
451         }
452
453         /// <summary>
454         /// Measure the layout and its content to determine the measured width and the measured height.<br />
455         /// </summary>
456         /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
457         /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
458         /// <since_tizen> 6 </since_tizen>
459         protected override void OnMeasure( MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec )
460         {
461             bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL;
462             Extents padding = Owner.Padding;
463             Extents margin = Owner.Margin;
464
465             Interop.FlexLayout.FlexLayout_SetMargin(swigCPtr, Extents.getCPtr(margin));
466             Interop.FlexLayout.FlexLayout_SetPadding(swigCPtr, Extents.getCPtr(padding));
467
468             float width = FlexUndefined; // Behaves as WrapContent (Flex Auto)
469             float height = FlexUndefined; // Behaves as WrapContent (Flex Auto)
470
471             if( widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly ||  widthMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost )
472             {
473                 width = widthMeasureSpec.Size.AsDecimal();
474             }
475
476             if ( heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || heightMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost )
477             {
478                 height = heightMeasureSpec.Size.AsDecimal();
479             }
480
481             Interop.FlexLayout.FlexLayout_CalculateLayout( swigCPtr, width, height, isLayoutRtl );
482
483             LayoutLength flexLayoutWidth =  new LayoutLength( Interop.FlexLayout.FlexLayout_GetWidth(swigCPtr) );
484             LayoutLength flexLayoutHeight = new LayoutLength( Interop.FlexLayout.FlexLayout_GetHeight(swigCPtr) );
485
486             Debug.WriteLineIf( LayoutDebugFlex, "FlexLayout OnMeasure width:" + flexLayoutWidth.AsRoundedValue()
487                                                 + " height:" + flexLayoutHeight.AsRoundedValue() );
488
489             SetMeasuredDimensions( GetDefaultSize(flexLayoutWidth, widthMeasureSpec ),
490                                    GetDefaultSize(flexLayoutHeight, heightMeasureSpec ) );
491         }
492
493         /// <summary>
494         /// Assign a size and position to each of its children.<br />
495         /// </summary>
496         /// <param name="changed">This is a new size or position for this layout.</param>
497         /// <param name="left">Left position, relative to parent.</param>
498         /// <param name="top"> Top position, relative to parent.</param>
499         /// <param name="right">Right position, relative to parent.</param>
500         /// <param name="bottom">Bottom position, relative to parent.</param>
501         /// <since_tizen> 6 </since_tizen>
502         protected override void OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
503         {
504
505             bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL;
506             LayoutLength width = right - left;
507             LayoutLength height = bottom - top;
508
509             // Call to FlexLayout implementation to calculate layout values for later retrieval.
510             Interop.FlexLayout.FlexLayout_CalculateLayout( swigCPtr, width.AsDecimal(), height.AsDecimal(), isLayoutRtl );
511
512             int count = LayoutChildren.Count;
513             for( int childIndex = 0; childIndex < count; childIndex++)
514             {
515                 LayoutItem childLayout = LayoutChildren[childIndex];
516                 if( childLayout != null )
517                 {
518                     // Get the frame for the child, start, top, end, bottom.
519                     Vector4 frame = new Vector4(Interop.FlexLayout.FlexLayout_GetNodeFrame(swigCPtr, childIndex ), true);
520                     childLayout.Layout( new LayoutLength(frame.X), new LayoutLength(frame.Y), new LayoutLength(frame.Z), new LayoutLength(frame.W) );
521                 }
522             }
523         }
524
525     } // FLexlayout
526 } // namesspace Tizen.NUI