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.
18 using System.Collections.Generic;
19 using System.ComponentModel;
20 using Tizen.NUI.BaseComponents;
26 /// [Draft] LayoutGroup class providing container functionality.
28 internal class LayoutGroup : LayoutItem, ILayoutParent
30 protected List<LayoutItem> _children{ get;} // Children of this LayoutGroup
33 /// [Draft] Constructor
37 _children = new List<LayoutItem>();
41 /// [Draft] Constructor setting the owner of this LayoutGroup.
43 /// <param name="owner">Owning View of this layout, currently a View but may be extending for Windows/Layers.</param>
44 public LayoutGroup(View owner) : base(owner)
46 _children = new List<LayoutItem>();
50 /// From ILayoutParent.<br />
52 public virtual void Add(LayoutItem childLayout)
54 Log.Info("NUI","Add Layout:" + childLayout.Owner.Name + " to Layout:" + Owner.Name + "\n");
55 _children.Add(childLayout);
56 childLayout.SetParent(this);
57 OnChildAdd(childLayout);
62 /// Remove all layout children.<br />
64 public void RemoveAll()
66 Log.Info("NUI","Removing:" + _children.Count + "\n");
67 foreach( LayoutItem childLayout in _children )
69 childLayout.Owner = null;
72 // todo ensure child LayoutItems are still not parented to this group.
77 /// From ILayoutParent
79 public virtual void Remove(LayoutItem layoutItem)
81 foreach( LayoutItem childLayout in _children.ToList() )
83 if( childLayout == layoutItem )
85 childLayout.SetParent(null);
86 _children.Remove(childLayout);
92 private void AddChildToLayoutGroup(View child)
94 Log.Info("NUI", "Adding child View:" + child.Name + " to Layout:" + Owner?.Name + "\n");
96 // Only give children a layout if their parent is an explicit container or a pure View.
97 // Pure View meaning not derived from a View, e.g a Legacy container.
98 // layoutSet flag is true when the View became a layout using the set Layout API opposed to automatically due to it's parent.
99 // First time the set Layout API is used by any View the Window no longer has layoutingDisabled.
101 // If child already has a Layout then don't change it.
102 if (! View.layoutingDisabled && (null == child.Layout))
104 // Only wrap View with a Layout if a child a pure View or Layout explicitly set on this Layout
105 if ((true == Owner.layoutSet || GetType() == typeof(View)))
107 Log.Info("NUI", "Parent[" + Owner.Name + "] Layout set[" + Owner.layoutSet.ToString() + "] Pure View[" + (!Owner.layoutSet).ToString() + "]\n");
108 // If child of this layout is a pure View then assign it a LayoutGroup
109 // If the child is derived from a View then it may be a legacy or existing container hence will do layouting itself.
110 if (child.GetType() == typeof(View))
112 Log.Info("NUI", "Creating LayoutGroup for " + child.Name + "]\n");
113 child.Layout = new LayoutGroup();
117 // Adding child as a leaf, layouting will not propagate past this child.
118 // Legacy containers will be a LayoutItems too and layout their children how they wish.
119 Log.Info("NUI", "Creating LayoutItem for " + child.Name + "\n");
120 child.Layout = new LayoutItem();
126 // Add child layout to this LayoutGroup (Setting parent in the process)
127 if(child.Layout != null)
135 /// If the child has a layout then it is removed from the parent layout.
137 /// <param name="child">Child to remove.true</param>
138 private void RemoveChildFromLayoutGroup(View child)
140 Log.Info("NUI", "Removing child View:" + child.Name + " from Layout:" + Owner?.Name + "\n");
142 if(child.Layout != null)
144 Remove(child.Layout);
149 /// Callback for View.ChildAdded event
151 /// <param name="sender">The object triggering the event.</param>
152 /// <param name="childAddedEvent">Arguments from the event.</param>
153 void OnChildAddedToOwner(object sender, View.ChildAddedEventArgs childAddedEvent)
155 AddChildToLayoutGroup(childAddedEvent.Added);
159 /// Callback for View.ChildRemoved event
161 /// <param name="sender">The object triggering the event.</param>
162 /// <param name="childRemovedEvent">Arguments from the event.</param>
163 void OnChildRemovedFromOwner(object sender, View.ChildRemovedEventArgs childRemovedEvent)
165 RemoveChildFromLayoutGroup(childRemovedEvent.Removed);
169 /// Calculate the right measure spec for this child.
170 /// Does the hard part of MeasureChildren: figuring out the MeasureSpec to
171 /// pass to a particular child. This method figures out the right MeasureSpec
172 /// for one dimension (height or width) of one child view.<br />
174 /// <param name="parentMeasureSpec">The requirements for this view. MeasureSpecification.</param>
175 /// <param name="padding">The padding of this view for the current dimension and margins, if applicable. LayoutLength.</param>
176 /// <param name="childDimension"> How big the child wants to be in the current dimension. LayoutLength.</param>
177 /// <returns>a MeasureSpec for the child.</returns>
178 public static MeasureSpecification GetChildMeasureSpecification(MeasureSpecification parentMeasureSpec, LayoutLength padding, LayoutLength childDimension)
180 MeasureSpecification.ModeType specMode = parentMeasureSpec.Mode;
181 MeasureSpecification.ModeType resultMode = MeasureSpecification.ModeType.Unspecified;
182 LayoutLength resultSize = new LayoutLength(Math.Max( 0.0f, (parentMeasureSpec.Size.AsDecimal() - padding.AsDecimal() ) )); // reduce available size by the owners padding
186 // Parent has imposed an exact size on us
187 case MeasureSpecification.ModeType.Exactly:
189 if ((int)childDimension.AsRoundedValue() == LayoutParamPolicies.MatchParent)
191 // Child wants to be our size. So be it.
192 resultMode = MeasureSpecification.ModeType.Exactly;
194 else if ((int)childDimension.AsRoundedValue() == LayoutParamPolicies.WrapContent)
196 // Child wants to determine its own size. It can't be
198 resultMode = MeasureSpecification.ModeType.AtMost;
202 resultSize = childDimension;
203 resultMode = MeasureSpecification.ModeType.Exactly;
209 // Parent has imposed a maximum size on us
210 case MeasureSpecification.ModeType.AtMost:
212 if (childDimension.AsRoundedValue() == LayoutParamPolicies.MatchParent)
214 // Child wants to be our size, but our size is not fixed.
215 // Constrain child to not be bigger than us.
216 resultMode = MeasureSpecification.ModeType.AtMost;
218 else if (childDimension.AsRoundedValue() == LayoutParamPolicies.WrapContent)
220 // Child wants to determine its own size. It can't be
222 resultMode = MeasureSpecification.ModeType.AtMost;
226 // Child wants a specific size... so be it
227 resultSize = childDimension + padding;
228 resultMode = MeasureSpecification.ModeType.Exactly;
234 // Parent asked to see how big we want to be
235 case MeasureSpecification.ModeType.Unspecified:
238 if ((childDimension.AsRoundedValue() == LayoutParamPolicies.MatchParent))
240 // Child wants to be our size... find out how big it should be
241 resultMode = MeasureSpecification.ModeType.Unspecified;
243 else if (childDimension.AsRoundedValue() == (LayoutParamPolicies.WrapContent))
245 // Child wants to determine its own size.... find out how big
247 resultMode = MeasureSpecification.ModeType.Unspecified;
251 // Child wants a specific size... let him have it
252 resultSize = childDimension + padding;
253 resultMode = MeasureSpecification.ModeType.Exactly;
259 Log.Info("NUI", "MeasureSpecification resultSize:" + resultSize.AsRoundedValue()
260 + " resultMode:" + resultMode + "\n");
261 return new MeasureSpecification( resultSize, resultMode );
265 /// Measure the layout and its content to determine the measured width and the measured height.<br />
266 /// If this method is overridden, it is the subclass's responsibility to make
267 /// sure the measured height and width are at least the layout's minimum height
268 /// and width. <br />
270 /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
271 /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
272 protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
274 Log.Info("NUI", "OnMeasure\n");
275 LayoutLength measuredWidth = new LayoutLength(0.0f);
276 LayoutLength measuredHeight = new LayoutLength(0.0f);
278 // Layout takes size of largest child width and largest child height dimensions
279 foreach( LayoutItem childLayout in _children )
281 if( childLayout != null )
283 MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
284 LayoutLength childWidth = new LayoutLength(childLayout.MeasuredWidth.Size);
285 LayoutLength childHeight = new LayoutLength( childLayout.MeasuredHeight.Size);
286 Extents childMargin = childLayout.Owner.Margin;
287 measuredWidth = new LayoutLength(Math.Max( measuredWidth.AsDecimal(), childWidth.AsDecimal() + childMargin.Start + childMargin.End));
288 measuredHeight = new LayoutLength(Math.Max( measuredHeight.AsDecimal(), childHeight.AsDecimal() + childMargin.Top + childMargin.Bottom));
292 if( 0 == _children.Count )
294 // Must be a leaf as has no children
295 measuredWidth = GetDefaultSize( SuggestedMinimumWidth, widthMeasureSpec );
296 measuredHeight = GetDefaultSize( SuggestedMinimumHeight, heightMeasureSpec );
299 SetMeasuredDimensions( new MeasuredSize( measuredWidth, MeasuredSize.StateType.MeasuredSizeOK ),
300 new MeasuredSize( measuredHeight, MeasuredSize.StateType.MeasuredSizeOK ) );
304 /// Called from Layout() when this layout should assign a size and position to each of its children.<br />
305 /// Derived classes with children should override this method and call Layout() on each of their children.<br />
307 /// <param name="changed">This is a new size or position for this layout.</param>
308 /// <param name="left">Left position, relative to parent.</param>
309 /// <param name="top"> Top position, relative to parent.</param>
310 /// <param name="right">Right position, relative to parent.</param>
311 /// <param name="bottom">Bottom position, relative to parent.</param>
312 protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
314 Log.Info("NUI", "OnLayout\n");
315 foreach( LayoutItem childLayout in _children )
317 if( childLayout !=null )
319 // Use position if explicitly set to child otherwise will be top left.
320 var childLeft = new LayoutLength( childLayout.Owner.Position2D.X );
321 var childTop = new LayoutLength( childLayout.Owner.Position2D.Y );
327 // Margin and Padding only supported when child anchor point is TOP_LEFT.
328 if ( owner.PivotPoint == PivotPoint.TopLeft || ( owner.PositionUsesPivotPoint == false ) )
330 childLeft = childLeft + owner.Padding.Start + childLayout.Owner.Margin.Start;
331 childTop = childTop + owner.Padding.Top + childLayout.Owner.Margin.Top;
334 childLayout.Layout( childLeft, childTop, childLeft + childLayout.MeasuredWidth.Size, childTop + childLayout.MeasuredHeight.Size );
340 /// Overridden method called when the layout size changes.<br />
342 /// <param name="newSize">The new size of the layout.</param>
343 /// <param name="oldSize">The old size of the layout.</param>
344 protected override void OnSizeChanged(LayoutSize newSize, LayoutSize oldSize)
350 /// Overridden method called when the layout is attached to an owner.<br />
352 protected override void OnAttachedToOwner()
354 Log.Info("NUI", "Attaching to Owner:" + Owner.Name + "\n");
355 // Layout takes ownership of it's owner's children.
356 foreach (View view in Owner.Children)
358 AddChildToLayoutGroup(view);
361 // Layout attached to owner so connect to ChildAdded and ChildRemoved signals.
362 Owner.ChildAdded += OnChildAddedToOwner;
363 Owner.ChildRemoved += OnChildRemovedFromOwner;
366 // Virtual Methods that can be overridden by derived classes.
369 /// Callback when child is added to container.<br />
370 /// Derived classes can use this to set their own child properties on the child layout's owner.<br />
372 /// <param name="child">The Layout child.</param>
373 protected virtual void OnChildAdd(LayoutItem child)
378 /// Callback when child is removed from container.<br />
380 /// <param name="child">The Layout child.</param>
381 protected virtual void OnChildRemove(LayoutItem child)
386 /// Ask all of the children of this view to measure themselves, taking into
387 /// account both the MeasureSpec requirements for this view and its padding.<br />
388 /// The heavy lifting is done in GetChildMeasureSpec.<br />
390 /// <param name="widthMeasureSpec">The width requirements for this view.</param>
391 /// <param name="heightMeasureSpec">The height requirements for this view.</param>
392 protected virtual void MeasureChildren(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
394 foreach( LayoutItem childLayout in _children )
396 MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpec );
401 /// Ask one of the children of this view to measure itself, taking into
402 /// account both the MeasureSpec requirements for this view and its padding.<br />
403 /// The heavy lifting is done in GetChildMeasureSpec.<br />
405 /// <param name="child">The child to measure.</param>
406 /// <param name="parentWidthMeasureSpec">The width requirements for this view.</param>
407 /// <param name="parentHeightMeasureSpec">The height requirements for this view.</param>
408 protected virtual void MeasureChild(LayoutItem child, MeasureSpecification parentWidthMeasureSpec, MeasureSpecification parentHeightMeasureSpec)
410 View childOwner = child.Owner;
411 Log.Info("NUI", "LayoutGroup MeasureChild:" + childOwner.Name
412 + " child widthSpecification policy:" + childOwner.WidthSpecification
413 + " child heightSpecification policy:" + childOwner.HeightSpecification + "\n");
415 Extents padding = childOwner.Padding; // Padding of this layout's owner, not of the child being measured.
417 MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification( parentWidthMeasureSpec,
418 new LayoutLength(padding.Start + padding.End ),
419 new LayoutLength(childOwner.WidthSpecification) );
421 MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification( parentHeightMeasureSpec,
422 new LayoutLength(padding.Top + padding.Bottom),
423 new LayoutLength(childOwner.HeightSpecification) );
425 child.Measure( childWidthMeasureSpec, childHeightMeasureSpec );
429 /// Ask one of the children of this view to measure itself, taking into
430 /// account both the MeasureSpec requirements for this view and its padding.<br />
431 /// and margins. The child must have MarginLayoutParams The heavy lifting is
432 /// done in GetChildMeasureSpecification.<br />
434 /// <param name="child">The child to measure.</param>
435 /// <param name="parentWidthMeasureSpec">The width requirements for this view.</param>
436 /// <param name="widthUsed">Extra space that has been used up by the parent horizontally (possibly by other children of the parent).</param>
437 /// <param name="parentHeightMeasureSpec">The height requirements for this view.</param>
438 /// <param name="heightUsed">Extra space that has been used up by the parent vertically (possibly by other children of the parent).</param>
439 protected virtual void MeasureChildWithMargins(LayoutItem child, MeasureSpecification parentWidthMeasureSpec, LayoutLength widthUsed, MeasureSpecification parentHeightMeasureSpec, LayoutLength heightUsed)
441 View childOwner = child.Owner;
442 int desiredWidth = childOwner.WidthSpecification;
443 int desiredHeight = childOwner.HeightSpecification;
445 Extents padding = childOwner.Padding; // Padding of this layout's owner, not of the child being measured.
447 MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification( parentWidthMeasureSpec,
448 new LayoutLength( padding.Start + padding.End ) +
449 widthUsed, new LayoutLength(desiredWidth) );
452 MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification( parentHeightMeasureSpec,
453 new LayoutLength( padding.Top + padding.Bottom )+
454 heightUsed, new LayoutLength(desiredHeight) );
456 child.Measure( childWidthMeasureSpec, childHeightMeasureSpec );