2 * Copyright (c) 2019 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.
19 using System.ComponentModel;
20 using Tizen.NUI.BaseComponents;
26 enum LayoutFlags : short
31 MeasuredDimensionSet = 4
35 /// [Draft] Base class for layouts. It is used to layout a View
36 /// It can be laid out by a LayoutGroup.
38 internal class LayoutItem
40 private MeasureSpecification OldWidthMeasureSpec; // Store measure specification to compare against later
41 private MeasureSpecification OldHeightMeasureSpec;// Store measure specification to compare against later
43 private LayoutFlags Flags = LayoutFlags.None;
45 private ILayoutParent Parent;
47 LayoutData _layoutPositionData;
49 private Extents _padding;
50 private Extents _margin;
52 public TransitionCondition ConditionForAnimation{get; set;}
55 /// [Draft] The View that this Layout has been assigned to.
57 public View Owner{get; set;} // Should not keep a View alive.
60 /// [Draft] Is this Layout set to animate its content.
62 public bool Animate{get; set;}
65 /// [Draft] Margin for this LayoutItem
81 /// [Draft] Padding for this LayoutItem
83 public Extents Padding
97 /// [Draft] Constructor
105 /// [Draft] Constructor setting the owner of this LayoutItem.
107 /// <param name="owner">Owning View of this layout, currently a View but may be extending for Windows/Layers.</param>
108 public LayoutItem(View owner)
115 /// [Draft] Set parent to this layout.
117 /// <param name="parent">Parent to set on this Layout.</param>
118 public void SetParent( ILayoutParent parent)
120 Parent = parent as LayoutGroup;
124 /// Unparent this layout from it's owner, and remove any layout children in derived types. <br />
126 public void Unparent()
128 // Enable directly derived types to first remove children
131 // Remove myself from parent
132 Parent?.Remove( this );
134 // Remove parent reference
137 // Lastly, clear layout from owning View.
138 Owner?.ResetLayout();
141 private void Initialize()
143 _layoutPositionData = new LayoutData(this,TransitionCondition.Unspecified,0,0,0,0);
144 _padding = new Extents(0,0,0,0);
145 _margin = new Extents(0,0,0,0);
149 /// Get the View owning this LayoutItem
151 internal View GetOwner()
157 /// Initialize the layout and allow derived classes to also perform any operations
159 /// <param name="owner">Owner of this Layout.</param>
160 internal void AttachToOwner(View owner)
162 // Assign the layout owner.
165 // Add layout to parent layout if a layout container
166 View parent = Owner.GetParent() as View;
167 (parent?.Layout as LayoutGroup)?.Add( this );
169 // If Add or ChangeOnAdd then do not update condition
170 if (ConditionForAnimation.HasFlag(TransitionCondition.Unspecified))
172 ConditionForAnimation = TransitionCondition.LayoutChanged;
177 /// This is called to find out how big a layout should be. <br />
178 /// The parent supplies constraint information in the width and height parameters. <br />
179 /// The actual measurement work of a layout is performed in OnMeasure called by this
180 /// method. Therefore, only OnMeasure can and must be overridden by subclasses. <br />
182 /// <param name="widthMeasureSpec"> Horizontal space requirements as imposed by the parent.</param>
183 /// <param name="heightMeasureSpec">Vertical space requirements as imposed by the parent.</param>
184 internal void Measure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
186 // Check if relayouting is required.
187 bool specChanged = (widthMeasureSpec.Size != OldWidthMeasureSpec.Size) ||
188 (heightMeasureSpec.Size != OldHeightMeasureSpec.Size) ||
189 (widthMeasureSpec.Mode != OldWidthMeasureSpec.Mode) ||
190 (heightMeasureSpec.Mode != OldHeightMeasureSpec.Mode);
192 bool isSpecExactly = (widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly) &&
193 (heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly);
195 bool matchesSpecSize = (MeasuredWidth.Size == widthMeasureSpec.Size) &&
196 (MeasuredHeight.Size == heightMeasureSpec.Size);
198 bool needsLayout = specChanged && ( !isSpecExactly || !matchesSpecSize);
199 needsLayout = needsLayout || ((Flags & LayoutFlags.ForceLayout) == LayoutFlags.ForceLayout);
203 OnMeasure(widthMeasureSpec, heightMeasureSpec);
204 Flags = Flags | LayoutFlags.LayoutRequired;
205 Flags &= ~LayoutFlags.ForceLayout;
207 OldWidthMeasureSpec = widthMeasureSpec;
208 OldHeightMeasureSpec = heightMeasureSpec;
212 /// Assign a size and position to a layout and all of its descendants. <br />
213 /// This is the second phase of the layout mechanism. (The first is measuring). In this phase, each parent
214 /// calls layout on all of its children to position them. This is typically done using the child<br />
215 /// measurements that were stored in the measure pass.<br />
217 /// <param name="left">Left position, relative to parent.</param>
218 /// <param name="top">Top position, relative to parent.</param>
219 /// <param name="right">Right position, relative to parent.</param>
220 /// <param name="bottom">Bottom position, relative to parent.</param>
221 public void Layout(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
223 bool changed = SetFrame(left.AsRoundedValue(),
224 top.AsRoundedValue(),
225 right.AsRoundedValue(),
226 bottom.AsRoundedValue());
228 // Check if Measure needed before Layouting
229 if (changed || ((Flags & LayoutFlags.LayoutRequired) == LayoutFlags.LayoutRequired))
231 OnLayout(changed, left, top, right, bottom);
233 Flags &= ~LayoutFlags.LayoutRequired;
238 /// Utility to return a default size.<br />
239 /// Uses the supplied size if the MeasureSpecification imposed no constraints. Will get larger if allowed by the
240 /// MeasureSpecification.<br />
242 /// <param name="size"> Default size for this layout.</param>
243 /// <param name="measureSpecification"> Constraints imposed by the parent.</param>
244 /// <returns>The size this layout should be.</returns>
245 public static LayoutLength GetDefaultSize(LayoutLength size, MeasureSpecification measureSpecification)
247 LayoutLength result = size;
248 MeasureSpecification.ModeType specMode = measureSpecification.Mode;
249 LayoutLength specSize = measureSpecification.Size;
253 case MeasureSpecification.ModeType.Unspecified:
258 case MeasureSpecification.ModeType.AtMost:
260 // Ensure the default size does not exceed the spec size unless the default size is 0.
261 // Another container could provide a default size of 0.
263 // Do not set size to 0, use specSize in this case as could be a legacy container
264 if( ( size.AsDecimal() < specSize.AsDecimal()) && ( size.AsDecimal() > 0) )
274 case MeasureSpecification.ModeType.Exactly:
284 public ILayoutParent GetParent()
290 /// Request that this layout is re-laid out.<br />
291 /// This will make this layout and all it's parent layouts dirty.<br />
293 public void RequestLayout()
295 Flags = Flags | LayoutFlags.ForceLayout;
296 Window.Instance.LayoutController.RequestLayout(this);
300 /// Predicate to determine if this layout has been requested to re-layout.<br />
302 public bool LayoutRequested
306 return ( Flags & LayoutFlags.ForceLayout) == LayoutFlags.ForceLayout;
311 /// Get the measured width (without any measurement flags).<br />
312 /// This method should be used only during measurement and layout calculations.<br />
314 public MeasuredSize MeasuredWidth{ get; set; } = new MeasuredSize( new LayoutLength(-3), MeasuredSize.StateType.MeasuredSizeOK);
317 /// Get the measured height (without any measurement flags).<br />
318 /// This method should be used only during measurement and layout calculations.<br />
320 public MeasuredSize MeasuredHeight{ get; set; } = new MeasuredSize( new LayoutLength(-3), MeasuredSize.StateType.MeasuredSizeOK);
323 /// Get the measured width and state.<br />
324 /// This method should be used only during measurement and layout calculations.<br />
326 public MeasuredSize MeasuredWidthAndState
330 return MeasuredWidth; // Not bitmasking State unless proven to be required.
336 /// Get the measured height and state.<br />
337 /// This method should be used only during measurement and layout calculations.<br />
339 public MeasuredSize MeasuredHeightAndState
343 return MeasuredHeight; // Not bitmasking State unless proven to be required.
348 /// Returns the suggested minimum width that the layout should use.<br />
349 /// This returns the maximum of the layout's minimum width and the owner's natural width.<br />
351 public LayoutLength SuggestedMinimumWidth
355 int naturalWidth = Owner.NaturalSize2D.Width;
356 return new LayoutLength(Math.Max( MinimumWidth.AsDecimal(), naturalWidth ));
361 /// Returns the suggested minimum height that the layout should use.<br />
362 /// This returns the maximum of the layout's minimum height and the owner's natural height.<br />
364 public LayoutLength SuggestedMinimumHeight
368 int naturalHeight = Owner.NaturalSize2D.Height;
369 return new LayoutLength(Math.Max( MinimumHeight.AsDecimal(), naturalHeight ));
374 /// Sets the minimum width of the layout.<br />
375 /// It is not guaranteed the layout will be able to achieve this minimum width (for example, if its parent
376 /// layout constrains it with less available width).<br />
377 /// 1. if the owner's View.WidthSpecification has exact value, then that value overrides the minimum size.<br />
378 /// 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 />
379 /// 3. If the owner's View.WidthSpecification is set to View.LayoutParamPolicies.MatchParent, then the parent width takes precedence over the minimum width.<br />
381 public LayoutLength MinimumWidth {get; set;}
384 /// Sets the minimum height of the layout.<br />
385 /// It is not guaranteed the layout will be able to achieve this minimum height (for example, if its parent
386 /// layout constrains it with less available height).<br />
387 /// 1. if the owner's View.HeightSpecification has exact value, then that value overrides the minimum size.<br />
388 /// 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 />
389 /// 3. If the owner's View.HeightSpecification is set to View.LayoutParamPolicies.MatchParent, then the parent height takes precedence over the minimum height.<br />
391 public LayoutLength MinimumHeight {get; set;}
394 /// Utility to reconcile a desired size and state, with constraints imposed by a MeasureSpecification.
396 /// <param name="size"> How big the layout wants to be.</param>
397 /// <param name="measureSpecification"> Constraints imposed by the parent.</param>
398 /// <param name="childMeasuredState"> Size information bit mask for the layout's children.</param>
399 /// <returns> A measured size, which may indicate that it is too small. </returns>
400 protected MeasuredSize ResolveSizeAndState( LayoutLength size, MeasureSpecification measureSpecification, MeasuredSize.StateType childMeasuredState )
402 var specMode = measureSpecification.Mode;
403 LayoutLength specSize = measureSpecification.Size;
404 MeasuredSize result = new MeasuredSize( size, childMeasuredState);
408 case MeasureSpecification.ModeType.AtMost:
410 if (specSize.AsRoundedValue() < size.AsRoundedValue())
412 result = new MeasuredSize( specSize, MeasuredSize.StateType.MeasuredSizeTooSmall);
417 case MeasureSpecification.ModeType.Exactly:
419 result.Size = specSize;
423 case MeasureSpecification.ModeType.Unspecified:
433 /// This method must be called by OnMeasure(MeasureSpec,MeasureSpec) to store the measured width and measured height.
435 /// <param name="measuredWidth">The measured width of this layout.</param>
436 /// <param name="measuredHeight">The measured height of this layout.</param>
437 protected void SetMeasuredDimensions( MeasuredSize measuredWidth, MeasuredSize measuredHeight )
439 MeasuredWidth = measuredWidth;
440 MeasuredHeight = measuredHeight;
441 Flags = Flags | LayoutFlags.MeasuredDimensionSet;
445 /// Measure the layout and its content to determine the measured width and the
446 /// measured height.<br />
447 /// The base class implementation of measure defaults to the background size,
448 /// unless a larger size is allowed by the MeasureSpec. Subclasses should
449 /// override to provide better measurements of their content.<br />
450 /// If this method is overridden, it is the subclass's responsibility to make sure the
451 /// measured height and width are at least the layout's minimum height and width.<br />
453 /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
454 /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
455 protected virtual void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
457 // GetDefaultSize will limit the MeasureSpec to the suggested minimumWidth and minimumHeight
458 SetMeasuredDimensions( GetDefaultSize( SuggestedMinimumWidth, widthMeasureSpec ),
459 GetDefaultSize( SuggestedMinimumHeight, heightMeasureSpec ) );
463 /// Called from Layout() when this layout should assign a size and position to each of its children. <br />
464 /// Derived classes with children should override this method and call Layout() on each of their children. <br />
466 /// <param name="changed">This is a new size or position for this layout.</param>
467 /// <param name="left">Left position, relative to parent.</param>
468 /// <param name="top">Top position, relative to parent.</param>
469 /// <param name="right">Right position, relative to parent.</param>
470 /// <param name="bottom">Bottom position, relative to parent.</param>
471 protected virtual void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
476 /// Virtual method to inform derived classes when the layout size changed. <br />
478 /// <param name="newSize">The new size of the layout.</param>
479 /// <param name="oldSize">The old size of the layout.</param>
480 protected virtual void OnSizeChanged(LayoutSize newSize, LayoutSize oldSize)
485 /// Virtual method to allow derived classes to remove any children before it is removed from
488 public virtual void OnUnparent()
493 /// Virtual method called when this Layout is attached to it's owner.
494 /// Allows derived layouts to take ownership of child Views and connect to any Owner signals required.
496 protected virtual void OnAttachedToOwner()
500 private bool SetFrame(float left, float top, float right, float bottom)
502 bool changed = false;
504 if ( _layoutPositionData.Left != left ||
505 _layoutPositionData.Right != right ||
506 _layoutPositionData.Top != top ||
507 _layoutPositionData.Bottom != bottom )
511 float oldWidth = _layoutPositionData.Right - _layoutPositionData.Left;
512 float oldHeight = _layoutPositionData.Bottom - _layoutPositionData.Top;
513 float newWidth = right - left;
514 float newHeight = bottom - top;
515 bool sizeChanged = ( newWidth != oldWidth ) || ( newHeight != oldHeight );
517 // Set condition to layout changed as currently unspecified. Add, Remove would have specified a condition.
518 if (ConditionForAnimation.HasFlag(TransitionCondition.Unspecified))
520 ConditionForAnimation = TransitionCondition.LayoutChanged;
523 // Store new layout position data
524 _layoutPositionData = new LayoutData(this, ConditionForAnimation, left, top, right, bottom);
526 Window.Instance.LayoutController.AddTransitionDataEntry(_layoutPositionData);
528 // Reset condition for animation ready for next transition when required.
529 ConditionForAnimation = TransitionCondition.Unspecified;