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
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 /// [Draft] Set parent to this layout.
128 /// <param name="parent">Parent to set on this Layout.</param>
129 internal void SetParent( ILayoutParent parent)
131 Parent = parent as LayoutGroup;
135 /// Unparent this layout from it's owner, and remove any layout children in derived types. <br />
137 internal void Unparent()
139 // Enable directly derived types to first remove children
142 // Remove myself from parent
143 Parent?.Remove( this );
145 // Remove parent reference
148 // Lastly, clear layout from owning View.
149 Owner?.ResetLayout();
152 private void Initialize()
154 LayoutWithTransition = false;
155 _layoutPositionData = new LayoutData(this,TransitionCondition.Unspecified,0,0,0,0);
156 _padding = new Extents(0,0,0,0);
157 _margin = new Extents(0,0,0,0);
161 /// Initialize the layout and allow derived classes to also perform any operations
163 /// <param name="owner">Owner of this Layout.</param>
164 internal void AttachToOwner(View owner)
166 // Assign the layout owner.
169 // Add layout to parent layout if a layout container
170 View parent = Owner.GetParent() as View;
171 (parent?.Layout as LayoutGroup)?.Add( this );
173 // If Add or ChangeOnAdd then do not update condition
174 if (ConditionForAnimation.Equals(TransitionCondition.Unspecified))
176 ConditionForAnimation = TransitionCondition.LayoutChanged;
181 /// This is called to find out how big a layout should be. <br />
182 /// The parent supplies constraint information in the width and height parameters. <br />
183 /// The actual measurement work of a layout is performed in OnMeasure called by this
184 /// method. Therefore, only OnMeasure can and must be overridden by subclasses. <br />
186 /// <param name="widthMeasureSpec"> Horizontal space requirements as imposed by the parent.</param>
187 /// <param name="heightMeasureSpec">Vertical space requirements as imposed by the parent.</param>
188 /// <since_tizen> 6 </since_tizen>
189 public void Measure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
191 // Check if relayouting is required.
192 bool specChanged = (widthMeasureSpec.Size != OldWidthMeasureSpec.Size) ||
193 (heightMeasureSpec.Size != OldHeightMeasureSpec.Size) ||
194 (widthMeasureSpec.Mode != OldWidthMeasureSpec.Mode) ||
195 (heightMeasureSpec.Mode != OldHeightMeasureSpec.Mode);
197 bool isSpecExactly = (widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly) &&
198 (heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly);
200 bool matchesSpecSize = (MeasuredWidth.Size == widthMeasureSpec.Size) &&
201 (MeasuredHeight.Size == heightMeasureSpec.Size);
203 bool needsLayout = specChanged && ( !isSpecExactly || !matchesSpecSize);
204 needsLayout = needsLayout || ((Flags & LayoutFlags.ForceLayout) == LayoutFlags.ForceLayout);
208 OnMeasure(widthMeasureSpec, heightMeasureSpec);
209 Flags = Flags | LayoutFlags.LayoutRequired;
210 Flags &= ~LayoutFlags.ForceLayout;
212 OldWidthMeasureSpec = widthMeasureSpec;
213 OldHeightMeasureSpec = heightMeasureSpec;
217 /// Assign a size and position to a layout and all of its descendants. <br />
218 /// This is the second phase of the layout mechanism. (The first is measuring). In this phase, each parent
219 /// calls layout on all of its children to position them. This is typically done using the child<br />
220 /// measurements that were stored in the measure pass.<br />
222 /// <param name="left">Left position, relative to parent.</param>
223 /// <param name="top">Top position, relative to parent.</param>
224 /// <param name="right">Right position, relative to parent.</param>
225 /// <param name="bottom">Bottom position, relative to parent.</param>
226 /// <since_tizen> 6 </since_tizen>
227 public void Layout(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
229 bool changed = SetFrame(left.AsRoundedValue(),
230 top.AsRoundedValue(),
231 right.AsRoundedValue(),
232 bottom.AsRoundedValue());
234 // Check if Measure needed before Layouting
235 if (changed || ((Flags & LayoutFlags.LayoutRequired) == LayoutFlags.LayoutRequired))
237 OnLayout(changed, left, top, right, bottom);
239 Flags &= ~LayoutFlags.LayoutRequired;
244 /// Utility to return a default size.<br />
245 /// Uses the supplied size if the MeasureSpecification imposed no constraints. Will get larger if allowed by the
246 /// MeasureSpecification.<br />
248 /// <param name="size"> Default size for this layout.</param>
249 /// <param name="measureSpecification"> Constraints imposed by the parent.</param>
250 /// <returns>The size this layout should be.</returns>
251 /// <since_tizen> 6 </since_tizen>
252 public static LayoutLength GetDefaultSize(LayoutLength size, MeasureSpecification measureSpecification)
254 LayoutLength result = size;
255 MeasureSpecification.ModeType specMode = measureSpecification.Mode;
256 LayoutLength specSize = measureSpecification.Size;
260 case MeasureSpecification.ModeType.Unspecified:
265 case MeasureSpecification.ModeType.AtMost:
267 // Ensure the default size does not exceed the spec size unless the default size is 0.
268 // Another container could provide a default size of 0.
270 // Do not set size to 0, use specSize in this case as could be a legacy container
271 if( ( size.AsDecimal() < specSize.AsDecimal()) && ( size.AsDecimal() > 0) )
281 case MeasureSpecification.ModeType.Exactly:
292 /// Get the Layouts parent
294 /// <returns>Layout parent with an LayoutParent interface</returns>
295 /// <since_tizen> 6 </since_tizen>
296 public ILayoutParent GetParent()
302 /// Request that this layout is re-laid out.<br />
303 /// This will make this layout and all it's parent layouts dirty.<br />
305 /// <since_tizen> 6 </since_tizen>
306 public void RequestLayout()
308 Flags = Flags | LayoutFlags.ForceLayout;
309 Window.Instance.LayoutController.RequestLayout(this);
313 /// Predicate to determine if this layout has been requested to re-layout.<br />
316 internal bool LayoutRequested
320 return ( Flags & LayoutFlags.ForceLayout) == LayoutFlags.ForceLayout;
324 internal void SetReplaceFlag()
326 parentReplacement = true;
329 internal bool IsReplaceFlag()
331 return parentReplacement;
334 internal void ClearReplaceFlag()
336 parentReplacement = false;
340 /// Get the measured width (without any measurement flags).<br />
341 /// This method should be used only during measurement and layout calculations.<br />
343 /// <since_tizen> 6 </since_tizen>
344 public MeasuredSize MeasuredWidth{ get; set; } = new MeasuredSize( new LayoutLength(-3), MeasuredSize.StateType.MeasuredSizeOK);
347 /// Get the measured height (without any measurement flags).<br />
348 /// This method should be used only during measurement and layout calculations.<br />
350 /// <since_tizen> 6 </since_tizen>
351 public MeasuredSize MeasuredHeight{ get; set; } = new MeasuredSize( new LayoutLength(-3), MeasuredSize.StateType.MeasuredSizeOK);
354 /// Returns the suggested minimum width that the layout should use.<br />
355 /// This returns the maximum of the layout's minimum width and the owner's natural width.<br />
357 /// <since_tizen> 6 </since_tizen>
358 public LayoutLength SuggestedMinimumWidth
362 int naturalWidth = Owner.NaturalSize2D.Width;
363 return new LayoutLength(Math.Max( MinimumWidth.AsDecimal(), naturalWidth ));
368 /// Returns the suggested minimum height that the layout should use.<br />
369 /// This returns the maximum of the layout's minimum height and the owner's natural height.<br />
371 /// <since_tizen> 6 </since_tizen>
372 public LayoutLength SuggestedMinimumHeight
376 int naturalHeight = Owner.NaturalSize2D.Height;
377 return new LayoutLength(Math.Max( MinimumHeight.AsDecimal(), naturalHeight ));
382 /// Sets the minimum width of the layout.<br />
383 /// It is not guaranteed the layout will be able to achieve this minimum width (for example, if its parent
384 /// layout constrains it with less available width).<br />
385 /// 1. if the owner's View.WidthSpecification has exact value, then that value overrides the minimum size.<br />
386 /// 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 />
387 /// 3. If the owner's View.WidthSpecification is set to View.LayoutParamPolicies.MatchParent, then the parent width takes precedence over the minimum width.<br />
389 internal LayoutLength MinimumWidth {get; set;}
392 /// Sets the minimum height of the layout.<br />
393 /// It is not guaranteed the layout will be able to achieve this minimum height (for example, if its parent
394 /// layout constrains it with less available height).<br />
395 /// 1. if the owner's View.HeightSpecification has exact value, then that value overrides the minimum size.<br />
396 /// 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 />
397 /// 3. If the owner's View.HeightSpecification is set to View.LayoutParamPolicies.MatchParent, then the parent height takes precedence over the minimum height.<br />
399 internal LayoutLength MinimumHeight {get; set;}
402 /// Utility to reconcile a desired size and state, with constraints imposed by a MeasureSpecification.
404 /// <param name="size"> How big the layout wants to be.</param>
405 /// <param name="measureSpecification"> Constraints imposed by the parent.</param>
406 /// <param name="childMeasuredState"> Size information bit mask for the layout's children.</param>
407 /// <returns> A measured size, which may indicate that it is too small. </returns>
408 /// <since_tizen> 6 </since_tizen>
409 protected MeasuredSize ResolveSizeAndState( LayoutLength size, MeasureSpecification measureSpecification, MeasuredSize.StateType childMeasuredState )
411 var specMode = measureSpecification.Mode;
412 LayoutLength specSize = measureSpecification.Size;
413 MeasuredSize result = new MeasuredSize( size, childMeasuredState );
417 case MeasureSpecification.ModeType.AtMost:
419 if (specSize.AsRoundedValue() < size.AsRoundedValue())
421 result = new MeasuredSize( specSize, MeasuredSize.StateType.MeasuredSizeTooSmall);
426 case MeasureSpecification.ModeType.Exactly:
428 result.Size = specSize;
432 case MeasureSpecification.ModeType.Unspecified:
442 /// This method must be called by OnMeasure(MeasureSpec,MeasureSpec) to store the measured width and measured height.
444 /// <param name="measuredWidth">The measured width of this layout.</param>
445 /// <param name="measuredHeight">The measured height of this layout.</param>
446 /// <since_tizen> 6 </since_tizen>
447 protected void SetMeasuredDimensions( MeasuredSize measuredWidth, MeasuredSize measuredHeight )
449 MeasuredWidth = measuredWidth;
450 MeasuredHeight = measuredHeight;
451 Flags = Flags | LayoutFlags.MeasuredDimensionSet;
455 /// Measure the layout and its content to determine the measured width and the
456 /// measured height.<br />
457 /// The base class implementation of measure defaults to the background size,
458 /// unless a larger size is allowed by the MeasureSpec. Subclasses should
459 /// override to provide better measurements of their content.<br />
460 /// If this method is overridden, it is the subclass's responsibility to make sure the
461 /// measured height and width are at least the layout's minimum height and width.<br />
463 /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
464 /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
465 /// <since_tizen> 6 </since_tizen>
466 protected virtual void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
468 // GetDefaultSize will limit the MeasureSpec to the suggested minimumWidth and minimumHeight
469 SetMeasuredDimensions( GetDefaultSize( SuggestedMinimumWidth, widthMeasureSpec ),
470 GetDefaultSize( SuggestedMinimumHeight, heightMeasureSpec ) );
474 /// Called from Layout() when this layout should assign a size and position to each of its children. <br />
475 /// Derived classes with children should override this method and call Layout() on each of their children. <br />
477 /// <param name="changed">This is a new size or position for this layout.</param>
478 /// <param name="left">Left position, relative to parent.</param>
479 /// <param name="top">Top position, relative to parent.</param>
480 /// <param name="right">Right position, relative to parent.</param>
481 /// <param name="bottom">Bottom position, relative to parent.</param>
482 /// <since_tizen> 6 </since_tizen>
483 protected virtual void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
488 /// Virtual method to allow derived classes to remove any children before it is removed from
491 /// <since_tizen> 6 </since_tizen>
492 protected virtual void OnUnparent()
497 /// Virtual method called when this Layout is attached to it's owner.
498 /// Allows derived layouts to take ownership of child Views and connect to any Owner signals required.
500 /// <since_tizen> 6 </since_tizen>
501 protected virtual void OnAttachedToOwner()
505 private bool SetFrame(float left, float top, float right, float bottom)
507 bool changed = false;
509 if ( _layoutPositionData.Left != left ||
510 _layoutPositionData.Right != right ||
511 _layoutPositionData.Top != top ||
512 _layoutPositionData.Bottom != bottom )
516 float oldWidth = _layoutPositionData.Right - _layoutPositionData.Left;
517 float oldHeight = _layoutPositionData.Bottom - _layoutPositionData.Top;
518 float newWidth = right - left;
519 float newHeight = bottom - top;
520 bool sizeChanged = ( newWidth != oldWidth ) || ( newHeight != oldHeight );
522 // Set condition to layout changed as currently unspecified. Add, Remove would have specified a condition.
523 if (ConditionForAnimation.Equals(TransitionCondition.Unspecified))
525 ConditionForAnimation = TransitionCondition.LayoutChanged;
528 // Store new layout position data
529 _layoutPositionData = new LayoutData(this, ConditionForAnimation, left, top, right, bottom);
531 Debug.WriteLineIf( LayoutDebugFrameData, "LayoutItem FramePositionData View:" + _layoutPositionData.Item.Owner.Name +
532 " left:" + _layoutPositionData.Left +
533 " top:" + _layoutPositionData.Top +
534 " right:" + _layoutPositionData.Right +
535 " bottom:" + _layoutPositionData.Bottom );
537 if (Owner.Parent != null && Owner.Parent.Layout != null && Owner.Parent.Layout.LayoutWithTransition)
539 NUIApplication.GetDefaultWindow().LayoutController.AddTransitionDataEntry(_layoutPositionData);
543 Owner.SetSize(right - left, bottom - top, Owner.Position.Z);
544 if(SetPositionByLayout)
546 Owner.SetPosition(left, top, Owner.Position.Z);
551 // Reset condition for animation ready for next transition when required.
552 ConditionForAnimation = TransitionCondition.Unspecified;