-/* 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
[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.
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)
{
- Tizen.Log.Error("NUI", "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.DeleteFlexLayout(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.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.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.NewFlexLayout(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();
}
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.SetFlexJustification(swigCPtr, (int)value);
/// </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;
// independent child will be measured in LayoutGroup.OnMeasureIndependentChildren().
- if (child?.ExcludeLayouting ?? true)
+ 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)
/// 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.RemoveChild(swigCPtr, child);
+ Interop.FlexLayout.RemoveChild(swigCPtr, child.Owner.SwigCPtr);
}
/// <summary>
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();
}
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));
{
// 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();
}
}
}