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