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;
22 using Tizen.NUI.Binding;
27 /// [Draft] This class implements a flex layout.
28 /// The flex layout implementation is based on open source Facebook Yoga layout engine.
29 /// For more information about the flex layout API and how to use it please refer to https://yogalayout.com/docs/
30 /// We implement the subset of the API in the class below.
32 public class FlexLayout : LayoutGroup, global::System.IDisposable
37 [EditorBrowsable(EditorBrowsableState.Never)]
38 internal static readonly BindableProperty FlexItemProperty = BindableProperty.CreateAttached("FlexItem", typeof(HandleRef), typeof(FlexLayout), default(HandleRef));
41 /// FlexAlignmentSelfProperty
43 [EditorBrowsable(EditorBrowsableState.Never)]
44 public static readonly BindableProperty FlexAlignmentSelfProperty = BindableProperty.CreateAttached("FlexAlignmentSelf", typeof(AlignmentType), typeof(FlexLayout), AlignmentType.Auto, propertyChanged: OnChildPropertyChanged);
47 /// FlexPositionTypeProperty
49 [EditorBrowsable(EditorBrowsableState.Never)]
50 public static readonly BindableProperty FlexPositionTypeProperty = BindableProperty.CreateAttached("FlexPositionType", typeof(PositionType), typeof(FlexLayout), PositionType.Relative, propertyChanged: OnChildPropertyChanged);
53 /// AspectRatioProperty
55 [EditorBrowsable(EditorBrowsableState.Never)]
56 public static readonly BindableProperty FlexAspectRatioProperty = BindableProperty.CreateAttached("FlexAspectRatio", typeof(float), typeof(FlexLayout), FlexUndefined, validateValue: (bindable, value) => (float)value > 0, propertyChanged: OnChildPropertyChanged);
61 [EditorBrowsable(EditorBrowsableState.Never)]
62 public static readonly BindableProperty FlexBasisProperty = BindableProperty.CreateAttached("FlexBasis", typeof(float), typeof(FlexLayout), FlexUndefined, validateValue: (bindable, value) => (float)value >= 0, propertyChanged: OnChildPropertyChanged);
65 /// FlexShrinkProperty
67 [EditorBrowsable(EditorBrowsableState.Never)]
68 public static readonly BindableProperty FlexShrinkProperty = BindableProperty.CreateAttached("FlexShrink", typeof(float), typeof(FlexLayout), 1.0f, validateValue: (bindable, value) => (float)value >= 0, propertyChanged: OnChildPropertyChanged);
73 [EditorBrowsable(EditorBrowsableState.Never)]
74 public static readonly BindableProperty FlexGrowProperty = BindableProperty.CreateAttached("FlexGrow", typeof(float), typeof(FlexLayout), FlexUndefined, validateValue: (bindable, value) => (float)value >= 0, propertyChanged: OnChildPropertyChanged);
76 private static bool LayoutDebugFlex = false; // Debug flag
78 private global::System.Runtime.InteropServices.HandleRef swigCPtr;
79 private bool swigCMemOwn;
80 private bool disposed;
81 private bool isDisposeQueued = false;
83 private MeasureSpecification parentMeasureSpecificationWidth;
84 private MeasureSpecification parentMeasureSpecificationHeight;
86 private IntPtr _rootFlex; // Pointer to the unmanged flex layout class.
88 internal const float FlexUndefined = 10E20F; // Auto setting which is equivalent to WrapContent.
90 internal struct MeasuredSize
92 public MeasuredSize(float x, float y)
102 /// Get the alignment self of the layout items.
104 [EditorBrowsable(EditorBrowsableState.Never)]
105 public static AlignmentType GetFlexAlignmentSelf(View view)
107 return (AlignmentType)view.GetValue(FlexAlignmentSelfProperty);
111 /// Get the position type of the layout items.
113 [EditorBrowsable(EditorBrowsableState.Never)]
114 public static PositionType GetFlexPositionType(View view)
116 return (PositionType)view.GetValue(FlexPositionTypeProperty);
120 /// Get the aspect ratio of the layout items.
122 [EditorBrowsable(EditorBrowsableState.Never)]
123 public static float GetFlexAspectRatio(View view)
125 return (float)view.GetValue(FlexAspectRatioProperty);
129 /// Get the basis of the layout items.
131 [EditorBrowsable(EditorBrowsableState.Never)]
132 public static float GetFlexBasis(View view)
134 return (float)view.GetValue(FlexBasisProperty);
138 /// Get the shrink of the layout items.
140 [EditorBrowsable(EditorBrowsableState.Never)]
141 public static float GetFlexShrink(View view)
143 return (float)view.GetValue(FlexShrinkProperty);
147 /// Get the grow of the layout items.
149 [EditorBrowsable(EditorBrowsableState.Never)]
150 public static float GetFlexGrow(View view)
152 return (float)view.GetValue(FlexGrowProperty);
156 /// Set the alignment self of the layout items.
158 [EditorBrowsable(EditorBrowsableState.Never)]
159 public static void SetFlexAlignmentSelf(View view, AlignmentType value)
161 SetChildValue(view, FlexAlignmentSelfProperty, value);
165 /// Set the position type of the layout items.
167 [EditorBrowsable(EditorBrowsableState.Never)]
168 public static void SetFlexPositionType(View view, PositionType value)
170 SetChildValue(view, FlexPositionTypeProperty, value);
174 /// Set the aspect ratio of the layout items.
176 [EditorBrowsable(EditorBrowsableState.Never)]
177 public static void SetFlexAspectRatio(View view, float value)
179 SetChildValue(view, FlexAspectRatioProperty, value);
183 /// Set the basis of the layout items.
185 [EditorBrowsable(EditorBrowsableState.Never)]
186 public static void SetFlexBasis(View view, float value)
188 SetChildValue(view, FlexBasisProperty, value);
192 /// Set the shrink of the layout items.
194 [EditorBrowsable(EditorBrowsableState.Never)]
195 public static void SetFlexShrink(View view, float value)
197 SetChildValue(view, FlexShrinkProperty, value);
201 /// Set the grow of the layout items.
203 [EditorBrowsable(EditorBrowsableState.Never)]
204 public static void SetFlexGrow(View view, float value)
206 SetChildValue(view, FlexGrowProperty, value);
209 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
210 internal delegate MeasuredSize ChildMeasureCallback( global::System.IntPtr child, float width, int measureModeWidth, float height, int measureModeHeight );
212 event ChildMeasureCallback measureChildDelegate; // Stores a delegate to the child measure callback. Used for all children of this FlexLayout.
214 internal FlexLayout(global::System.IntPtr cPtr, bool cMemoryOwn)
216 swigCMemOwn = cMemoryOwn;
217 swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
218 _rootFlex = Interop.FlexLayout.FlexLayout_New();
219 measureChildDelegate = new ChildMeasureCallback(measureChild);
222 internal static global::System.Runtime.InteropServices.HandleRef getCPtr(FlexLayout obj)
224 return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr;
230 /// <since_tizen> 6 </since_tizen>
231 public void Dispose()
233 // Throw exception if Dispose() is called in separate thread.
234 if (!Window.IsInstalled())
236 throw new System.InvalidOperationException("This API called from separate thread. This API must be called from MainThread.");
241 Dispose(DisposeTypes.Implicit);
245 Dispose(DisposeTypes.Explicit);
246 System.GC.SuppressFinalize(this);
253 /// <since_tizen> 6 </since_tizen>
254 protected virtual void Dispose(DisposeTypes type)
261 if (type == DisposeTypes.Explicit)
264 // Release your own managed resources here.
265 // You should release all of your own disposable objects here.
269 // Release your own unmanaged resources here.
270 // You should not access any managed member here except static instance.
271 // because the execution order of Finalizes is non-deterministic.
272 if (swigCPtr.Handle != global::System.IntPtr.Zero)
277 Interop.FlexLayout.delete_FlexLayout(swigCPtr);
279 swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
285 /// [Draft] Creates a FlexLayout object.
287 /// <since_tizen> 6 </since_tizen>
288 public FlexLayout() : this(Interop.FlexLayout.FlexLayout_New(), true)
290 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
293 internal static FlexLayout DownCast(BaseHandle handle)
295 FlexLayout ret = new FlexLayout(Interop.FlexLayout.FlexLayout_DownCast(BaseHandle.getCPtr(handle)), true);
296 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
300 internal FlexLayout(FlexLayout other) : this(Interop.FlexLayout.new_FlexLayout__SWIG_1(FlexLayout.getCPtr(other)), true)
302 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
305 internal FlexLayout Assign(FlexLayout other)
307 FlexLayout ret = new FlexLayout(Interop.FlexLayout.FlexLayout_Assign(swigCPtr, FlexLayout.getCPtr(other)), false);
308 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
312 internal void SetFlexDirection(FlexLayout.FlexDirection flexDirection)
314 Interop.FlexLayout.FlexLayout_SetFlexDirection(swigCPtr, (int)flexDirection);
318 internal FlexLayout.FlexDirection GetFlexDirection()
320 FlexLayout.FlexDirection ret = (FlexLayout.FlexDirection)Interop.FlexLayout.FlexLayout_GetFlexDirection(swigCPtr);
325 internal void SetFlexJustification(FlexLayout.FlexJustification flexJustification)
327 Interop.FlexLayout.FlexLayout_SetFlexJustification(swigCPtr, (int)flexJustification);
331 internal FlexLayout.FlexJustification GetFlexJustification()
333 FlexLayout.FlexJustification ret = (FlexLayout.FlexJustification)Interop.FlexLayout.FlexLayout_GetFlexJustification(swigCPtr);
337 internal void SetFlexWrap(FlexLayout.FlexWrapType flexWrap)
339 Interop.FlexLayout.FlexLayout_SetFlexWrap(swigCPtr, (int)flexWrap);
343 internal FlexLayout.FlexWrapType GetFlexWrap()
345 FlexLayout.FlexWrapType ret = (FlexLayout.FlexWrapType)Interop.FlexLayout.FlexLayout_GetFlexWrap(swigCPtr);
349 internal void SetFlexAlignment(FlexLayout.AlignmentType flexAlignment)
351 Interop.FlexLayout.FlexLayout_SetFlexAlignment(swigCPtr, (int)flexAlignment);
355 internal FlexLayout.AlignmentType GetFlexAlignment()
357 FlexLayout.AlignmentType ret = (FlexLayout.AlignmentType)Interop.FlexLayout.FlexLayout_GetFlexAlignment(swigCPtr);
358 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
362 internal void SetFlexItemsAlignment(FlexLayout.AlignmentType flexAlignment)
364 Interop.FlexLayout.FlexLayout_SetFlexItemsAlignment(swigCPtr, (int)flexAlignment);
368 internal FlexLayout.AlignmentType GetFlexItemsAlignment()
370 FlexLayout.AlignmentType ret = (FlexLayout.AlignmentType)Interop.FlexLayout.FlexLayout_GetFlexItemsAlignment(swigCPtr);
371 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
376 /// [Draft] Get/Set the flex direction in the layout.
377 /// The direction of the main-axis which determines the direction that flex items are laid out.
379 /// <since_tizen> 6 </since_tizen>
380 public FlexDirection Direction
384 return GetFlexDirection();
388 SetFlexDirection(value);
393 /// [Draft] Get/Set the justification in the layout.
395 /// <since_tizen> 6 </since_tizen>
396 public FlexJustification Justification
400 return GetFlexJustification();
404 SetFlexJustification(value);
409 /// [Draft] Get/Set the wrap in the layout.
411 /// <since_tizen> 6 </since_tizen>
412 public FlexWrapType WrapType
416 return GetFlexWrap();
425 /// [Draft] Get/Set the alignment of the layout content.
427 /// <since_tizen> 6 </since_tizen>
428 public AlignmentType Alignment
432 return GetFlexAlignment();
436 SetFlexAlignment(value);
441 /// [Draft] Get/Set the alignment of the layout items.
443 /// <since_tizen> 6 </since_tizen>
444 public AlignmentType ItemsAlignment
448 return GetFlexItemsAlignment();
452 SetFlexItemsAlignment(value);
457 /// [Draft] Enumeration for the direction of the main axis in the flex container.
458 /// This determines the direction that flex items are laid out in the flex container.
460 /// <since_tizen> 6 </since_tizen>
461 public enum FlexDirection
464 /// The flexible items are displayed vertically as a column
468 /// The flexible items are displayed vertically as a column, but in reverse order
472 /// The flexible items are displayed horizontally as a row
476 /// The flexible items are displayed horizontally as a row, but in reverse order
482 /// [Draft] Enumeration for the alignment of the flex items when the items do not use all available space on the main-axis.
484 /// <since_tizen> 6 </since_tizen>
485 public enum FlexJustification
488 /// Items are positioned at the beginning of the container
492 /// Items are positioned at the center of the container
496 /// Items are positioned at the end of the container
500 /// Items are positioned with equal space between the lines
504 /// Items are positioned with equal space before, between, and after the lines
510 /// [Draft] Enumeration for the wrap type of the flex container when there is no enough room for all the items on one flex line.
512 /// <since_tizen> 6 </since_tizen>
513 public enum FlexWrapType
516 /// Flex items laid out in single line (shrunk to fit the flex container along the main axis)
520 /// Flex items laid out in multiple lines if needed
526 /// [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.
528 /// <since_tizen> 6 </since_tizen>
529 public enum AlignmentType
532 /// Inherits the same alignment from the parent
536 /// At the beginning of the container
540 /// At the center of the container
544 /// At the end of the container
548 /// Stretch to fit the container
553 /// Enumeration for the position type of the flex item how it is positioned within its parent.
555 [EditorBrowsable(EditorBrowsableState.Never)]
556 public enum PositionType
559 /// Flex items laid out relatively.
561 [EditorBrowsable(EditorBrowsableState.Never)]
564 /// Flex items laid out absolutely.
566 [EditorBrowsable(EditorBrowsableState.Never)]
570 private MeasuredSize measureChild(global::System.IntPtr childPtr, float width, int measureModeWidth, float height, int measureModeHeight)
572 // We need to measure child layout
573 View child = Registry.GetManagedBaseHandleFromNativePtr(childPtr) as View;
575 LayoutItem childLayout = child.Layout;
577 MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(
578 new MeasureSpecification(
579 new LayoutLength(parentMeasureSpecificationWidth.Size - (child.Margin.Start + child.Margin.End)),
580 parentMeasureSpecificationWidth.Mode),
581 new LayoutLength(Padding.Start + Padding.End),
582 new LayoutLength(child.WidthSpecification));
584 MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(
585 new MeasureSpecification(
586 new LayoutLength(parentMeasureSpecificationHeight.Size - (child.Margin.Top + child.Margin.Bottom)),
587 parentMeasureSpecificationHeight.Mode),
588 new LayoutLength(Padding.Top + Padding.Bottom),
589 new LayoutLength(child.HeightSpecification));
591 childLayout.Measure( childWidthMeasureSpec, childHeightMeasureSpec);
593 return new MeasuredSize(childLayout.MeasuredWidth.Size.AsRoundedValue(),childLayout.MeasuredHeight.Size.AsRoundedValue());
596 void InsertChild( LayoutItem child )
598 // Store created node for child
599 IntPtr childPtr = Interop.FlexLayout.FlexLayout_AddChildWithMargin(swigCPtr, View.getCPtr(child.Owner), Extents.getCPtr(child.Owner.Margin), measureChildDelegate, LayoutChildren.Count-1);
600 HandleRef childHandleRef = new HandleRef(child.Owner, childPtr);
601 SetChildValue(child.Owner, FlexItemProperty, childHandleRef);
605 /// Callback when child is added to container.<br />
606 /// Derived classes can use this to set their own child properties on the child layout's owner.<br />
608 /// <param name="child">The Layout child.</param>
609 /// <since_tizen> 6 </since_tizen>
610 protected override void OnChildAdd(LayoutItem child)
616 /// Callback when child is removed from container.<br />
618 /// <param name="child">The Layout child.</param>
619 /// <since_tizen> 6 </since_tizen>
620 protected override void OnChildRemove(LayoutItem child)
622 // When child View is removed from it's parent View (that is a Layout) then remove it from the layout too.
623 // FlexLayout refers to the child as a View not LayoutItem.
624 Interop.FlexLayout.FlexLayout_RemoveChild(swigCPtr, child);
628 /// Measure the layout and its content to determine the measured width and the measured height.<br />
630 /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
631 /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
632 /// <since_tizen> 6 </since_tizen>
633 protected override void OnMeasure( MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec )
635 bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL;
636 Extents padding = Owner.Padding;
637 Extents margin = Owner.Margin;
639 Interop.FlexLayout.FlexLayout_SetMargin(swigCPtr, Extents.getCPtr(margin));
640 Interop.FlexLayout.FlexLayout_SetPadding(swigCPtr, Extents.getCPtr(padding));
642 float width = FlexUndefined; // Behaves as WrapContent (Flex Auto)
643 float height = FlexUndefined; // Behaves as WrapContent (Flex Auto)
645 if( widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || widthMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost )
647 width = widthMeasureSpec.Size.AsDecimal();
650 if ( heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || heightMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost )
652 height = heightMeasureSpec.Size.AsDecimal();
655 // Save measureSpec to measure children.
656 // In other Layout, we can pass it as parameter to measuring child but in FlexLayout we can't
657 // because measurChild function is called by native callback.
658 parentMeasureSpecificationWidth = widthMeasureSpec;
659 parentMeasureSpecificationHeight = heightMeasureSpec;
661 // Assign child properties
662 for (int i = 0; i < LayoutChildren.Count; i++)
664 LayoutItem layoutItem = LayoutChildren[i];
665 View Child = layoutItem?.Owner;
666 HandleRef childHandleRef = (HandleRef)Child.GetValue(FlexItemProperty);
667 if (childHandleRef.Handle == IntPtr.Zero || Child == null)
670 AlignmentType flexAlignemnt = GetFlexAlignmentSelf(Child);
671 PositionType flexPosition = GetFlexPositionType(Child);
672 float flexAspectRatio = GetFlexAspectRatio(Child);
673 float flexBasis = GetFlexBasis(Child);
674 float flexShrink = GetFlexShrink(Child);
675 float flexGrow = GetFlexGrow(Child);
677 Interop.FlexLayout.FlexLayout_SetFlexAlignmentSelf(childHandleRef, (int)flexAlignemnt);
678 Interop.FlexLayout.FlexLayout_SetFlexPositionType(childHandleRef, (int)flexPosition);
679 Interop.FlexLayout.FlexLayout_SetFlexAspectRatio(childHandleRef, flexAspectRatio);
680 Interop.FlexLayout.FlexLayout_SetFlexBasis(childHandleRef, flexBasis);
681 Interop.FlexLayout.FlexLayout_SetFlexShrink(childHandleRef, flexShrink);
682 Interop.FlexLayout.FlexLayout_SetFlexGrow(childHandleRef, flexGrow);
685 Interop.FlexLayout.FlexLayout_CalculateLayout( swigCPtr, width, height, isLayoutRtl );
687 LayoutLength flexLayoutWidth = new LayoutLength( Interop.FlexLayout.FlexLayout_GetWidth(swigCPtr) );
688 LayoutLength flexLayoutHeight = new LayoutLength( Interop.FlexLayout.FlexLayout_GetHeight(swigCPtr) );
690 Debug.WriteLineIf( LayoutDebugFlex, "FlexLayout OnMeasure width:" + flexLayoutWidth.AsRoundedValue()
691 + " height:" + flexLayoutHeight.AsRoundedValue() );
693 SetMeasuredDimensions( GetDefaultSize(flexLayoutWidth, widthMeasureSpec ),
694 GetDefaultSize(flexLayoutHeight, heightMeasureSpec ) );
698 /// Assign a size and position to each of its children.<br />
700 /// <param name="changed">This is a new size or position for this layout.</param>
701 /// <param name="left">Left position, relative to parent.</param>
702 /// <param name="top"> Top position, relative to parent.</param>
703 /// <param name="right">Right position, relative to parent.</param>
704 /// <param name="bottom">Bottom position, relative to parent.</param>
705 /// <since_tizen> 6 </since_tizen>
706 protected override void OnLayout( bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom )
709 bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL;
710 LayoutLength width = right - left;
711 LayoutLength height = bottom - top;
713 // Call to FlexLayout implementation to calculate layout values for later retrieval.
714 Interop.FlexLayout.FlexLayout_CalculateLayout( swigCPtr, width.AsDecimal(), height.AsDecimal(), isLayoutRtl );
716 int count = LayoutChildren.Count;
717 for( int childIndex = 0; childIndex < count; childIndex++)
719 LayoutItem childLayout = LayoutChildren[childIndex];
720 if( childLayout != null )
722 // Get the frame for the child, start, top, end, bottom.
723 Vector4 frame = new Vector4(Interop.FlexLayout.FlexLayout_GetNodeFrame(swigCPtr, childIndex ), true);
724 childLayout.Layout( new LayoutLength(frame.X), new LayoutLength(frame.Y), new LayoutLength(frame.Z), new LayoutLength(frame.W) );
730 } // namesspace Tizen.NUI