1 /* Copyright (c) 2019 Samsung Electronics Co., Ltd.
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
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 using System.ComponentModel;
19 using Tizen.NUI.BaseComponents;
20 using System.Runtime.InteropServices;
21 using System.Diagnostics;
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.
31 public class FlexLayout : LayoutGroup, global::System.IDisposable
33 private static bool LayoutDebugFlex = false; // Debug flag
34 float Flex{ get; set;}
35 int AlignSelf{get; set;}
37 private global::System.Runtime.InteropServices.HandleRef swigCPtr;
38 private bool swigCMemOwn;
39 private bool disposed;
40 private bool isDisposeQueued = false;
42 private MeasureSpecification parentMeasureSpecificationWidth;
43 private MeasureSpecification parentMeasureSpecificationHeight;
45 private IntPtr _rootFlex; // Pointer to the unmanged flex layout class.
47 internal const float FlexUndefined = 10E20F; // Auto setting which is equivalent to WrapContent.
49 internal struct MeasuredSize
51 public MeasuredSize(float x, float y)
60 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
61 internal delegate MeasuredSize ChildMeasureCallback( global::System.IntPtr child, float width, int measureModeWidth, float height, int measureModeHeight );
63 event ChildMeasureCallback measureChildDelegate; // Stores a delegate to the child measure callback. Used for all children of this FlexLayout.
65 internal FlexLayout(global::System.IntPtr cPtr, bool cMemoryOwn)
67 swigCMemOwn = cMemoryOwn;
68 swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
69 _rootFlex = Interop.FlexLayout.FlexLayout_New();
70 measureChildDelegate = new ChildMeasureCallback(measureChild);
73 internal static global::System.Runtime.InteropServices.HandleRef getCPtr(FlexLayout obj)
75 return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr;
81 /// <since_tizen> 6 </since_tizen>
84 // Throw exception if Dispose() is called in separate thread.
85 if (!Window.IsInstalled())
87 throw new System.InvalidOperationException("This API called from separate thread. This API must be called from MainThread.");
92 Dispose(DisposeTypes.Implicit);
96 Dispose(DisposeTypes.Explicit);
97 System.GC.SuppressFinalize(this);
104 /// <since_tizen> 6 </since_tizen>
105 protected virtual void Dispose(DisposeTypes type)
112 if (type == DisposeTypes.Explicit)
115 // Release your own managed resources here.
116 // You should release all of your own disposable objects here.
120 // Release your own unmanaged resources here.
121 // You should not access any managed member here except static instance.
122 // because the execution order of Finalizes is non-deterministic.
123 if (swigCPtr.Handle != global::System.IntPtr.Zero)
128 Interop.FlexLayout.delete_FlexLayout(swigCPtr);
130 swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
136 /// [Draft] Creates a FlexLayout object.
138 /// <since_tizen> 6 </since_tizen>
139 public FlexLayout() : this(Interop.FlexLayout.FlexLayout_New(), true)
141 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
144 internal static FlexLayout DownCast(BaseHandle handle)
146 FlexLayout ret = new FlexLayout(Interop.FlexLayout.FlexLayout_DownCast(BaseHandle.getCPtr(handle)), true);
147 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
151 internal FlexLayout(FlexLayout other) : this(Interop.FlexLayout.new_FlexLayout__SWIG_1(FlexLayout.getCPtr(other)), true)
153 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
156 internal FlexLayout Assign(FlexLayout other)
158 FlexLayout ret = new FlexLayout(Interop.FlexLayout.FlexLayout_Assign(swigCPtr, FlexLayout.getCPtr(other)), false);
159 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
163 internal void SetFlexDirection(FlexLayout.FlexDirection flexDirection)
165 Interop.FlexLayout.FlexLayout_SetFlexDirection(swigCPtr, (int)flexDirection);
169 internal FlexLayout.FlexDirection GetFlexDirection()
171 FlexLayout.FlexDirection ret = (FlexLayout.FlexDirection)Interop.FlexLayout.FlexLayout_GetFlexDirection(swigCPtr);
176 internal void SetFlexJustification(FlexLayout.FlexJustification flexJustification)
178 Interop.FlexLayout.FlexLayout_SetFlexJustification(swigCPtr, (int)flexJustification);
182 internal FlexLayout.FlexJustification GetFlexJustification()
184 FlexLayout.FlexJustification ret = (FlexLayout.FlexJustification)Interop.FlexLayout.FlexLayout_GetFlexJustification(swigCPtr);
188 internal void SetFlexWrap(FlexLayout.FlexWrapType flexWrap)
190 Interop.FlexLayout.FlexLayout_SetFlexWrap(swigCPtr, (int)flexWrap);
194 internal FlexLayout.FlexWrapType GetFlexWrap()
196 FlexLayout.FlexWrapType ret = (FlexLayout.FlexWrapType)Interop.FlexLayout.FlexLayout_GetFlexWrap(swigCPtr);
200 internal void SetFlexAlignment(FlexLayout.AlignmentType flexAlignment)
202 Interop.FlexLayout.FlexLayout_SetFlexAlignment(swigCPtr, (int)flexAlignment);
206 internal FlexLayout.AlignmentType GetFlexAlignment()
208 FlexLayout.AlignmentType ret = (FlexLayout.AlignmentType)Interop.FlexLayout.FlexLayout_GetFlexAlignment(swigCPtr);
209 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
213 internal void SetFlexItemsAlignment(FlexLayout.AlignmentType flexAlignment)
215 Interop.FlexLayout.FlexLayout_SetFlexItemsAlignment(swigCPtr, (int)flexAlignment);
219 internal FlexLayout.AlignmentType GetFlexItemsAlignment()
221 FlexLayout.AlignmentType ret = (FlexLayout.AlignmentType)Interop.FlexLayout.FlexLayout_GetFlexItemsAlignment(swigCPtr);
222 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
227 /// [Draft] Get/Set the flex direction in the layout.
228 /// The direction of the main-axis which determines the direction that flex items are laid out.
230 /// <since_tizen> 6 </since_tizen>
231 public FlexDirection Direction
235 return GetFlexDirection();
239 SetFlexDirection(value);
244 /// [Draft] Get/Set the justification in the layout.
246 /// <since_tizen> 6 </since_tizen>
247 public FlexJustification Justification
251 return GetFlexJustification();
255 SetFlexJustification(value);
260 /// [Draft] Get/Set the wrap in the layout.
262 /// <since_tizen> 6 </since_tizen>
263 public FlexWrapType WrapType
267 return GetFlexWrap();
276 /// [Draft] Get/Set the alignment of the layout content.
278 /// <since_tizen> 6 </since_tizen>
279 public AlignmentType Alignment
283 return GetFlexAlignment();
287 SetFlexAlignment(value);
292 /// [Draft] Get/Set the alignment of the layout items.
294 /// <since_tizen> 6 </since_tizen>
295 public AlignmentType ItemsAlignment
299 return GetFlexItemsAlignment();
303 SetFlexItemsAlignment(value);
308 /// [Draft] Enumeration for the direction of the main axis in the flex container.
309 /// This determines the direction that flex items are laid out in the flex container.
311 /// <since_tizen> 6 </since_tizen>
312 public enum FlexDirection
315 /// The flexible items are displayed vertically as a column
319 /// The flexible items are displayed vertically as a column, but in reverse order
323 /// The flexible items are displayed horizontally as a row
327 /// The flexible items are displayed horizontally as a row, but in reverse order
333 /// [Draft] Enumeration for the alignment of the flex items when the items do not use all available space on the main-axis.
335 /// <since_tizen> 6 </since_tizen>
336 public enum FlexJustification
339 /// Items are positioned at the beginning of the container
343 /// Items are positioned at the center of the container
347 /// Items are positioned at the end of the container
351 /// Items are positioned with equal space between the lines
355 /// Items are positioned with equal space before, between, and after the lines
361 /// [Draft] Enumeration for the wrap type of the flex container when there is no enough room for all the items on one flex line.
363 /// <since_tizen> 6 </since_tizen>
364 public enum FlexWrapType
367 /// Flex items laid out in single line (shrunk to fit the flex container along the main axis)
371 /// Flex items laid out in multiple lines if needed
377 /// [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.
379 /// <since_tizen> 6 </since_tizen>
380 public enum AlignmentType
383 /// Inherits the same alignment from the parent
387 /// At the beginning of the container
391 /// At the center of the container
395 /// At the end of the container
399 /// Stretch to fit the container
404 private MeasuredSize measureChild(global::System.IntPtr childPtr, float width, int measureModeWidth, float height, int measureModeHeight)
406 // We need to measure child layout
407 View child = Registry.GetManagedBaseHandleFromNativePtr(childPtr) as View;
409 LayoutItem childLayout = child.Layout;
411 MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(
412 new MeasureSpecification(
413 new LayoutLength(parentMeasureSpecificationWidth.Size - (child.Margin.Start + child.Margin.End)),
414 parentMeasureSpecificationWidth.Mode),
415 new LayoutLength(Padding.Start + Padding.End),
416 new LayoutLength(child.WidthSpecification));
418 MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(
419 new MeasureSpecification(
420 new LayoutLength(parentMeasureSpecificationHeight.Size - (child.Margin.Top + child.Margin.Bottom)),
421 parentMeasureSpecificationHeight.Mode),
422 new LayoutLength(Padding.Top + Padding.Bottom),
423 new LayoutLength(child.HeightSpecification));
425 childLayout.Measure( childWidthMeasureSpec, childHeightMeasureSpec);
427 return new MeasuredSize(childLayout.MeasuredWidth.Size.AsRoundedValue(),childLayout.MeasuredHeight.Size.AsRoundedValue());
430 void InsertChild( LayoutItem child )
432 // Store created node for child
433 Interop.FlexLayout.FlexLayout_AddChildWithMargin(swigCPtr, View.getCPtr(child.Owner), Extents.getCPtr(child.Owner.Margin), measureChildDelegate, LayoutChildren.Count-1);
437 /// Callback when child is added to container.<br />
438 /// Derived classes can use this to set their own child properties on the child layout's owner.<br />
440 /// <param name="child">The Layout child.</param>
441 /// <since_tizen> 6 </since_tizen>
442 protected override void OnChildAdd(LayoutItem child)
448 /// Callback when child is removed from container.<br />
450 /// <param name="child">The Layout child.</param>
451 /// <since_tizen> 6 </since_tizen>
452 protected override void OnChildRemove(LayoutItem child)
454 // When child View is removed from it's parent View (that is a Layout) then remove it from the layout too.
455 // FlexLayout refers to the child as a View not LayoutItem.
456 Interop.FlexLayout.FlexLayout_RemoveChild(swigCPtr, child);
460 /// Measure the layout and its content to determine the measured width and the measured height.<br />
462 /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
463 /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
464 /// <since_tizen> 6 </since_tizen>
465 protected override void OnMeasure( MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec )
467 bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL;
468 Extents padding = Owner.Padding;
469 Extents margin = Owner.Margin;
471 Interop.FlexLayout.FlexLayout_SetMargin(swigCPtr, Extents.getCPtr(margin));
472 Interop.FlexLayout.FlexLayout_SetPadding(swigCPtr, Extents.getCPtr(padding));
474 float width = FlexUndefined; // Behaves as WrapContent (Flex Auto)
475 float height = FlexUndefined; // Behaves as WrapContent (Flex Auto)
477 if( widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || widthMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost )
479 width = widthMeasureSpec.Size.AsDecimal();
482 if ( heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || heightMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost )
484 height = heightMeasureSpec.Size.AsDecimal();
487 // Save measureSpec to measure children.
488 // In other Layout, we can pass it as parameter to measuring child but in FlexLayout we can't
489 // because measurChild function is called by native callback.
490 parentMeasureSpecificationWidth = widthMeasureSpec;
491 parentMeasureSpecificationHeight = heightMeasureSpec;
493 Interop.FlexLayout.FlexLayout_CalculateLayout( swigCPtr, width, height, isLayoutRtl );
495 LayoutLength flexLayoutWidth = new LayoutLength( Interop.FlexLayout.FlexLayout_GetWidth(swigCPtr) );
496 LayoutLength flexLayoutHeight = new LayoutLength( Interop.FlexLayout.FlexLayout_GetHeight(swigCPtr) );
498 Debug.WriteLineIf( LayoutDebugFlex, "FlexLayout OnMeasure width:" + flexLayoutWidth.AsRoundedValue()
499 + " height:" + flexLayoutHeight.AsRoundedValue() );
501 SetMeasuredDimensions( GetDefaultSize(flexLayoutWidth, widthMeasureSpec ),
502 GetDefaultSize(flexLayoutHeight, heightMeasureSpec ) );
506 /// Assign a size and position to each of its children.<br />
508 /// <param name="changed">This is a new size or position for this layout.</param>
509 /// <param name="left">Left position, relative to parent.</param>
510 /// <param name="top"> Top position, relative to parent.</param>
511 /// <param name="right">Right position, relative to parent.</param>
512 /// <param name="bottom">Bottom position, relative to parent.</param>
513 /// <since_tizen> 6 </since_tizen>
514 protected override void OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
517 bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL;
518 LayoutLength width = right - left;
519 LayoutLength height = bottom - top;
521 // Call to FlexLayout implementation to calculate layout values for later retrieval.
522 Interop.FlexLayout.FlexLayout_CalculateLayout( swigCPtr, width.AsDecimal(), height.AsDecimal(), isLayoutRtl );
524 int count = LayoutChildren.Count;
525 for( int childIndex = 0; childIndex < count; childIndex++)
527 LayoutItem childLayout = LayoutChildren[childIndex];
528 if( childLayout != null )
530 // Get the frame for the child, start, top, end, bottom.
531 Vector4 frame = new Vector4(Interop.FlexLayout.FlexLayout_GetNodeFrame(swigCPtr, childIndex ), true);
532 childLayout.Layout( new LayoutLength(frame.X), new LayoutLength(frame.Y), new LayoutLength(frame.Z), new LayoutLength(frame.W) );
538 } // namesspace Tizen.NUI