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 /// 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 /// <param name="view">The layout item</param>
105 /// <returns> The value of alignment self</returns>
106 /// <since_tizen> 8 </since_tizen>
107 public static AlignmentType GetFlexAlignmentSelf(View view)
109 return (AlignmentType)view.GetValue(FlexAlignmentSelfProperty);
113 /// Get the position type of the layout items.
115 /// <param name="view">The layout item</param>
116 /// <returns> The value of position type</returns>
117 /// <since_tizen> 8 </since_tizen>
118 public static PositionType GetFlexPositionType(View view)
120 return (PositionType)view.GetValue(FlexPositionTypeProperty);
124 /// Get the aspect ratio of the layout items.
126 /// <param name="view">The layout item</param>
127 /// <returns> The value of aspect ratio</returns>
128 /// <since_tizen> 8 </since_tizen>
129 public static float GetFlexAspectRatio(View view)
131 return (float)view.GetValue(FlexAspectRatioProperty);
135 /// Get the basis of the layout items.
137 /// <param name="view">The layout item</param>
138 /// <returns> The value of basis</returns>
139 /// <since_tizen> 8 </since_tizen>
140 public static float GetFlexBasis(View view)
142 return (float)view.GetValue(FlexBasisProperty);
146 /// Get the shrink of the layout items.
148 /// <param name="view">The layout item</param>
149 /// <returns> The value of shrink</returns>
150 /// <since_tizen> 8 </since_tizen>
151 public static float GetFlexShrink(View view)
153 return (float)view.GetValue(FlexShrinkProperty);
157 /// Get the grow of the layout items.
159 /// <param name="view">The layout item</param>
160 /// <returns> The value of grow</returns>
161 /// <since_tizen> 8 </since_tizen>
162 public static float GetFlexGrow(View view)
164 return (float)view.GetValue(FlexGrowProperty);
168 /// Set the alignment self of the layout items.
170 /// <param name="view">The layout item</param>
171 /// <param name="value">The target value of alignment self</param>
172 /// <since_tizen> 8 </since_tizen>
173 public static void SetFlexAlignmentSelf(View view, AlignmentType value)
175 SetChildValue(view, FlexAlignmentSelfProperty, value);
179 /// Set the position type of the layout items.
181 /// <param name="view">The layout item</param>
182 /// <param name="value">The target value of position type</param>
183 /// <since_tizen> 8 </since_tizen>
184 public static void SetFlexPositionType(View view, PositionType value)
186 SetChildValue(view, FlexPositionTypeProperty, value);
190 /// Set the aspect ratio of the layout items.
192 /// <param name="view">The layout item</param>
193 /// <param name="value">The target value of aspect ratio</param>
194 /// <since_tizen> 8 </since_tizen>
195 public static void SetFlexAspectRatio(View view, float value)
197 SetChildValue(view, FlexAspectRatioProperty, value);
201 /// Set the basis of the layout items.
203 /// <param name="view">The layout item</param>
204 /// <param name="value">The target value of basis</param>
205 /// <since_tizen> 8 </since_tizen>
206 public static void SetFlexBasis(View view, float value)
208 SetChildValue(view, FlexBasisProperty, value);
212 /// Set the shrink of the layout items.
214 /// <param name="view">The layout item</param>
215 /// <param name="value">The target value of shrink</param>
216 /// <since_tizen> 8 </since_tizen>
217 public static void SetFlexShrink(View view, float value)
219 SetChildValue(view, FlexShrinkProperty, value);
223 /// Set the grow of the layout items.
225 /// <param name="view">The layout item</param>
226 /// <param name="value">The target value of flex</param>
227 /// <since_tizen> 8 </since_tizen>
228 public static void SetFlexGrow(View view, float value)
230 SetChildValue(view, FlexGrowProperty, value);
233 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
234 internal delegate MeasuredSize ChildMeasureCallback(global::System.IntPtr child, float width, int measureModeWidth, float height, int measureModeHeight);
236 event ChildMeasureCallback measureChildDelegate; // Stores a delegate to the child measure callback. Used for all children of this FlexLayout.
238 internal FlexLayout(global::System.IntPtr cPtr, bool cMemoryOwn)
240 swigCMemOwn = cMemoryOwn;
241 swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
242 _rootFlex = Interop.FlexLayout.FlexLayout_New();
243 measureChildDelegate = new ChildMeasureCallback(measureChild);
246 internal static global::System.Runtime.InteropServices.HandleRef getCPtr(FlexLayout obj)
248 return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr;
254 /// <since_tizen> 6 </since_tizen>
255 public void Dispose()
257 // Throw exception if Dispose() is called in separate thread.
258 if (!Window.IsInstalled())
260 throw new System.InvalidOperationException("This API called from separate thread. This API must be called from MainThread.");
265 Dispose(DisposeTypes.Implicit);
269 Dispose(DisposeTypes.Explicit);
270 System.GC.SuppressFinalize(this);
277 /// <since_tizen> 6 </since_tizen>
278 protected virtual void Dispose(DisposeTypes type)
285 if (type == DisposeTypes.Explicit)
288 // Release your own managed resources here.
289 // You should release all of your own disposable objects here.
293 // Release your own unmanaged resources here.
294 // You should not access any managed member here except static instance.
295 // because the execution order of Finalizes is non-deterministic.
296 if (swigCPtr.Handle != global::System.IntPtr.Zero)
301 Interop.FlexLayout.delete_FlexLayout(swigCPtr);
303 swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
309 /// Creates a FlexLayout object.
311 /// <since_tizen> 6 </since_tizen>
312 public FlexLayout() : this(Interop.FlexLayout.FlexLayout_New(), true)
314 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
317 internal static FlexLayout DownCast(BaseHandle handle)
319 FlexLayout ret = new FlexLayout(Interop.FlexLayout.FlexLayout_DownCast(BaseHandle.getCPtr(handle)), true);
320 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
324 internal FlexLayout(FlexLayout other) : this(Interop.FlexLayout.new_FlexLayout__SWIG_1(FlexLayout.getCPtr(other)), true)
326 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
329 internal FlexLayout Assign(FlexLayout other)
331 FlexLayout ret = new FlexLayout(Interop.FlexLayout.FlexLayout_Assign(swigCPtr, FlexLayout.getCPtr(other)), false);
332 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
336 internal void SetFlexDirection(FlexLayout.FlexDirection flexDirection)
338 Interop.FlexLayout.FlexLayout_SetFlexDirection(swigCPtr, (int)flexDirection);
342 internal FlexLayout.FlexDirection GetFlexDirection()
344 FlexLayout.FlexDirection ret = (FlexLayout.FlexDirection)Interop.FlexLayout.FlexLayout_GetFlexDirection(swigCPtr);
349 internal void SetFlexJustification(FlexLayout.FlexJustification flexJustification)
351 Interop.FlexLayout.FlexLayout_SetFlexJustification(swigCPtr, (int)flexJustification);
355 internal FlexLayout.FlexJustification GetFlexJustification()
357 FlexLayout.FlexJustification ret = (FlexLayout.FlexJustification)Interop.FlexLayout.FlexLayout_GetFlexJustification(swigCPtr);
361 internal void SetFlexWrap(FlexLayout.FlexWrapType flexWrap)
363 Interop.FlexLayout.FlexLayout_SetFlexWrap(swigCPtr, (int)flexWrap);
367 internal FlexLayout.FlexWrapType GetFlexWrap()
369 FlexLayout.FlexWrapType ret = (FlexLayout.FlexWrapType)Interop.FlexLayout.FlexLayout_GetFlexWrap(swigCPtr);
373 internal void SetFlexAlignment(FlexLayout.AlignmentType flexAlignment)
375 Interop.FlexLayout.FlexLayout_SetFlexAlignment(swigCPtr, (int)flexAlignment);
379 internal FlexLayout.AlignmentType GetFlexAlignment()
381 FlexLayout.AlignmentType ret = (FlexLayout.AlignmentType)Interop.FlexLayout.FlexLayout_GetFlexAlignment(swigCPtr);
382 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
386 internal void SetFlexItemsAlignment(FlexLayout.AlignmentType flexAlignment)
388 Interop.FlexLayout.FlexLayout_SetFlexItemsAlignment(swigCPtr, (int)flexAlignment);
392 internal FlexLayout.AlignmentType GetFlexItemsAlignment()
394 FlexLayout.AlignmentType ret = (FlexLayout.AlignmentType)Interop.FlexLayout.FlexLayout_GetFlexItemsAlignment(swigCPtr);
395 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
400 /// Get/Set the flex direction in the layout.
401 /// The direction of the main-axis which determines the direction that flex items are laid out.
403 /// <since_tizen> 6 </since_tizen>
404 public FlexDirection Direction
408 return GetFlexDirection();
412 SetFlexDirection(value);
417 /// Get/Set the justification in the layout.
419 /// <since_tizen> 6 </since_tizen>
420 public FlexJustification Justification
424 return GetFlexJustification();
428 SetFlexJustification(value);
433 /// Get/Set the wrap in the layout.
435 /// <since_tizen> 6 </since_tizen>
436 public FlexWrapType WrapType
440 return GetFlexWrap();
449 /// Get/Set the alignment of the layout content.
451 /// <since_tizen> 6 </since_tizen>
452 public AlignmentType Alignment
456 return GetFlexAlignment();
460 SetFlexAlignment(value);
465 /// Get/Set the alignment of the layout items.
467 /// <since_tizen> 6 </since_tizen>
468 public AlignmentType ItemsAlignment
472 return GetFlexItemsAlignment();
476 SetFlexItemsAlignment(value);
481 /// Enumeration for the direction of the main axis in the flex container.
482 /// This determines the direction that flex items are laid out in the flex container.
484 /// <since_tizen> 6 </since_tizen>
485 public enum FlexDirection
488 /// The flexible items are displayed vertically as a column
492 /// The flexible items are displayed vertically as a column, but in reverse order
496 /// The flexible items are displayed horizontally as a row
500 /// The flexible items are displayed horizontally as a row, but in reverse order
506 /// Enumeration for the alignment of the flex items when the items do not use all available space on the main-axis.
508 /// <since_tizen> 6 </since_tizen>
509 public enum FlexJustification
512 /// Items are positioned at the beginning of the container
516 /// Items are positioned at the center of the container
520 /// Items are positioned at the end of the container
524 /// Items are positioned with equal space between the lines
528 /// Items are positioned with equal space before, between, and after the lines
534 /// Enumeration for the wrap type of the flex container when there is no enough room for all the items on one flex line.
536 /// <since_tizen> 6 </since_tizen>
537 public enum FlexWrapType
540 /// Flex items laid out in single line (shrunk to fit the flex container along the main axis)
544 /// Flex items laid out in multiple lines if needed
550 /// 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.
552 /// <since_tizen> 6 </since_tizen>
553 public enum AlignmentType
556 /// Inherits the same alignment from the parent
560 /// At the beginning of the container
564 /// At the center of the container
568 /// At the end of the container
572 /// Stretch to fit the container
577 /// Enumeration for the position type of the flex item how it is positioned within its parent.
579 /// <since_tizen> 8 </since_tizen>
580 public enum PositionType
583 /// Flex items laid out relatively.
587 /// Flex items laid out absolutely.
592 private MeasuredSize measureChild(global::System.IntPtr childPtr, float width, int measureModeWidth, float height, int measureModeHeight)
594 // We need to measure child layout
595 View child = Registry.GetManagedBaseHandleFromNativePtr(childPtr) as View;
597 LayoutItem childLayout = child.Layout;
599 MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(
600 new MeasureSpecification(
601 new LayoutLength(parentMeasureSpecificationWidth.Size - (child.Margin.Start + child.Margin.End)),
602 parentMeasureSpecificationWidth.Mode),
603 new LayoutLength(Padding.Start + Padding.End),
604 new LayoutLength(child.WidthSpecification));
606 MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(
607 new MeasureSpecification(
608 new LayoutLength(parentMeasureSpecificationHeight.Size - (child.Margin.Top + child.Margin.Bottom)),
609 parentMeasureSpecificationHeight.Mode),
610 new LayoutLength(Padding.Top + Padding.Bottom),
611 new LayoutLength(child.HeightSpecification));
613 childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
615 return new MeasuredSize(childLayout.MeasuredWidth.Size.AsRoundedValue(), childLayout.MeasuredHeight.Size.AsRoundedValue());
618 void InsertChild(LayoutItem child)
620 // Store created node for child
621 IntPtr childPtr = Interop.FlexLayout.FlexLayout_AddChildWithMargin(swigCPtr, View.getCPtr(child.Owner), Extents.getCPtr(child.Owner.Margin), measureChildDelegate, LayoutChildren.Count - 1);
622 HandleRef childHandleRef = new HandleRef(child.Owner, childPtr);
623 SetChildValue(child.Owner, FlexItemProperty, childHandleRef);
627 /// Callback when child is added to container.<br />
628 /// Derived classes can use this to set their own child properties on the child layout's owner.<br />
630 /// <param name="child">The Layout child.</param>
631 /// <since_tizen> 6 </since_tizen>
632 protected override void OnChildAdd(LayoutItem child)
638 /// Callback when child is removed from container.<br />
640 /// <param name="child">The Layout child.</param>
641 /// <since_tizen> 6 </since_tizen>
642 protected override void OnChildRemove(LayoutItem child)
644 // When child View is removed from it's parent View (that is a Layout) then remove it from the layout too.
645 // FlexLayout refers to the child as a View not LayoutItem.
646 Interop.FlexLayout.FlexLayout_RemoveChild(swigCPtr, child);
650 /// Measure the layout and its content to determine the measured width and the measured height.<br />
652 /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
653 /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
654 /// <since_tizen> 6 </since_tizen>
655 protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
657 bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL;
658 Extents padding = Owner.Padding;
659 Extents margin = Owner.Margin;
661 Interop.FlexLayout.FlexLayout_SetMargin(swigCPtr, Extents.getCPtr(margin));
662 Interop.FlexLayout.FlexLayout_SetPadding(swigCPtr, Extents.getCPtr(padding));
664 float width = FlexUndefined; // Behaves as WrapContent (Flex Auto)
665 float height = FlexUndefined; // Behaves as WrapContent (Flex Auto)
667 if (widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || widthMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost)
669 width = widthMeasureSpec.Size.AsDecimal();
672 if (heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || heightMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost)
674 height = heightMeasureSpec.Size.AsDecimal();
677 // Save measureSpec to measure children.
678 // In other Layout, we can pass it as parameter to measuring child but in FlexLayout we can't
679 // because measurChild function is called by native callback.
680 parentMeasureSpecificationWidth = widthMeasureSpec;
681 parentMeasureSpecificationHeight = heightMeasureSpec;
683 // Assign child properties
684 for (int i = 0; i < LayoutChildren.Count; i++)
686 LayoutItem layoutItem = LayoutChildren[i];
687 View Child = layoutItem?.Owner;
688 HandleRef childHandleRef = (HandleRef)Child.GetValue(FlexItemProperty);
689 if (childHandleRef.Handle == IntPtr.Zero || Child == null)
692 AlignmentType flexAlignemnt = GetFlexAlignmentSelf(Child);
693 PositionType flexPosition = GetFlexPositionType(Child);
694 float flexAspectRatio = GetFlexAspectRatio(Child);
695 float flexBasis = GetFlexBasis(Child);
696 float flexShrink = GetFlexShrink(Child);
697 float flexGrow = GetFlexGrow(Child);
699 Interop.FlexLayout.FlexLayout_SetFlexAlignmentSelf(childHandleRef, (int)flexAlignemnt);
700 Interop.FlexLayout.FlexLayout_SetFlexPositionType(childHandleRef, (int)flexPosition);
701 Interop.FlexLayout.FlexLayout_SetFlexAspectRatio(childHandleRef, flexAspectRatio);
702 Interop.FlexLayout.FlexLayout_SetFlexBasis(childHandleRef, flexBasis);
703 Interop.FlexLayout.FlexLayout_SetFlexShrink(childHandleRef, flexShrink);
704 Interop.FlexLayout.FlexLayout_SetFlexGrow(childHandleRef, flexGrow);
707 Interop.FlexLayout.FlexLayout_CalculateLayout(swigCPtr, width, height, isLayoutRtl);
709 LayoutLength flexLayoutWidth = new LayoutLength(Interop.FlexLayout.FlexLayout_GetWidth(swigCPtr));
710 LayoutLength flexLayoutHeight = new LayoutLength(Interop.FlexLayout.FlexLayout_GetHeight(swigCPtr));
712 Debug.WriteLineIf(LayoutDebugFlex, "FlexLayout OnMeasure width:" + flexLayoutWidth.AsRoundedValue()
713 + " height:" + flexLayoutHeight.AsRoundedValue());
715 SetMeasuredDimensions(GetDefaultSize(flexLayoutWidth, widthMeasureSpec),
716 GetDefaultSize(flexLayoutHeight, heightMeasureSpec));
720 /// Assign a size and position to each of its children.<br />
722 /// <param name="changed">This is a new size or position for this layout.</param>
723 /// <param name="left">Left position, relative to parent.</param>
724 /// <param name="top"> Top position, relative to parent.</param>
725 /// <param name="right">Right position, relative to parent.</param>
726 /// <param name="bottom">Bottom position, relative to parent.</param>
727 /// <since_tizen> 6 </since_tizen>
728 protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
731 bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL;
732 LayoutLength width = right - left;
733 LayoutLength height = bottom - top;
735 // Call to FlexLayout implementation to calculate layout values for later retrieval.
736 Interop.FlexLayout.FlexLayout_CalculateLayout(swigCPtr, width.AsDecimal(), height.AsDecimal(), isLayoutRtl);
738 int count = LayoutChildren.Count;
739 for (int childIndex = 0; childIndex < count; childIndex++)
741 LayoutItem childLayout = LayoutChildren[childIndex];
742 if (childLayout != null)
744 // Get the frame for the child, start, top, end, bottom.
745 Vector4 frame = new Vector4(Interop.FlexLayout.FlexLayout_GetNodeFrame(swigCPtr, childIndex), true);
746 childLayout.Layout(new LayoutLength(frame.X), new LayoutLength(frame.Y), new LayoutLength(frame.Z), new LayoutLength(frame.W));
752 } // namesspace Tizen.NUI