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 private LayoutLength _left;
48 private LayoutLength _right;
49 private LayoutLength _top;
50 private LayoutLength _bottom;
51 private LayoutData _layoutData;
53 private Extents _padding;
54 private Extents _margin;
57 /// [Draft] The View that this Layout has been assigned to.
59 public View Owner{get; set;} // Should not keep a View alive.
62 /// [Draft] Margin for this LayoutItem
78 /// [Draft] Padding for this LayoutItem
80 public Extents Padding
94 /// [Draft] Constructor
102 /// [Draft] Constructor setting the owner of this LayoutItem.
104 /// <param name="owner">Owning View of this layout, currently a View but may be extending for Windows/Layers.</param>
105 public LayoutItem(View owner)
112 /// [Draft] Set parent to this layout.
114 /// <param name="parent">Parent to set on this Layout.</param>
115 public void SetParent( ILayoutParent parent)
117 Parent = parent as LayoutGroup;
121 /// Unparent this layout from it's owner, and remove any layout children in derived types. <br />
123 public void Unparent()
125 // Enable directly derived types to first remove children
128 // Remove myself from parent
129 Parent?.Remove( this );
131 // Remove parent reference
134 // Lastly, clear layout from owning View.
135 Owner?.ResetLayout();
138 private void Initialize()
140 _layoutData = new LayoutData();
141 _left = new LayoutLength(0);
142 _top = new LayoutLength(0);
143 _right = new LayoutLength(0);
144 _bottom = new LayoutLength(0);
145 _padding = new Extents(0,0,0,0);
146 _margin = new Extents(0,0,0,0);
150 /// Get the View owning this LayoutItem
152 internal View GetOwner()
158 /// Initialize the layout and allow derived classes to also perform any operations
160 /// <param name="owner">Owner of this Layout.</param>
161 internal void AttachToOwner(View owner)
163 // Assign the layout owner.
166 // Add layout to parent layout if a layout container
167 View parent = Owner.GetParent() as View;
168 (parent?.Layout as LayoutGroup)?.Add( this );
172 /// This is called to find out how big a layout should be. <br />
173 /// The parent supplies constraint information in the width and height parameters. <br />
174 /// The actual measurement work of a layout is performed in OnMeasure called by this
175 /// method. Therefore, only OnMeasure can and must be overridden by subclasses. <br />
177 /// <param name="widthMeasureSpec"> Horizontal space requirements as imposed by the parent.</param>
178 /// <param name="heightMeasureSpec">Vertical space requirements as imposed by the parent.</param>
179 internal void Measure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
181 // Check if relayouting is required.
182 bool specChanged = (widthMeasureSpec.Size != OldWidthMeasureSpec.Size) ||
183 (heightMeasureSpec.Size != OldHeightMeasureSpec.Size) ||
184 (widthMeasureSpec.Mode != OldWidthMeasureSpec.Mode) ||
185 (heightMeasureSpec.Mode != OldHeightMeasureSpec.Mode);
187 bool isSpecExactly = (widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly) &&
188 (heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly);
190 bool matchesSpecSize = (MeasuredWidth.Size == widthMeasureSpec.Size) &&
191 (MeasuredHeight.Size == heightMeasureSpec.Size);
193 bool needsLayout = specChanged && ( !isSpecExactly || !matchesSpecSize);
194 needsLayout = needsLayout || ((Flags & LayoutFlags.ForceLayout) == LayoutFlags.ForceLayout);
198 OnMeasure(widthMeasureSpec, heightMeasureSpec);
199 Flags = Flags | LayoutFlags.LayoutRequired;
200 Flags &= ~LayoutFlags.ForceLayout;
202 OldWidthMeasureSpec = widthMeasureSpec;
203 OldHeightMeasureSpec = heightMeasureSpec;
207 /// Assign a size and position to a layout and all of its descendants. <br />
208 /// This is the second phase of the layout mechanism. (The first is measuring). In this phase, each parent
209 /// calls layout on all of its children to position them. This is typically done using the child<br />
210 /// measurements that were stored in the measure pass.<br />
212 /// <param name="left">Left position, relative to parent.</param>
213 /// <param name="top">Top position, relative to parent.</param>
214 /// <param name="right">Right position, relative to parent.</param>
215 /// <param name="bottom">Bottom position, relative to parent.</param>
216 public void Layout(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
218 bool changed = SetFrame(left, top, right, bottom);
220 // Check if Measure needed before Layouting
221 if (changed || ((Flags & LayoutFlags.LayoutRequired) == LayoutFlags.LayoutRequired))
223 OnLayout(changed, left, top, right, bottom);
225 Flags &= ~LayoutFlags.LayoutRequired;
230 /// Utility to return a default size.<br />
231 /// Uses the supplied size if the MeasureSpecification imposed no constraints. Will get larger if allowed by the
232 /// MeasureSpecification.<br />
234 /// <param name="size"> Default size for this layout.</param>
235 /// <param name="measureSpecification"> Constraints imposed by the parent.</param>
236 /// <returns>The size this layout should be.</returns>
237 public static LayoutLength GetDefaultSize(LayoutLength size, MeasureSpecification measureSpecification)
239 LayoutLength result = size;
240 MeasureSpecification.ModeType specMode = measureSpecification.Mode;
241 LayoutLength specSize = measureSpecification.Size;
245 case MeasureSpecification.ModeType.Unspecified:
250 case MeasureSpecification.ModeType.AtMost:
252 // Ensure the default size does not exceed the spec size unless the default size is 0.
253 // Another container could provide a default size of 0.
255 // Do not set size to 0, use specSize in this case as could be a legacy container
256 if( ( size.AsDecimal() < specSize.AsDecimal()) && ( size.AsDecimal() > 0) )
266 case MeasureSpecification.ModeType.Exactly:
276 public ILayoutParent GetParent()
282 /// Request that this layout is re-laid out.<br />
283 /// This will make this layout and all it's parent layouts dirty.<br />
285 public void RequestLayout()
287 Flags = Flags | LayoutFlags.ForceLayout;
288 Window.Instance.LayoutController.RequestLayout(this);
292 /// Predicate to determine if this layout has been requested to re-layout.<br />
294 public bool LayoutRequested
298 return ( Flags & LayoutFlags.ForceLayout) == LayoutFlags.ForceLayout;
303 /// Get the measured width (without any measurement flags).<br />
304 /// This method should be used only during measurement and layout calculations.<br />
306 public MeasuredSize MeasuredWidth{ get; set; } = new MeasuredSize( new LayoutLength(-3), MeasuredSize.StateType.MeasuredSizeOK);
309 /// Get the measured height (without any measurement flags).<br />
310 /// This method should be used only during measurement and layout calculations.<br />
312 public MeasuredSize MeasuredHeight{ get; set; } = new MeasuredSize( new LayoutLength(-3), MeasuredSize.StateType.MeasuredSizeOK);
315 /// Get the measured width and state.<br />
316 /// This method should be used only during measurement and layout calculations.<br />
318 public MeasuredSize MeasuredWidthAndState
322 return MeasuredWidth; // Not bitmasking State unless proven to be required.
328 /// Get the measured height and state.<br />
329 /// This method should be used only during measurement and layout calculations.<br />
331 public MeasuredSize MeasuredHeightAndState
335 return MeasuredHeight; // Not bitmasking State unless proven to be required.
340 /// Returns the suggested minimum width that the layout should use.<br />
341 /// This returns the maximum of the layout's minimum width and the owner's natural width.<br />
343 public LayoutLength SuggestedMinimumWidth
347 int naturalWidth = Owner.NaturalSize2D.Width;
348 return new LayoutLength(Math.Max( MinimumWidth.AsDecimal(), naturalWidth ));
353 /// Returns the suggested minimum height that the layout should use.<br />
354 /// This returns the maximum of the layout's minimum height and the owner's natural height.<br />
356 public LayoutLength SuggestedMinimumHeight
360 int naturalHeight = Owner.NaturalSize2D.Height;
361 return new LayoutLength(Math.Max( MinimumHeight.AsDecimal(), naturalHeight ));
366 /// Sets the minimum width of the layout.<br />
367 /// It is not guaranteed the layout will be able to achieve this minimum width (for example, if its parent
368 /// layout constrains it with less available width).<br />
369 /// 1. if the owner's View.LayoutWidthSpecification has exact value, then that value overrides the minimum size.<br />
370 /// 2. If the owner's View.LayoutWidthSpecification is set to View.LayoutParamPolicies.WrapContent, then the view's width is set based on the suggested minimum width. (@see GetSuggestedMinimumWidth()).<br />
371 /// 3. If the owner's View.LayoutWidthSpecification is set to View.LayoutParamPolicies.MatchParent, then the parent width takes precedence over the minimum width.<br />
373 public LayoutLength MinimumWidth {get; set;}
376 /// Sets the minimum height of the layout.<br />
377 /// It is not guaranteed the layout will be able to achieve this minimum height (for example, if its parent
378 /// layout constrains it with less available height).<br />
379 /// 1. if the owner's View.LayoutHeightSpecification has exact value, then that value overrides the minimum size.<br />
380 /// 2. If the owner's View.LayoutHeightSpecification is set to View.LayoutParamPolicies.WrapContent, then the view's height is set based on the suggested minimum height. (@see GetSuggestedMinimumHeight()).<br />
381 /// 3. If the owner's View.LayoutHeightSpecification is set to View.LayoutParamPolicies.MatchParent, then the parent height takes precedence over the minimum height.<br />
383 public LayoutLength MinimumHeight {get; set;}
386 /// Utility to reconcile a desired size and state, with constraints imposed by a MeasureSpecification.
388 /// <param name="size"> How big the layout wants to be.</param>
389 /// <param name="measureSpecification"> Constraints imposed by the parent.</param>
390 /// <param name="childMeasuredState"> Size information bit mask for the layout's children.</param>
391 /// <returns> A measured size, which may indicate that it is too small. </returns>
392 protected MeasuredSize ResolveSizeAndState( LayoutLength size, MeasureSpecification measureSpecification, MeasuredSize.StateType childMeasuredState )
394 var specMode = measureSpecification.Mode;
395 LayoutLength specSize = measureSpecification.Size;
396 MeasuredSize result = new MeasuredSize( size, childMeasuredState);
400 case MeasureSpecification.ModeType.AtMost:
402 if (specSize.AsRoundedValue() < size.AsRoundedValue())
404 result = new MeasuredSize( specSize, MeasuredSize.StateType.MeasuredSizeTooSmall);
409 case MeasureSpecification.ModeType.Exactly:
411 result.Size = specSize;
415 case MeasureSpecification.ModeType.Unspecified:
425 /// This method must be called by OnMeasure(MeasureSpec,MeasureSpec) to store the measured width and measured height.
427 /// <param name="measuredWidth">The measured width of this layout.</param>
428 /// <param name="measuredHeight">The measured height of this layout.</param>
429 protected void SetMeasuredDimensions( MeasuredSize measuredWidth, MeasuredSize measuredHeight )
431 MeasuredWidth = measuredWidth;
432 MeasuredHeight = measuredHeight;
433 Flags = Flags | LayoutFlags.MeasuredDimensionSet;
437 /// Measure the layout and its content to determine the measured width and the
438 /// measured height.<br />
439 /// The base class implementation of measure defaults to the background size,
440 /// unless a larger size is allowed by the MeasureSpec. Subclasses should
441 /// override to provide better measurements of their content.<br />
442 /// If this method is overridden, it is the subclass's responsibility to make sure the
443 /// measured height and width are at least the layout's minimum height and width.<br />
445 /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
446 /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
447 protected virtual void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
449 // GetDefaultSize will limit the MeasureSpec to the suggested minimumWidth and minimumHeight
450 SetMeasuredDimensions( GetDefaultSize( SuggestedMinimumWidth, widthMeasureSpec ),
451 GetDefaultSize( SuggestedMinimumHeight, heightMeasureSpec ) );
455 /// Called from Layout() when this layout should assign a size and position to each of its children. <br />
456 /// Derived classes with children should override this method and call Layout() on each of their children. <br />
458 /// <param name="changed">This is a new size or position for this layout.</param>
459 /// <param name="left">Left position, relative to parent.</param>
460 /// <param name="top">Top position, relative to parent.</param>
461 /// <param name="right">Right position, relative to parent.</param>
462 /// <param name="bottom">Bottom position, relative to parent.</param>
463 protected virtual void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
468 /// Virtual method to inform derived classes when the layout size changed. <br />
470 /// <param name="newSize">The new size of the layout.</param>
471 /// <param name="oldSize">The old size of the layout.</param>
472 protected virtual void OnSizeChanged(LayoutSize newSize, LayoutSize oldSize)
477 /// Virtual method to allow derived classes to remove any children before it is removed from
480 public virtual void OnUnparent()
485 /// Virtual method called when this Layout is attached to it's owner.
486 /// Allows derived layouts to take ownership of child Views and connect to any Owner signals required.
488 protected virtual void OnAttachedToOwner()
492 private bool SetFrame(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
494 bool changed = false;
496 if( _left != left || _right != right || _top != top || _bottom != bottom )
501 LayoutLength oldWidth = _right - _left;
502 LayoutLength oldHeight = _bottom - _top;
503 LayoutLength newWidth = right - left;
504 LayoutLength newHeight = bottom - top;
505 bool sizeChanged = ( newWidth != oldWidth ) || ( newHeight != oldHeight );
512 // Set actual positions of View.
513 Owner.SetX(_left.AsRoundedValue());
514 Owner.SetY(_top.AsRoundedValue());
515 Owner.SetSize((int)newWidth.AsRoundedValue(), (int)newHeight.AsRoundedValue());