2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 using System.Diagnostics;
21 using Tizen.NUI.BaseComponents;
22 using System.ComponentModel;
28 enum LayoutFlags : short
33 MeasuredDimensionSet = 4
37 /// [Draft] Base class for layouts. It is used to layout a View
38 /// It can be laid out by a LayoutGroup.
40 public class LayoutItem : Disposable
42 static bool LayoutDebugFrameData = false; // Debug flag
43 private MeasureSpecification OldWidthMeasureSpec; // Store measure specification to compare against later
44 private MeasureSpecification OldHeightMeasureSpec;// Store measure specification to compare against later
46 private LayoutFlags Flags = LayoutFlags.None;
48 private ILayoutParent Parent;
50 LayoutData layoutPositionData;
52 private Extents padding;
53 private Extents margin;
55 private bool parentReplacement = false;
58 /// [Draft] Condition event that is causing this Layout to transition.
60 internal TransitionCondition ConditionForAnimation { get; set; }
63 /// [Draft] The View that this Layout has been assigned to.
65 /// <since_tizen> 6 </since_tizen>
66 public View Owner { get; set; } // Should not keep a View alive.
69 /// [Draft] Use transition for layouting child
71 /// <since_tizen> 6 </since_tizen>
72 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
73 [EditorBrowsable(EditorBrowsableState.Never)]
74 public bool LayoutWithTransition { get; set; }
77 /// [Draft] Set position by layouting result
79 [EditorBrowsable(EditorBrowsableState.Never)]
80 public bool SetPositionByLayout { get; set; } = true;
83 /// [Draft] Margin for this LayoutItem
85 /// <since_tizen> 6 </since_tizen>
100 /// [Draft] Padding for this LayoutItem
102 /// <since_tizen> 6 </since_tizen>
103 public Extents Padding
117 /// [Draft] Constructor
119 /// <since_tizen> 6 </since_tizen>
126 /// destructor. This is HiddenAPI. recommended not to use in public.
128 [EditorBrowsable(EditorBrowsableState.Never)]
135 /// [Draft] Set parent to this layout.
137 /// <param name="parent">Parent to set on this Layout.</param>
138 internal void SetParent(ILayoutParent parent)
140 Parent = parent as LayoutGroup;
144 /// Unparent this layout from it's owner, and remove any layout children in derived types. <br />
146 internal void Unparent()
148 // Enable directly derived types to first remove children
151 // Remove myself from parent
152 Parent?.Remove(this);
154 // Remove parent reference
157 // Lastly, clear layout from owning View.
158 Owner?.ResetLayout();
161 private void Initialize()
163 LayoutWithTransition = false;
164 layoutPositionData = new LayoutData(this, TransitionCondition.Unspecified, 0, 0, 0, 0);
165 padding = new Extents(0, 0, 0, 0);
166 margin = new Extents(0, 0, 0, 0);
170 /// Initialize the layout and allow derived classes to also perform any operations
172 /// <param name="owner">Owner of this Layout.</param>
173 internal void AttachToOwner(View owner)
175 // Assign the layout owner.
178 // Add layout to parent layout if a layout container
179 View parent = Owner.GetParent() as View;
180 (parent?.Layout as LayoutGroup)?.Add(this);
182 // If Add or ChangeOnAdd then do not update condition
183 if (ConditionForAnimation.Equals(TransitionCondition.Unspecified))
185 ConditionForAnimation = TransitionCondition.LayoutChanged;
190 /// This is called to find out how big a layout should be. <br />
191 /// The parent supplies constraint information in the width and height parameters. <br />
192 /// The actual measurement work of a layout is performed in OnMeasure called by this
193 /// method. Therefore, only OnMeasure can and must be overridden by subclasses. <br />
195 /// <param name="widthMeasureSpec"> Horizontal space requirements as imposed by the parent.</param>
196 /// <param name="heightMeasureSpec">Vertical space requirements as imposed by the parent.</param>
197 /// <since_tizen> 6 </since_tizen>
198 public void Measure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
200 // Check if relayouting is required.
201 bool specChanged = (widthMeasureSpec.Size != OldWidthMeasureSpec.Size) ||
202 (heightMeasureSpec.Size != OldHeightMeasureSpec.Size) ||
203 (widthMeasureSpec.Mode != OldWidthMeasureSpec.Mode) ||
204 (heightMeasureSpec.Mode != OldHeightMeasureSpec.Mode);
206 bool isSpecExactly = (widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly) &&
207 (heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly);
209 bool matchesSpecSize = (MeasuredWidth.Size == widthMeasureSpec.Size) &&
210 (MeasuredHeight.Size == heightMeasureSpec.Size);
212 bool needsLayout = specChanged && (!isSpecExactly || !matchesSpecSize);
213 needsLayout = needsLayout || ((Flags & LayoutFlags.ForceLayout) == LayoutFlags.ForceLayout);
217 OnMeasure(widthMeasureSpec, heightMeasureSpec);
218 Flags = Flags | LayoutFlags.LayoutRequired;
219 Flags &= ~LayoutFlags.ForceLayout;
221 OldWidthMeasureSpec = widthMeasureSpec;
222 OldHeightMeasureSpec = heightMeasureSpec;
226 /// Assign a size and position to a layout and all of its descendants. <br />
227 /// This is the second phase of the layout mechanism. (The first is measuring). In this phase, each parent
228 /// calls layout on all of its children to position them. This is typically done using the child<br />
229 /// measurements that were stored in the measure pass.<br />
231 /// <param name="left">Left position, relative to parent.</param>
232 /// <param name="top">Top position, relative to parent.</param>
233 /// <param name="right">Right position, relative to parent.</param>
234 /// <param name="bottom">Bottom position, relative to parent.</param>
235 /// <since_tizen> 6 </since_tizen>
236 public void Layout(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
238 bool changed = SetFrame(left.AsRoundedValue(),
239 top.AsRoundedValue(),
240 right.AsRoundedValue(),
241 bottom.AsRoundedValue());
243 // Check if Measure needed before Layouting
244 if (changed || ((Flags & LayoutFlags.LayoutRequired) == LayoutFlags.LayoutRequired))
246 OnLayout(changed, left, top, right, bottom);
248 Flags &= ~LayoutFlags.LayoutRequired;
253 /// Utility to return a default size.<br />
254 /// Uses the supplied size if the MeasureSpecification imposed no constraints. Will get larger if allowed by the
255 /// MeasureSpecification.<br />
257 /// <param name="size"> Default size for this layout.</param>
258 /// <param name="measureSpecification"> Constraints imposed by the parent.</param>
259 /// <returns>The size this layout should be.</returns>
260 /// <since_tizen> 6 </since_tizen>
261 public static LayoutLength GetDefaultSize(LayoutLength size, MeasureSpecification measureSpecification)
263 LayoutLength result = size;
264 MeasureSpecification.ModeType specMode = measureSpecification.Mode;
265 LayoutLength specSize = measureSpecification.Size;
269 case MeasureSpecification.ModeType.Unspecified:
274 case MeasureSpecification.ModeType.AtMost:
276 // Ensure the default size does not exceed the spec size unless the default size is 0.
277 // Another container could provide a default size of 0.
279 // Do not set size to 0, use specSize in this case as could be a legacy container
280 if ((size.AsDecimal() < specSize.AsDecimal()) && (size.AsDecimal() > 0))
290 case MeasureSpecification.ModeType.Exactly:
301 /// Get the Layouts parent
303 /// <returns>Layout parent with an LayoutParent interface</returns>
304 /// <since_tizen> 6 </since_tizen>
305 public ILayoutParent GetParent()
311 /// Request that this layout is re-laid out.<br />
312 /// This will make this layout and all it's parent layouts dirty.<br />
314 /// <since_tizen> 6 </since_tizen>
315 public void RequestLayout()
317 Flags = Flags | LayoutFlags.ForceLayout;
320 LayoutGroup layoutGroup = Parent as LayoutGroup;
321 if (!layoutGroup.LayoutRequested)
323 layoutGroup.RequestLayout();
330 /// Predicate to determine if this layout has been requested to re-layout.<br />
333 internal bool LayoutRequested
337 return (Flags & LayoutFlags.ForceLayout) == LayoutFlags.ForceLayout;
341 internal void SetReplaceFlag()
343 parentReplacement = true;
346 internal bool IsReplaceFlag()
348 return parentReplacement;
351 internal void ClearReplaceFlag()
353 parentReplacement = false;
357 /// Get the measured width (without any measurement flags).<br />
358 /// This method should be used only during measurement and layout calculations.<br />
360 /// <since_tizen> 6 </since_tizen>
361 public MeasuredSize MeasuredWidth { get; set; } = new MeasuredSize(new LayoutLength(-3), MeasuredSize.StateType.MeasuredSizeOK);
364 /// Get the measured height (without any measurement flags).<br />
365 /// This method should be used only during measurement and layout calculations.<br />
367 /// <since_tizen> 6 </since_tizen>
368 public MeasuredSize MeasuredHeight { get; set; } = new MeasuredSize(new LayoutLength(-3), MeasuredSize.StateType.MeasuredSizeOK);
371 /// Returns the suggested minimum width that the layout should use.<br />
372 /// This returns the maximum of the layout's minimum width and the owner's natural width.<br />
374 /// <since_tizen> 6 </since_tizen>
375 public LayoutLength SuggestedMinimumWidth
379 float maximumWidth = Owner.MaximumSize.Width;
380 float minimumWidth = Owner.MinimumSize.Width;
382 float baseHeight = Owner.MaximumSize.Height > 0 ? Math.Min(Owner.MaximumSize.Height, Owner.NaturalSize.Height) : Owner.NaturalSize.Height;
383 float baseWidth = Owner.GetWidthForHeight(baseHeight);
385 float result = minimumWidth > 0 ? Math.Max(baseWidth, minimumWidth) : baseWidth;
386 result = maximumWidth > 0 ? Math.Min(result, maximumWidth) : result;
388 return new LayoutLength(result);
393 /// Returns the suggested minimum height that the layout should use.<br />
394 /// This returns the maximum of the layout's minimum height and the owner's natural height.<br />
396 /// <since_tizen> 6 </since_tizen>
397 public LayoutLength SuggestedMinimumHeight
401 float maximumHeight = Owner.MaximumSize.Height;
402 float minimumHeight = Owner.MinimumSize.Height;
404 float baseWidth = Owner.MaximumSize.Width > 0 ? Math.Min(Owner.MaximumSize.Width, Owner.NaturalSize.Width) : Owner.NaturalSize.Width;
405 float baseHeight = Owner.GetHeightForWidth(baseWidth);
407 float result = minimumHeight > 0 ? Math.Max(baseHeight, minimumHeight) : baseHeight;
408 result = maximumHeight > 0 ? Math.Min(result, maximumHeight) : result;
410 return new LayoutLength(result);
415 /// Sets the minimum width of the layout.<br />
416 /// It is not guaranteed the layout will be able to achieve this minimum width (for example, if its parent
417 /// layout constrains it with less available width).<br />
418 /// 1. if the owner's View.WidthSpecification has exact value, then that value overrides the minimum size.<br />
419 /// 2. If the owner's View.WidthSpecification is set to View.LayoutParamPolicies.WrapContent, then the view's width is set based on the suggested minimum width. (@see GetSuggestedMinimumWidth()).<br />
420 /// 3. If the owner's View.WidthSpecification is set to View.LayoutParamPolicies.MatchParent, then the parent width takes precedence over the minimum width.<br />
422 internal LayoutLength MinimumWidth { get; set; }
425 /// Sets the minimum height of the layout.<br />
426 /// It is not guaranteed the layout will be able to achieve this minimum height (for example, if its parent
427 /// layout constrains it with less available height).<br />
428 /// 1. if the owner's View.HeightSpecification has exact value, then that value overrides the minimum size.<br />
429 /// 2. If the owner's View.HeightSpecification is set to View.LayoutParamPolicies.WrapContent, then the view's height is set based on the suggested minimum height. (@see GetSuggestedMinimumHeight()).<br />
430 /// 3. If the owner's View.HeightSpecification is set to View.LayoutParamPolicies.MatchParent, then the parent height takes precedence over the minimum height.<br />
432 internal LayoutLength MinimumHeight { get; set; }
435 /// Utility to reconcile a desired size and state, with constraints imposed by a MeasureSpecification.
437 /// <param name="size"> How big the layout wants to be.</param>
438 /// <param name="measureSpecification"> Constraints imposed by the parent.</param>
439 /// <param name="childMeasuredState"> Size information bit mask for the layout's children.</param>
440 /// <returns> A measured size, which may indicate that it is too small. </returns>
441 /// <since_tizen> 6 </since_tizen>
442 protected MeasuredSize ResolveSizeAndState(LayoutLength size, MeasureSpecification measureSpecification, MeasuredSize.StateType childMeasuredState)
444 var specMode = measureSpecification.Mode;
445 LayoutLength specSize = measureSpecification.Size;
446 MeasuredSize result = new MeasuredSize(size, childMeasuredState);
450 case MeasureSpecification.ModeType.AtMost:
452 if (specSize.AsRoundedValue() < size.AsRoundedValue())
454 result = new MeasuredSize(specSize, MeasuredSize.StateType.MeasuredSizeTooSmall);
459 case MeasureSpecification.ModeType.Exactly:
461 result.Size = specSize;
465 case MeasureSpecification.ModeType.Unspecified:
475 /// This method must be called by OnMeasure(MeasureSpec,MeasureSpec) to store the measured width and measured height.
477 /// <param name="measuredWidth">The measured width of this layout.</param>
478 /// <param name="measuredHeight">The measured height of this layout.</param>
479 /// <since_tizen> 6 </since_tizen>
480 protected void SetMeasuredDimensions(MeasuredSize measuredWidth, MeasuredSize measuredHeight)
482 MeasuredWidth = measuredWidth;
483 MeasuredHeight = measuredHeight;
484 Flags = Flags | LayoutFlags.MeasuredDimensionSet;
488 /// Measure the layout and its content to determine the measured width and the
489 /// measured height.<br />
490 /// The base class implementation of measure defaults to the background size,
491 /// unless a larger size is allowed by the MeasureSpec. Subclasses should
492 /// override to provide better measurements of their content.<br />
493 /// If this method is overridden, it is the subclass's responsibility to make sure the
494 /// measured height and width are at least the layout's minimum height and width.<br />
496 /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
497 /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
498 /// <since_tizen> 6 </since_tizen>
499 protected virtual void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
501 // GetDefaultSize will limit the MeasureSpec to the suggested minimumWidth and minimumHeight
502 SetMeasuredDimensions(GetDefaultSize(SuggestedMinimumWidth, widthMeasureSpec),
503 GetDefaultSize(SuggestedMinimumHeight, heightMeasureSpec));
507 /// Called from Layout() when this layout should assign a size and position to each of its children. <br />
508 /// Derived classes with children should override this method and call Layout() on each of their children. <br />
510 /// <param name="changed">This is a new size or position for this layout.</param>
511 /// <param name="left">Left position, relative to parent.</param>
512 /// <param name="top">Top position, relative to parent.</param>
513 /// <param name="right">Right position, relative to parent.</param>
514 /// <param name="bottom">Bottom position, relative to parent.</param>
515 /// <since_tizen> 6 </since_tizen>
516 protected virtual void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
521 /// Virtual method to allow derived classes to remove any children before it is removed from
524 /// <since_tizen> 6 </since_tizen>
525 protected virtual void OnUnparent()
530 /// Virtual method called when this Layout is attached to it's owner.
531 /// Allows derived layouts to take ownership of child Views and connect to any Owner signals required.
533 /// <since_tizen> 6 </since_tizen>
534 protected virtual void OnAttachedToOwner()
538 private bool SetFrame(float left, float top, float right, float bottom)
540 bool changed = false;
542 if (layoutPositionData.Left != left ||
543 layoutPositionData.Right != right ||
544 layoutPositionData.Top != top ||
545 layoutPositionData.Bottom != bottom)
549 float oldWidth = layoutPositionData.Right - layoutPositionData.Left;
550 float oldHeight = layoutPositionData.Bottom - layoutPositionData.Top;
551 float newWidth = right - left;
552 float newHeight = bottom - top;
553 bool sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
555 // Set condition to layout changed as currently unspecified. Add, Remove would have specified a condition.
556 if (ConditionForAnimation.Equals(TransitionCondition.Unspecified))
558 ConditionForAnimation = TransitionCondition.LayoutChanged;
561 // Store new layout position data
562 layoutPositionData = new LayoutData(this, ConditionForAnimation, left, top, right, bottom);
564 Debug.WriteLineIf(LayoutDebugFrameData, "LayoutItem FramePositionData View:" + layoutPositionData.Item.Owner.Name +
565 " left:" + layoutPositionData.Left +
566 " top:" + layoutPositionData.Top +
567 " right:" + layoutPositionData.Right +
568 " bottom:" + layoutPositionData.Bottom);
570 if (Owner.Parent != null && Owner.Parent.Layout != null && Owner.Parent.Layout.LayoutWithTransition)
572 NUIApplication.GetDefaultWindow().LayoutController.AddTransitionDataEntry(layoutPositionData);
576 Owner.SetSize(right - left, bottom - top, Owner.Position.Z);
577 if (SetPositionByLayout)
579 Owner.SetPosition(left, top, Owner.Position.Z);
584 // Reset condition for animation ready for next transition when required.
585 ConditionForAnimation = TransitionCondition.Unspecified;
592 /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
594 // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
595 [EditorBrowsable(EditorBrowsableState.Never)]
596 protected override void Dispose(DisposeTypes type)