-/* Copyright (c) 2020 Samsung Electronics Co., Ltd.
+/* Copyright (c) 2021 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/// For more information about the flex layout API and how to use it please refer to https://yogalayout.com/docs/
/// We implement the subset of the API in the class below.
/// </summary>
- public class FlexLayout : LayoutGroup, global::System.IDisposable
+ public class FlexLayout : LayoutGroup
{
/// <summary>
/// FlexItemProperty
/// FlexPositionTypeProperty
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
- public static readonly BindableProperty FlexPositionTypeProperty = BindableProperty.CreateAttached("FlexPositionType", typeof(PositionType), typeof(FlexLayout), PositionType.Relative, validateValue: ValidateEnum((int)PositionType.Relative, (int)PositionType.Absolute), propertyChanged: OnChildPropertyChanged);
+ public static readonly BindableProperty FlexPositionTypeProperty = BindableProperty.CreateAttached("FlexPositionType", typeof(PositionType), typeof(FlexLayout), PositionType.Relative, validateValue: ValidateEnum((int)PositionType.Relative, (int)PositionType.Absolute),
+ propertyChanged: (bindable, oldValue, newValue) =>
+ {
+ if (bindable is View view)
+ {
+ view.ExcludeLayouting = (PositionType)newValue == PositionType.Absolute;
+ }
+ },
+ defaultValueCreator: (bindable) =>
+ {
+ var view = (View)bindable;
+ if (view.ExcludeLayouting)
+ return PositionType.Absolute;
+
+ return PositionType.Relative;
+ });
+
/// <summary>
/// AspectRatioProperty
[EditorBrowsable(EditorBrowsableState.Never)]
public static readonly BindableProperty FlexGrowProperty = BindableProperty.CreateAttached("FlexGrow", typeof(float), typeof(FlexLayout), FlexUndefined, validateValue: (bindable, value) => (float)value >= 0, propertyChanged: OnChildPropertyChanged);
- private static bool LayoutDebugFlex = false; // Debug flag
-
private global::System.Runtime.InteropServices.HandleRef swigCPtr;
private bool swigCMemOwn;
private bool disposed;
private MeasureSpecification parentMeasureSpecificationWidth;
private MeasureSpecification parentMeasureSpecificationHeight;
- private IntPtr _rootFlex; // Pointer to the unmanged flex layout class.
+ private IntPtr _rootFlex; // Pointer to the unmanaged flex layout class.
internal const float FlexUndefined = 10E20F; // Auto setting which is equivalent to WrapContent.
{
swigCMemOwn = cMemoryOwn;
swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
- _rootFlex = Interop.FlexLayout.FlexLayout_New();
+ _rootFlex = Interop.FlexLayout.New();
measureChildDelegate = new ChildMeasureCallback(measureChild);
}
return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr;
}
+ /// <summary>
+ /// Hidden API (Inhouse API).
+ /// Destructor.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ ~FlexLayout() => Dispose(false);
+
/// <inheritdoc/>
/// <since_tizen> 6 </since_tizen>
- public void Dispose()
+ public new void Dispose()
{
- // Throw exception if Dispose() is called in separate thread.
- if (!Window.IsInstalled())
+ Dispose(true);
+ System.GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Hidden API (Inhouse API).
+ /// Dispose.
+ /// </summary>
+ /// <remarks>
+ /// Following the guide of https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose.
+ /// This will replace "protected virtual void Dispose(DisposeTypes type)" which is exactly same in functionality.
+ /// </remarks>
+ /// <param name="disposing">true in order to free managed objects</param>
+ // Protected implementation of Dispose pattern.
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected override void Dispose(bool disposing)
+ {
+ if (disposed)
{
- throw new System.InvalidOperationException("This API called from separate thread. This API must be called from MainThread.");
+ return;
}
- if (isDisposeQueued)
+ if (disposing)
{
- Dispose(DisposeTypes.Implicit);
+ // TODO: dispose managed state (managed objects).
+ // Explicit call. user calls Dispose()
+
+ //Throw exception if Dispose() is called in separate thread.
+ if (!Window.IsInstalled())
+ {
+ throw new System.InvalidOperationException("This API called from separate thread. This API must be called from MainThread.");
+ }
+
+ if (isDisposeQueued)
+ {
+ Dispose(DisposeTypes.Implicit);
+ }
+ else
+ {
+ Dispose(DisposeTypes.Explicit);
+ }
}
else
{
- Dispose(DisposeTypes.Explicit);
- System.GC.SuppressFinalize(this);
+ // Implicit call. user doesn't call Dispose(), so this object is added into DisposeQueue to be disposed automatically.
+ if (!isDisposeQueued)
+ {
+ isDisposeQueued = true;
+ DisposeQueue.Instance.Add(this);
+ }
}
+
+ // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
+ // TODO: set large fields to null.
+ base.Dispose(disposing);
}
/// <inheritdoc/>
if (type == DisposeTypes.Explicit)
{
- // Called by User
- // Release your own managed resources here.
- // You should release all of your own disposable objects here.
-
+ //Called by User
+ //Release your own managed resources here.
+ //You should release all of your own disposable objects here.
}
- // Release your own unmanaged resources here.
- // You should not access any managed member here except static instance.
- // because the execution order of Finalizes is non-deterministic.
+ //Release your own unmanaged resources here.
+ //You should not access any managed member here except static instance.
+ //because the execution order of Finalizes is non-deterministic.
if (swigCPtr.Handle != global::System.IntPtr.Zero)
{
if (swigCMemOwn)
{
swigCMemOwn = false;
- Interop.FlexLayout.delete_FlexLayout(swigCPtr);
+ ReleaseSwigCPtr(swigCPtr);
}
swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
}
+
disposed = true;
}
/// <summary>
- /// Creates a FlexLayout object.
+ /// Hidden API (Inhouse API).
+ /// Release swigCPtr.
/// </summary>
- /// <since_tizen> 6 </since_tizen>
- public FlexLayout() : this(Interop.FlexLayout.FlexLayout_New(), true)
- {
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
- }
-
- internal static FlexLayout DownCast(BaseHandle handle)
+ /// This will not be public opened.
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected virtual void ReleaseSwigCPtr(System.Runtime.InteropServices.HandleRef swigCPtr)
{
- FlexLayout ret = new FlexLayout(Interop.FlexLayout.FlexLayout_DownCast(BaseHandle.getCPtr(handle)), true);
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
- return ret;
+ Interop.FlexLayout.DeleteFlexLayout(swigCPtr);
}
- internal FlexLayout(FlexLayout other) : this(Interop.FlexLayout.new_FlexLayout__SWIG_1(FlexLayout.getCPtr(other)), true)
+ /// <summary>
+ /// Creates a FlexLayout object.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public FlexLayout() : this(Interop.FlexLayout.New(), true)
{
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
internal FlexLayout.AlignmentType GetFlexAlignment()
{
- FlexLayout.AlignmentType ret = (FlexLayout.AlignmentType)Interop.FlexLayout.FlexLayout_GetFlexAlignment(swigCPtr);
+ FlexLayout.AlignmentType ret = (FlexLayout.AlignmentType)Interop.FlexLayout.GetFlexAlignment(swigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return ret;
}
internal FlexLayout.AlignmentType GetFlexItemsAlignment()
{
- FlexLayout.AlignmentType ret = (FlexLayout.AlignmentType)Interop.FlexLayout.FlexLayout_GetFlexItemsAlignment(swigCPtr);
+ FlexLayout.AlignmentType ret = (FlexLayout.AlignmentType)Interop.FlexLayout.GetFlexItemsAlignment(swigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return ret;
}
/// <since_tizen> 6 </since_tizen>
public FlexDirection Direction
{
- get => (FlexDirection)Interop.FlexLayout.FlexLayout_GetFlexDirection(swigCPtr);
+ get => (FlexDirection)Interop.FlexLayout.GetFlexDirection(swigCPtr);
set
{
if (value < FlexDirection.Column || value > FlexDirection.RowReverse)
throw new InvalidEnumArgumentException(nameof(Direction));
- Interop.FlexLayout.FlexLayout_SetFlexDirection(swigCPtr, (int)value);
+ Interop.FlexLayout.SetFlexDirection(swigCPtr, (int)value);
RequestLayout();
}
}
/// <since_tizen> 6 </since_tizen>
public FlexJustification Justification
{
- get => (FlexJustification)Interop.FlexLayout.FlexLayout_GetFlexJustification(swigCPtr);
+ get => (FlexJustification)Interop.FlexLayout.GetFlexJustification(swigCPtr);
set
{
- if (value < FlexJustification.FlexStart || value > FlexJustification.SpaceAround)
+ if (value < FlexJustification.FlexStart || value > FlexJustification.SpaceEvenly)
throw new InvalidEnumArgumentException(nameof(Justification));
- Interop.FlexLayout.FlexLayout_SetFlexJustification(swigCPtr, (int)value);
+ Interop.FlexLayout.SetFlexJustification(swigCPtr, (int)value);
RequestLayout();
}
}
/// <since_tizen> 6 </since_tizen>
public FlexWrapType WrapType
{
- get => (FlexWrapType)Interop.FlexLayout.FlexLayout_GetFlexWrap(swigCPtr);
+ get => (FlexWrapType)Interop.FlexLayout.GetFlexWrap(swigCPtr);
set
{
if (value != FlexWrapType.NoWrap && value != FlexWrapType.Wrap)
throw new InvalidEnumArgumentException(nameof(WrapType));
- Interop.FlexLayout.FlexLayout_SetFlexWrap(swigCPtr, (int)value);
+ Interop.FlexLayout.SetFlexWrap(swigCPtr, (int)value);
RequestLayout();
}
if (value < AlignmentType.Auto || value > AlignmentType.Stretch)
throw new InvalidEnumArgumentException(nameof(Alignment));
- Interop.FlexLayout.FlexLayout_SetFlexAlignment(swigCPtr, (int)value);
+ Interop.FlexLayout.SetFlexAlignment(swigCPtr, (int)value);
RequestLayout();
}
}
if (value < AlignmentType.Auto || value > AlignmentType.Stretch)
throw new InvalidEnumArgumentException(nameof(ItemsAlignment));
- Interop.FlexLayout.FlexLayout_SetFlexItemsAlignment(swigCPtr, (int)value);
+ Interop.FlexLayout.SetFlexItemsAlignment(swigCPtr, (int)value);
RequestLayout();
}
}
/// </summary>
SpaceBetween,
/// <summary>
+ /// Items are positioned with equal space before, and after the lines.<br/>
+ /// </summary>
+ SpaceAround,
+ /// <summary>
/// Items are positioned with equal space before, between, and after the lines.<br/>
- /// Compared to <see cref="FlexJustification.SpaceBetween"/> using <see cref="FlexJustification.SpaceAround"/>
- /// will result in space being distributed to the beginning of the first child and end of the last child.
+ /// Spaces are distributed equally to the beginning of the first child, between each child, and the end of the last child.
/// </summary>
- SpaceAround
+ /// <since_tizen> 9 </since_tizen>
+ SpaceEvenly
}
/// <summary>
{
// We need to measure child layout
View child = Registry.GetManagedBaseHandleFromNativePtr(childPtr) as View;
- if (child == null)
+ // independent child will be measured in LayoutGroup.OnMeasureIndependentChildren().
+ if ((child == null) || (child?.ExcludeLayouting ?? true))
{
measureSize.width = 0;
measureSize.height = 0;
}
LayoutItem childLayout = child.Layout;
+ Extents margin = child.Margin;
MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(
new MeasureSpecification(
- new LayoutLength(parentMeasureSpecificationWidth.Size - (child.Margin.Start + child.Margin.End)),
+ new LayoutLength(parentMeasureSpecificationWidth.Size - (margin.Start + margin.End)),
parentMeasureSpecificationWidth.Mode),
new LayoutLength(Padding.Start + Padding.End),
new LayoutLength(child.WidthSpecification));
MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(
new MeasureSpecification(
- new LayoutLength(parentMeasureSpecificationHeight.Size - (child.Margin.Top + child.Margin.Bottom)),
+ new LayoutLength(parentMeasureSpecificationHeight.Size - (margin.Top + margin.Bottom)),
parentMeasureSpecificationHeight.Mode),
new LayoutLength(Padding.Top + Padding.Bottom),
new LayoutLength(child.HeightSpecification));
- childLayout.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ childLayout?.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
- measureSize.width = childLayout.MeasuredWidth.Size.AsRoundedValue();
- measureSize.height = childLayout.MeasuredHeight.Size.AsRoundedValue();
+ measureSize.width = (childLayout == null) ? 0 : childLayout.MeasuredWidth.Size.AsRoundedValue();
+ measureSize.height = (childLayout == null) ? 0 : childLayout.MeasuredHeight.Size.AsRoundedValue();
}
void InsertChild(LayoutItem child)
{
// Store created node for child
- IntPtr childPtr = Interop.FlexLayout.FlexLayout_AddChildWithMargin(swigCPtr, View.getCPtr(child.Owner), Extents.getCPtr(child.Owner.Margin), measureChildDelegate, LayoutChildren.Count - 1);
+ IntPtr childPtr = Interop.FlexLayout.AddChildWithMargin(swigCPtr, View.getCPtr(child.Owner), Extents.getCPtr(child.Owner.Margin), measureChildDelegate, LayoutChildren.Count - 1);
HandleRef childHandleRef = new HandleRef(child.Owner, childPtr);
SetAttachedValue(child.Owner, FlexItemProperty, childHandleRef);
}
/// Derived classes can use this to set their own child properties on the child layout's owner.<br />
/// </summary>
/// <param name="child">The Layout child.</param>
+ /// <exception cref="ArgumentNullException"> Thrown when child is null. </exception>
/// <since_tizen> 6 </since_tizen>
protected override void OnChildAdd(LayoutItem child)
{
+ if (null == child)
+ {
+ throw new ArgumentNullException(nameof(child));
+ }
InsertChild(child);
}
/// Callback when child is removed from container.<br />
/// </summary>
/// <param name="child">The Layout child.</param>
+ /// <exception cref="ArgumentNullException"> Thrown when child is null. </exception>
/// <since_tizen> 6 </since_tizen>
protected override void OnChildRemove(LayoutItem child)
{
+ if (null == child)
+ {
+ throw new ArgumentNullException(nameof(child));
+ }
// When child View is removed from it's parent View (that is a Layout) then remove it from the layout too.
// FlexLayout refers to the child as a View not LayoutItem.
- Interop.FlexLayout.FlexLayout_RemoveChild(swigCPtr, child);
+ Interop.FlexLayout.RemoveChild(swigCPtr, child.Owner.SwigCPtr);
}
/// <summary>
{
bool isLayoutRtl = Owner.LayoutDirection == ViewLayoutDirectionType.RTL;
Extents padding = Owner.Padding;
- Extents margin = Owner.Margin;
- Interop.FlexLayout.FlexLayout_SetMargin(swigCPtr, Extents.getCPtr(margin));
- Interop.FlexLayout.FlexLayout_SetPadding(swigCPtr, Extents.getCPtr(padding));
+ Interop.FlexLayout.SetPadding(swigCPtr, Extents.getCPtr(padding));
float width = FlexUndefined; // Behaves as WrapContent (Flex Auto)
float height = FlexUndefined; // Behaves as WrapContent (Flex Auto)
- if (widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || widthMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost)
+ if (widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly)
{
width = widthMeasureSpec.Size.AsDecimal();
}
- if (heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly || heightMeasureSpec.Mode == MeasureSpecification.ModeType.AtMost)
+ if (heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly)
{
height = heightMeasureSpec.Size.AsDecimal();
}
parentMeasureSpecificationWidth = widthMeasureSpec;
parentMeasureSpecificationHeight = heightMeasureSpec;
+ Extents zeroMargin = new Extents();
+
// Assign child properties
for (int i = 0; i < LayoutChildren.Count; i++)
{
if (childHandleRef.Handle == IntPtr.Zero || Child == null)
continue;
- if (!layoutItem.Owner.ExcludeLayouting)
- {
- SetFlexPositionType(Child, PositionType.Absolute);
- Interop.FlexLayout.FlexLayout_SetFlexPositionType(childHandleRef, (int)PositionType.Absolute);
- MeasureChildWithoutPadding(layoutItem, widthMeasureSpec, heightMeasureSpec);
- continue;
- }
- else
- {
- SetFlexPositionType(Child, PositionType.Relative);
- Interop.FlexLayout.FlexLayout_SetFlexPositionType(childHandleRef, (int)PositionType.Relative);
- }
-
AlignmentType flexAlignemnt = GetFlexAlignmentSelf(Child);
+ PositionType positionType = GetFlexPositionType(Child);
float flexAspectRatio = GetFlexAspectRatio(Child);
float flexBasis = GetFlexBasis(Child);
float flexShrink = GetFlexShrink(Child);
float flexGrow = GetFlexGrow(Child);
-
- Interop.FlexLayout.FlexLayout_SetFlexAlignmentSelf(childHandleRef, (int)flexAlignemnt);
- Interop.FlexLayout.FlexLayout_SetFlexAspectRatio(childHandleRef, flexAspectRatio);
- Interop.FlexLayout.FlexLayout_SetFlexBasis(childHandleRef, flexBasis);
- Interop.FlexLayout.FlexLayout_SetFlexShrink(childHandleRef, flexShrink);
- Interop.FlexLayout.FlexLayout_SetFlexGrow(childHandleRef, flexGrow);
+ Extents childMargin = Child.ExcludeLayouting ? zeroMargin : layoutItem.Margin;
+
+ Interop.FlexLayout.SetMargin(childHandleRef, Extents.getCPtr(childMargin));
+ Interop.FlexLayout.SetFlexAlignmentSelf(childHandleRef, (int)flexAlignemnt);
+ Interop.FlexLayout.SetFlexPositionType(childHandleRef, (int)positionType);
+ Interop.FlexLayout.SetFlexAspectRatio(childHandleRef, flexAspectRatio);
+ Interop.FlexLayout.SetFlexBasis(childHandleRef, flexBasis);
+ Interop.FlexLayout.SetFlexShrink(childHandleRef, flexShrink);
+ Interop.FlexLayout.SetFlexGrow(childHandleRef, flexGrow);
}
- Interop.FlexLayout.FlexLayout_CalculateLayout(swigCPtr, width, height, isLayoutRtl);
+ Interop.FlexLayout.CalculateLayout(swigCPtr, width, height, isLayoutRtl);
+ zeroMargin.Dispose();
- LayoutLength flexLayoutWidth = new LayoutLength(Interop.FlexLayout.FlexLayout_GetWidth(swigCPtr));
- LayoutLength flexLayoutHeight = new LayoutLength(Interop.FlexLayout.FlexLayout_GetHeight(swigCPtr));
+ LayoutLength flexLayoutWidth = new LayoutLength(Interop.FlexLayout.GetWidth(swigCPtr));
+ LayoutLength flexLayoutHeight = new LayoutLength(Interop.FlexLayout.GetHeight(swigCPtr));
- Debug.WriteLineIf(LayoutDebugFlex, "FlexLayout OnMeasure width:" + flexLayoutWidth.AsRoundedValue()
- + " height:" + flexLayoutHeight.AsRoundedValue());
+ NUILog.Debug("FlexLayout OnMeasure width:" + flexLayoutWidth.AsRoundedValue() + " height:" + flexLayoutHeight.AsRoundedValue());
SetMeasuredDimensions(GetDefaultSize(flexLayoutWidth, widthMeasureSpec),
GetDefaultSize(flexLayoutHeight, heightMeasureSpec));
LayoutLength height = bottom - top;
// Call to FlexLayout implementation to calculate layout values for later retrieval.
- Interop.FlexLayout.FlexLayout_CalculateLayout(swigCPtr, width.AsDecimal(), height.AsDecimal(), isLayoutRtl);
+ Interop.FlexLayout.CalculateLayout(swigCPtr, width.AsDecimal(), height.AsDecimal(), isLayoutRtl);
- int count = LayoutChildren.Count;
- for (int childIndex = 0; childIndex < count; childIndex++)
+ for (int childIndex = 0; childIndex < LayoutChildren.Count; childIndex++)
{
LayoutItem childLayout = LayoutChildren[childIndex];
- if (childLayout != null)
+ if (!childLayout?.Owner?.ExcludeLayouting ?? false)
{
- if (childLayout.Owner.ExcludeLayouting)
- {
- // Get the frame for the child, start, top, end, bottom.
- Vector4 frame = new Vector4(Interop.FlexLayout.FlexLayout_GetNodeFrame(swigCPtr, childIndex), true);
- childLayout.Layout(new LayoutLength(frame.X), new LayoutLength(frame.Y), new LayoutLength(frame.Z), new LayoutLength(frame.W));
- }
+ // Get the frame for the child, start, top, end, bottom.
+ Vector4 frame = new Vector4(Interop.FlexLayout.GetNodeFrame(swigCPtr, childIndex), true);
+
+ // Child view's size is calculated in OnLayout() without considering child layout's measured size unlike other layouts' OnLayout().
+ // This causes that the grand child view's size is calculated incorrectly if the child and grand child have MatchParent Specification.
+ // e.g. Let parent view's width be 200 and parent has 2 children.
+ // Then, child layout's measured width becomes 200 and child view's width becomes 100. (by dali-toolkit's YOGA APIs)
+ // Then, grand child layout's measured width becomes 200 and grand child view's width becomes 200. (by NUI Layout)
+ //
+ // To resolve the above issue, child layout's measured size is set with the child view's size calculated by dali-toolkit's YOGA APIs.
+ MeasureSpecification widthSpec = new MeasureSpecification(new LayoutLength(frame.Z - frame.X), MeasureSpecification.ModeType.Exactly);
+ MeasureSpecification heightSpec = new MeasureSpecification(new LayoutLength(frame.W - frame.Y), MeasureSpecification.ModeType.Exactly);
+ MeasureChildWithoutPadding(childLayout, widthSpec, heightSpec);
+
+ childLayout.Layout(new LayoutLength(frame.X), new LayoutLength(frame.Y), new LayoutLength(frame.Z), new LayoutLength(frame.W));
+ frame.Dispose();
}
}
- LayoutForIndependentChild();
}
} // FLexlayout
} // namesspace Tizen.NUI