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;
54 /// The View that this Layout has been assigned to.
56 public View Owner{get; set;} // Should not keep a View alive.
59 /// [Draft] Constructor
66 /// [Draft] Constructor setting the owner of this LayoutItem.
68 /// <param name="owner">Owning View of this layout, currently a View but may be extending for Windows/Layers.</param>
69 public LayoutItem(View owner)
72 _layoutData = new LayoutData();
73 _left = new LayoutLength(0);
74 _top = new LayoutLength(0);
75 _right = new LayoutLength(0);
76 _bottom = new LayoutLength(0);
80 /// [Draft] Set parent to this layout.
82 /// <param name="parent">Parent to set on this Layout.</param>
83 public void SetParent( ILayoutParent parent)
85 Parent = parent as LayoutGroup;
86 Log.Info("NUI", "Setting Parent Layout for:" + Owner?.Name + " to (Parent):" + (parent == null ? "null":parent.ToString() ) + "\n");
90 /// Unparent this layout from it's owner, and remove any layout children in derived types. <br />
92 public void Unparent()
94 // Enable directly derived types to first remove children
97 // Remove myself from parent
98 Parent?.Remove( this );
100 // Remove parent reference
103 // Lastly, clear layout from owning View.
104 Owner?.ResetLayout();
108 /// Get the View owning this LayoutItem
110 internal View GetOwner()
116 /// Initialize the layout and allow derived classes to also perform any operations
118 /// <param name="owner">Owner of this Layout.</param>
119 internal void AttachToOwner(View owner)
121 // Assign the layout owner.
124 // Add layout to parent layout if a layout container
125 View parent = Owner.GetParent() as View;
126 (parent?.Layout as LayoutGroup)?.Add( this );
130 /// This is called to find out how big a layout should be. <br />
131 /// The parent supplies constraint information in the width and height parameters. <br />
132 /// The actual measurement work of a layout is performed in OnMeasure called by this
133 /// method. Therefore, only OnMeasure can and must be overridden by subclasses. <br />
135 /// <param name="widthMeasureSpec"> Horizontal space requirements as imposed by the parent.</param>
136 /// <param name="heightMeasureSpec">Vertical space requirements as imposed by the parent.</param>
137 internal void Measure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
139 // Check if relayouting is required.
140 bool specChanged = (widthMeasureSpec.Size != OldWidthMeasureSpec.Size) ||
141 (heightMeasureSpec.Size != OldHeightMeasureSpec.Size) ||
142 (widthMeasureSpec.Mode != OldWidthMeasureSpec.Mode) ||
143 (heightMeasureSpec.Mode != OldHeightMeasureSpec.Mode);
145 bool isSpecExactly = (widthMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly) &&
146 (heightMeasureSpec.Mode == MeasureSpecification.ModeType.Exactly);
148 bool matchesSpecSize = (MeasuredWidth.Size == widthMeasureSpec.Size) &&
149 (MeasuredHeight.Size == heightMeasureSpec.Size);
151 bool needsLayout = specChanged && ( !isSpecExactly || !matchesSpecSize);
152 needsLayout = needsLayout || ((Flags & LayoutFlags.ForceLayout) == LayoutFlags.ForceLayout);
154 Log.Info("NUI", "Measuring:" + Owner.Name + " needsLayout[" + needsLayout.ToString() + "]\n");
158 OnMeasure(widthMeasureSpec, heightMeasureSpec);
159 Flags = Flags | LayoutFlags.LayoutRequired;
160 Flags &= ~LayoutFlags.ForceLayout;
162 OldWidthMeasureSpec = widthMeasureSpec;
163 OldHeightMeasureSpec = heightMeasureSpec;
165 Log.Info("NUI", "LayoutItem Measure owner:" + Owner.Name + " width:" + widthMeasureSpec.Size.AsRoundedValue() + " height:" + heightMeasureSpec.Size.AsRoundedValue() + "\n");
169 /// Assign a size and position to a layout and all of its descendants. <br />
170 /// This is the second phase of the layout mechanism. (The first is measuring). In this phase, each parent
171 /// calls layout on all of its children to position them. This is typically done using the child<br />
172 /// measurements that were stored in the measure pass.<br />
174 /// <param name="left">Left position, relative to parent.</param>
175 /// <param name="top">Top position, relative to parent.</param>
176 /// <param name="right">Right position, relative to parent.</param>
177 /// <param name="bottom">Bottom position, relative to parent.</param>
178 public void Layout(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
180 Log.Info("NUI", "LayoutItem Layout owner:" + Owner.Name + "\n");
182 bool changed = SetFrame(left, top, right, bottom);
184 // Check if Measure needed before Layouting
185 if (changed || ((Flags & LayoutFlags.LayoutRequired) == LayoutFlags.LayoutRequired))
187 Log.Info("NUI", "LayoutItem Layout Frame changed or Layout forced\n");
188 OnLayout(changed, left, top, right, bottom);
190 Flags &= ~LayoutFlags.LayoutRequired;
195 /// Utility to return a default size.<br />
196 /// Uses the supplied size if the MeasureSpecification imposed no constraints. Will get larger if allowed by the
197 /// MeasureSpecification.<br />
199 /// <param name="size"> Default size for this layout.</param>
200 /// <param name="measureSpecification"> Constraints imposed by the parent.</param>
201 /// <returns>The size this layout should be.</returns>
202 public static LayoutLength GetDefaultSize(LayoutLength size, MeasureSpecification measureSpecification)
204 LayoutLength result = size;
205 MeasureSpecification.ModeType specMode = measureSpecification.Mode;
206 LayoutLength specSize = measureSpecification.Size;
210 case MeasureSpecification.ModeType.Unspecified:
215 case MeasureSpecification.ModeType.AtMost:
217 // Ensure the default size does not exceed the spec size unless the default size is 0.
218 // Another container could provide a default size of 0.
220 // Do not set size to 0, use specSize in this case as could be a legacy container
221 if( ( size.AsDecimal() < specSize.AsDecimal()) && ( size.AsDecimal() > 0) )
231 case MeasureSpecification.ModeType.Exactly:
238 Log.Info("NUI", "DefaultSize :" + result.AsRoundedValue() + "\n");
243 public ILayoutParent GetParent()
249 /// Request that this layout is re-laid out.<br />
250 /// This will make this layout and all it's parent layouts dirty.<br />
252 public void RequestLayout()
254 Log.Info("NUI", "RequestLayout on:" + Owner?.Name + "\n");
255 Flags = Flags | LayoutFlags.ForceLayout;
256 Window.Instance.LayoutController.RequestLayout(this);
260 /// Predicate to determine if this layout has been requested to re-layout.<br />
262 public bool LayoutRequested
266 return ( Flags & LayoutFlags.ForceLayout) == LayoutFlags.ForceLayout;
271 /// Get the measured width (without any measurement flags).<br />
272 /// This method should be used only during measurement and layout calculations.<br />
274 public MeasuredSize MeasuredWidth{ get; set; } = new MeasuredSize( new LayoutLength(-3), MeasuredSize.StateType.MeasuredSizeOK);
277 /// Get the measured height (without any measurement flags).<br />
278 /// This method should be used only during measurement and layout calculations.<br />
280 public MeasuredSize MeasuredHeight{ get; set; } = new MeasuredSize( new LayoutLength(-3), MeasuredSize.StateType.MeasuredSizeOK);
283 /// Get the measured width and state.<br />
284 /// This method should be used only during measurement and layout calculations.<br />
286 public MeasuredSize MeasuredWidthAndState
290 return MeasuredWidth; // Not bitmasking State unless proven to be required.
296 /// Get the measured height and state.<br />
297 /// This method should be used only during measurement and layout calculations.<br />
299 public MeasuredSize MeasuredHeightAndState
303 return MeasuredHeight; // Not bitmasking State unless proven to be required.
308 /// Returns the suggested minimum width that the layout should use.<br />
309 /// This returns the maximum of the layout's minimum width and the owner's natural width.<br />
311 public LayoutLength SuggestedMinimumWidth
315 int naturalWidth = Owner.NaturalSize2D.Width;
316 Log.Info("NUI", "NaturalWidth for: " + Owner.Name + " :" + naturalWidth +"\n");
317 return new LayoutLength(Math.Max( MinimumWidth.AsDecimal(), naturalWidth ));
322 /// Returns the suggested minimum height that the layout should use.<br />
323 /// This returns the maximum of the layout's minimum height and the owner's natural height.<br />
325 public LayoutLength SuggestedMinimumHeight
329 int naturalHeight = Owner.NaturalSize2D.Height;
330 Log.Info("NUI", "NaturalHeight for: " + Owner.Name + " :" + naturalHeight +"\n");
331 return new LayoutLength(Math.Max( MinimumHeight.AsDecimal(), naturalHeight ));
336 /// Sets the minimum width of the layout.<br />
337 /// It is not guaranteed the layout will be able to achieve this minimum width (for example, if its parent
338 /// layout constrains it with less available width).<br />
339 /// 1. if the owner's View.LayoutWidthSpecification has exact value, then that value overrides the minimum size.<br />
340 /// 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 />
341 /// 3. If the owner's View.LayoutWidthSpecification is set to View.LayoutParamPolicies.MatchParent, then the parent width takes precedence over the minimum width.<br />
343 public LayoutLength MinimumWidth {get; set;}
346 /// Sets the minimum height of the layout.<br />
347 /// It is not guaranteed the layout will be able to achieve this minimum height (for example, if its parent
348 /// layout constrains it with less available height).<br />
349 /// 1. if the owner's View.LayoutHeightSpecification has exact value, then that value overrides the minimum size.<br />
350 /// 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 />
351 /// 3. If the owner's View.LayoutHeightSpecification is set to View.LayoutParamPolicies.MatchParent, then the parent height takes precedence over the minimum height.<br />
353 public LayoutLength MinimumHeight {get; set;}
356 /// Utility to reconcile a desired size and state, with constraints imposed by a MeasureSpecification.
358 /// <param name="size"> How big the layout wants to be.</param>
359 /// <param name="measureSpecification"> Constraints imposed by the parent.</param>
360 /// <param name="childMeasuredState"> Size information bit mask for the layout's children.</param>
361 /// <returns> A measured size, which may indicate that it is too small. </returns>
362 protected MeasuredSize ResolveSizeAndState( LayoutLength size, MeasureSpecification measureSpecification, MeasuredSize.StateType childMeasuredState )
364 var specMode = measureSpecification.Mode;
365 LayoutLength specSize = measureSpecification.Size;
366 MeasuredSize result = new MeasuredSize( size, childMeasuredState);
370 case MeasureSpecification.ModeType.AtMost:
372 if (specSize.AsRoundedValue() < size.AsRoundedValue())
374 result = new MeasuredSize( specSize, MeasuredSize.StateType.MeasuredSizeTooSmall);
379 case MeasureSpecification.ModeType.Exactly:
381 result.Size = specSize;
385 case MeasureSpecification.ModeType.Unspecified:
395 /// This method must be called by OnMeasure(MeasureSpec,MeasureSpec) to store the measured width and measured height.
397 /// <param name="measuredWidth">The measured width of this layout.</param>
398 /// <param name="measuredHeight">The measured height of this layout.</param>
399 protected void SetMeasuredDimensions( MeasuredSize measuredWidth, MeasuredSize measuredHeight )
401 Log.Info("NUI", "For " + Owner.Name + " MeasuredWidth:" + measuredWidth.Size.AsRoundedValue()
402 + " MeasureHeight:" + measuredHeight.Size.AsRoundedValue() + "\n");
404 MeasuredWidth = measuredWidth;
405 MeasuredHeight = measuredHeight;
406 Flags = Flags | LayoutFlags.MeasuredDimensionSet;
410 /// Measure the layout and its content to determine the measured width and the
411 /// measured height.<br />
412 /// The base class implementation of measure defaults to the background size,
413 /// unless a larger size is allowed by the MeasureSpec. Subclasses should
414 /// override to provide better measurements of their content.<br />
415 /// If this method is overridden, it is the subclass's responsibility to make sure the
416 /// measured height and width are at least the layout's minimum height and width.<br />
418 /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
419 /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
420 protected virtual void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
422 // GetDefaultSize will limit the MeasureSpec to the suggested minimumWidth and minimumHeight
423 SetMeasuredDimensions( GetDefaultSize( SuggestedMinimumWidth, widthMeasureSpec ),
424 GetDefaultSize( SuggestedMinimumHeight, heightMeasureSpec ) );
428 /// Called from Layout() when this layout should assign a size and position to each of its children. <br />
429 /// Derived classes with children should override this method and call Layout() on each of their children. <br />
431 /// <param name="changed">This is a new size or position for this layout.</param>
432 /// <param name="left">Left position, relative to parent.</param>
433 /// <param name="top">Top position, relative to parent.</param>
434 /// <param name="right">Right position, relative to parent.</param>
435 /// <param name="bottom">Bottom position, relative to parent.</param>
436 protected virtual void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
441 /// Virtual method to inform derived classes when the layout size changed. <br />
443 /// <param name="newSize">The new size of the layout.</param>
444 /// <param name="oldSize">The old size of the layout.</param>
445 protected virtual void OnSizeChanged(LayoutSize newSize, LayoutSize oldSize)
450 /// Virtual method to allow derived classes to remove any children before it is removed from
453 public virtual void OnUnparent()
458 /// Virtual method called when this Layout is attached to it's owner.
459 /// Allows derived layouts to take ownership of child Views and connect to any Owner signals required.
461 protected virtual void OnAttachedToOwner()
465 private bool SetFrame(LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
467 bool changed = false;
469 if( _left != left || _right != right || _top != top || _bottom != bottom )
474 LayoutLength oldWidth = _right - _left;
475 LayoutLength oldHeight = _bottom - _top;
476 LayoutLength newWidth = right - left;
477 LayoutLength newHeight = bottom - top;
478 bool sizeChanged = ( newWidth != oldWidth ) || ( newHeight != oldHeight );
485 // Set actual positions of View.
486 Owner.SetX(_left.AsRoundedValue());
487 Owner.SetY(_top.AsRoundedValue());
488 Owner.SetSize((int)newWidth.AsRoundedValue(), (int)newHeight.AsRoundedValue());
490 Log.Info("NUI", "Frame set for " + Owner.Name + " to left:" + _left.AsRoundedValue() + " top:"
491 + _top.AsRoundedValue() + " width: " + newWidth.AsRoundedValue() + " height: "
492 + newHeight.AsRoundedValue() + "\n");