9ec42deba631f53d3e699ad5aee75b5f1392205a
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / Layouting / LayoutGroup.cs
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17 using System;
18 using System.Collections.Generic;
19 using System.ComponentModel;
20 using System.Linq;
21
22 using Tizen.NUI.BaseComponents;
23 using Tizen.NUI.Binding.Internals;
24 using static Tizen.NUI.Binding.BindableObject;
25
26 namespace Tizen.NUI
27 {
28     /// <summary>
29     /// [Draft] LayoutGroup class providing container functionality.
30     /// </summary>
31     public class LayoutGroup : LayoutItem, ILayoutParent
32     {
33         /// <summary>
34         /// [Draft] List of child layouts in this container.
35         /// </summary>
36         /// <since_tizen> 6 </since_tizen>
37         protected List<LayoutItem> LayoutChildren { get; } // Children of this LayoutGroup
38
39         /// <summary>
40         /// [Draft] Constructor
41         /// </summary>
42         /// <since_tizen> 6 </since_tizen>
43         public LayoutGroup()
44         {
45             LayoutChildren = new List<LayoutItem>();
46         }
47
48         /// <summary>
49         /// returns an enumerable collection of the child layouts that owner's <see cref="View.ExcludeLayouting"/> is false.
50         /// </summary>
51         /// <returns>An enumerable collection of the child layouts that affected by this layout.</returns>
52         [EditorBrowsable(EditorBrowsableState.Never)]
53         protected IEnumerable<LayoutItem> IterateLayoutChildren()
54         {
55             return LayoutChildren.Where<LayoutItem>(childLayout => childLayout.SetPositionByLayout);
56         }
57
58         /// <summary>
59         /// From ILayoutParent.<br />
60         /// </summary>
61         /// <exception cref="ArgumentNullException"> Thrown when childLayout is null. </exception>
62         /// <since_tizen> 6 </since_tizen>
63         /// <param name="childLayout">LayoutItem to add to the layout group.</param>
64         public virtual void Add(LayoutItem childLayout)
65         {
66             if (null == childLayout)
67             {
68                 throw new ArgumentNullException(nameof(childLayout));
69             }
70             LayoutChildren.Add(childLayout);
71             childLayout.SetParent(this);
72             // Child added to use a Add transition.
73             childLayout.ConditionForAnimation = ConditionForAnimation | TransitionCondition.Add;
74             // Child's parent sets all other children not being added to a ChangeOnAdd transition.
75             SetConditionsForAnimationOnLayoutGroup(TransitionCondition.ChangeOnAdd);
76             OnChildAdd(childLayout);
77             RequestLayout();
78         }
79
80         /// <summary>
81         /// Remove all layout children.<br />
82         /// </summary>
83         /// <since_tizen> 6 </since_tizen>
84         public void RemoveAll()
85         {
86             foreach (LayoutItem childLayout in LayoutChildren)
87             {
88                 childLayout.ConditionForAnimation = ConditionForAnimation | TransitionCondition.Remove;
89                 childLayout.Owner = null;
90             }
91             LayoutChildren.Clear();
92             // todo ensure child LayoutItems are still not parented to this group.
93             RequestLayout();
94         }
95
96         /// <summary>
97         /// From ILayoutParent
98         /// </summary>
99         /// <param name="layoutItem">LayoutItem to remove from the layout group.</param>
100         /// <since_tizen> 6 </since_tizen>
101         public virtual void Remove(LayoutItem layoutItem)
102         {
103             bool childRemoved = false;
104             foreach (LayoutItem childLayout in LayoutChildren.ToList())
105             {
106                 if (childLayout == layoutItem)
107                 {
108                     childLayout.ClearReplaceFlag();
109                     LayoutChildren.Remove(childLayout);
110
111                     if (LayoutWithTransition)
112                     {
113                         if (!childLayout.IsReplaceFlag())
114                         {
115                             NUIApplication.GetDefaultWindow().LayoutController.AddToRemovalStack(childLayout);
116                         }
117
118                         childLayout.ConditionForAnimation = childLayout.ConditionForAnimation | TransitionCondition.Remove;
119                         // Add LayoutItem to the transition stack so can animate it out.
120                         NUIApplication.GetDefaultWindow().LayoutController.AddTransitionDataEntry(new LayoutData(layoutItem, ConditionForAnimation, 0, 0, 0, 0));
121                     }
122
123                     // Reset condition for animation ready for next transition when required.
124                     // SetFrame usually would do this but this LayoutItem is being removed.
125                     childLayout.ConditionForAnimation = TransitionCondition.Unspecified;
126                     childRemoved = true;
127
128                     break;
129                 }
130             }
131
132             if (childRemoved)
133             {
134                 // If child removed then set all siblings not being added to a ChangeOnRemove transition.
135                 SetConditionsForAnimationOnLayoutGroup(TransitionCondition.ChangeOnRemove);
136             }
137             OnChildRemove(layoutItem);
138             RequestLayout();
139         }
140
141
142         /// <summary>
143         /// Sets the sibling order of the layout item so the layout can be defined within the same parent.
144         /// </summary>
145         /// <param name="order">the sibling order of the layout item</param>
146         /// <since_tizen> 6 </since_tizen>
147         /// This will be public opened in tizen_next after ACR done. Before ACR, need to be hidden as inhouse API.
148         [EditorBrowsable(EditorBrowsableState.Never)]
149         public void ChangeLayoutSiblingOrder(int order)
150         {
151             if (Owner != null)
152             {
153                 var ownerParent = Owner.GetParent() as View;
154                 if (ownerParent != null)
155                 {
156                     var parent = ownerParent.Layout as LayoutGroup;
157
158                     if (parent != null && parent.LayoutChildren.Count > order)
159                     {
160                         parent.LayoutChildren.Remove(this);
161                         parent.LayoutChildren.Insert(order, this);
162                     }
163                 }
164             }
165             RequestLayout();
166         }
167
168         // Attaches to View ChildAdded signal so called when a View is added to a view.
169         private void AddChildToLayoutGroup(View child)
170         {
171             if (View.LayoutingDisabled)
172             {
173                 return;
174             }
175             // Only give children a layout if their parent is an explicit container or a pure View.
176             // Pure View meaning not derived from a View, e.g a Legacy container.
177             // layoutSet flag is true when the View became a layout using the set Layout API opposed to automatically due to it's parent.
178             // First time the set Layout API is used by any View the Window no longer has layoutingDisabled.
179
180             // If child already has a Layout then don't change it.
181             if (null == child.Layout)
182             {
183                 // If child view does not have Layout, then default layout is automatically set to the child view.
184                 // The Layout is automatically added to this LayoutGroup when child view sets Layout.
185                 child.Layout = child.CreateDefaultLayout();
186             }
187             else
188             {
189                 Add(child.Layout);
190             }
191             // Parent transitions are not attached to children.
192         }
193
194         /// <summary>
195         /// If the child has a layout then it is removed from the parent layout.
196         /// </summary>
197         /// <param name="child">Child View to remove.</param>
198         internal void RemoveChildFromLayoutGroup(View child)
199         {
200             if (child.Layout != null)
201             {
202                 Remove(child.Layout);
203             }
204         }
205
206         /// <summary>
207         /// Set all children in a LayoutGroup to the supplied condition.
208         /// Children with Add or Remove conditions should not be changed.
209         /// </summary>
210         private void SetConditionsForAnimationOnLayoutGroup(TransitionCondition conditionToSet)
211         {
212             foreach (LayoutItem childLayout in LayoutChildren)
213             {
214                 switch (conditionToSet)
215                 {
216                     case TransitionCondition.ChangeOnAdd:
217                         {
218                             // If other children also being added (TransitionCondition.Add) then do not change their
219                             // conditions, Continue to use their Add transitions.
220                             if (childLayout.ConditionForAnimation.HasFlag(TransitionCondition.Add))
221                             {
222                                 break;  // Child being Added so don't update it's condition
223                             }
224                             else
225                             {
226                                 // Set siblings for the child being added to use the ChangeOnAdd transition.
227                                 childLayout.ConditionForAnimation = TransitionCondition.ChangeOnAdd;
228                             }
229                             break;
230                         }
231                     case TransitionCondition.ChangeOnRemove:
232                         {
233                             if (childLayout.ConditionForAnimation.HasFlag(TransitionCondition.Remove))
234                             {
235                                 break; // Child being Removed so don't update it's condition
236                             }
237                             else
238                             {
239                                 childLayout.ConditionForAnimation = TransitionCondition.ChangeOnRemove;
240                             }
241                             break;
242                         }
243                     case TransitionCondition.LayoutChanged:
244                         {
245                             childLayout.ConditionForAnimation = TransitionCondition.LayoutChanged;
246                             break;
247                         }
248                 }
249             }
250
251         }
252
253         /// <summary>
254         /// Callback for View.ChildAdded event
255         /// </summary>
256         /// <param name="sender">The object triggering the event.</param>
257         /// <param name="childAddedEvent">Arguments from the event.</param>
258         void OnChildAddedToOwner(object sender, View.ChildAddedEventArgs childAddedEvent)
259         {
260             AddChildToLayoutGroup(childAddedEvent.Added);
261         }
262
263         /// <summary>
264         /// Callback for View.ChildRemoved event
265         /// </summary>
266         /// <param name="sender">The object triggering the event.</param>
267         /// <param name="childRemovedEvent">Arguments from the event.</param>
268         void OnChildRemovedFromOwner(object sender, View.ChildRemovedEventArgs childRemovedEvent)
269         {
270             RemoveChildFromLayoutGroup(childRemovedEvent.Removed);
271         }
272
273         /// <summary>
274         /// Calculate the right measure spec for this child.
275         /// Does the hard part of MeasureChildren: figuring out the MeasureSpec to
276         /// pass to a particular child. This method figures out the right MeasureSpec
277         /// for one dimension (height or width) of one child view.<br />
278         /// </summary>
279         /// <param name="parentMeasureSpec">The requirements for this view. MeasureSpecification.</param>
280         /// <param name="padding">The padding of this view for the current dimension and margins, if applicable. LayoutLength.</param>
281         /// <param name="childDimension"> How big the child wants to be in the current dimension. LayoutLength.</param>
282         /// <returns>a MeasureSpec for the child.</returns>
283         public static MeasureSpecification GetChildMeasureSpecification(MeasureSpecification parentMeasureSpec, LayoutLength padding, LayoutLength childDimension)
284         {
285             MeasureSpecification.ModeType specMode = parentMeasureSpec.Mode;
286             MeasureSpecification.ModeType resultMode = MeasureSpecification.ModeType.Unspecified;
287
288             // Child only can use parent's size without parent's padding and own margin.
289             LayoutLength resultSize = new LayoutLength(Math.Max(0.0f, (parentMeasureSpec.Size - padding).AsDecimal()));
290             switch (specMode)
291             {
292                 // Parent has imposed an exact size on us
293                 case MeasureSpecification.ModeType.Exactly:
294                     {
295                         if ((int)childDimension.AsRoundedValue() == LayoutParamPolicies.MatchParent)
296                         {
297                             resultMode = MeasureSpecification.ModeType.Exactly;
298                         }
299                         else if ((int)childDimension.AsRoundedValue() == LayoutParamPolicies.WrapContent)
300                         {
301                             resultMode = MeasureSpecification.ModeType.AtMost;
302                         }
303                         else
304                         {
305                             resultSize = childDimension;
306                             resultMode = MeasureSpecification.ModeType.Exactly;
307                         }
308
309                         break;
310                     }
311
312                 // Parent has imposed a maximum size on us
313                 case MeasureSpecification.ModeType.AtMost:
314                     {
315                         if (childDimension.AsRoundedValue() == LayoutParamPolicies.MatchParent)
316                         {
317                             // Crashed. Cannot calculate.
318
319                             // Child wants to be our size, but our size is not fixed.
320                             // Constrain child to not be bigger than us.
321                             resultMode = MeasureSpecification.ModeType.AtMost;
322                         }
323                         else if (childDimension.AsRoundedValue() == LayoutParamPolicies.WrapContent)
324                         {
325                             // Child wants to determine its own size. It can't be
326                             // bigger than us.
327
328                             // Don't need parent's size. Size of this child will be determined by its children.
329                             resultMode = MeasureSpecification.ModeType.AtMost;
330                         }
331                         else
332                         {
333                             // Child wants a specific size... so be it
334                             resultSize = childDimension;
335                             resultMode = MeasureSpecification.ModeType.Exactly;
336                         }
337
338                         break;
339                     }
340
341                 // Parent asked to see how big we want to be
342                 case MeasureSpecification.ModeType.Unspecified:
343                     {
344                         if ((childDimension.AsRoundedValue() == LayoutParamPolicies.MatchParent))
345                         {
346                             // Child wants to be our size... find out how big it should be
347
348                             // There is no one who has exact size in parent hierarchy.
349                             // Cannot calculate.
350                             resultMode = MeasureSpecification.ModeType.Unspecified;
351                         }
352                         else if (childDimension.AsRoundedValue() == (LayoutParamPolicies.WrapContent))
353                         {
354                             // Child wants to determine its own size.... find out how big
355                             // it should be
356                             resultMode = MeasureSpecification.ModeType.Unspecified;
357                         }
358                         else
359                         {
360                             // Child wants a specific size... let him have it
361                             resultSize = childDimension;
362                             resultMode = MeasureSpecification.ModeType.Exactly;
363                         }
364                         break;
365                     }
366             } // switch
367
368             return new MeasureSpecification(resultSize, resultMode);
369         }
370
371         /// <summary>
372         /// Measure the layout and its content to determine the measured width and the measured height.<br />
373         /// If this method is overridden, it is the subclass's responsibility to make
374         /// sure the measured height and width are at least the layout's minimum height
375         /// and width. <br />
376         /// </summary>
377         /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
378         /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
379         /// <since_tizen> 6 </since_tizen>
380         protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
381         {
382             LayoutLength measuredWidth = new LayoutLength(0.0f);
383             LayoutLength measuredHeight = new LayoutLength(0.0f);
384
385             // Layout takes size of largest child width and largest child height dimensions
386             foreach (LayoutItem childLayout in LayoutChildren)
387             {
388                 if (childLayout != null)
389                 {
390                     MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
391                     LayoutLength childWidth = new LayoutLength(childLayout.MeasuredWidth.Size);
392                     LayoutLength childHeight = new LayoutLength(childLayout.MeasuredHeight.Size);
393
394                     Extents childMargin = childLayout.Margin;
395                     measuredWidth = new LayoutLength(Math.Max(measuredWidth.AsDecimal(), childWidth.AsDecimal() + childMargin.Start + childMargin.End));
396                     measuredHeight = new LayoutLength(Math.Max(measuredHeight.AsDecimal(), childHeight.AsDecimal() + childMargin.Top + childMargin.Bottom));
397                 }
398             }
399
400             if (0 == LayoutChildren.Count)
401             {
402                 // Must be a leaf as has no children
403                 measuredWidth = GetDefaultSize(SuggestedMinimumWidth, widthMeasureSpec);
404                 measuredHeight = GetDefaultSize(SuggestedMinimumHeight, heightMeasureSpec);
405             }
406
407             SetMeasuredDimensions(new MeasuredSize(measuredWidth, MeasuredSize.StateType.MeasuredSizeOK),
408                                    new MeasuredSize(measuredHeight, MeasuredSize.StateType.MeasuredSizeOK));
409         }
410
411         internal override void OnMeasureIndependentChildren(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
412         {
413             foreach (var childLayout in LayoutChildren)
414             {
415                 if (!childLayout.SetPositionByLayout)
416                 {
417                     MeasureChildWithoutPadding(childLayout, widthMeasureSpec, heightMeasureSpec);
418                 }
419             }
420         }
421
422         /// <summary>
423         /// Called from Layout() when this layout should assign a size and position to each of its children.<br />
424         /// Derived classes with children should override this method and call Layout() on each of their children.<br />
425         /// </summary>
426         /// <param name="changed">This is a new size or position for this layout.</param>
427         /// <param name="left">Left position, relative to parent.</param>
428         /// <param name="top"> Top position, relative to parent.</param>
429         /// <param name="right">Right position, relative to parent.</param>
430         /// <param name="bottom">Bottom position, relative to parent.</param>
431         /// <since_tizen> 6 </since_tizen>
432         protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
433         {
434             foreach (LayoutItem childLayout in LayoutChildren)
435             {
436                 if (childLayout != null)
437                 {
438                     // Use position if explicitly set to child otherwise will be top left.
439                     var childLeft = new LayoutLength(childLayout.Owner.PositionX);
440                     var childTop = new LayoutLength(childLayout.Owner.PositionY);
441
442                     View owner = Owner;
443
444                     if (owner != null)
445                     {
446                         // Margin and Padding only supported when child anchor point is TOP_LEFT.
447                         if (owner.PivotPoint == PivotPoint.TopLeft || (owner.PositionUsesPivotPoint == false))
448                         {
449                             childLeft = childLeft + owner.Padding.Start + childLayout.Margin.Start;
450                             childTop = childTop + owner.Padding.Top + childLayout.Margin.Top;
451                         }
452                     }
453                     childLayout.Layout(childLeft, childTop, childLeft + childLayout.MeasuredWidth.Size, childTop + childLayout.MeasuredHeight.Size);
454                 }
455             }
456         }
457
458         /// <summary>
459         /// Layout independent children those Owners have true ExcludeLayouting. <br />
460         /// These children are required not to be affected by this layout. <br />
461         /// </summary>
462         internal override void OnLayoutIndependentChildren(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
463         {
464             foreach (var childLayout in LayoutChildren)
465             {
466                 if (!childLayout.SetPositionByLayout)
467                 {
468                     LayoutLength childWidth = childLayout.MeasuredWidth.Size;
469                     LayoutLength childHeight = childLayout.MeasuredHeight.Size;
470
471                     LayoutLength childPositionX = new LayoutLength(childLayout.Owner.PositionX);
472                     LayoutLength childPositionY = new LayoutLength(childLayout.Owner.PositionY);
473
474                     childLayout.Layout(childPositionX, childPositionY, childPositionX + childWidth, childPositionY + childHeight, true);
475                 }
476             }
477         }
478
479         /// <summary>
480         /// Overridden method called when the layout is attached to an owner.<br />
481         /// </summary>
482         /// <since_tizen> 6 </since_tizen>
483         protected override void OnAttachedToOwner()
484         {
485             // Layout takes ownership of it's owner's children.
486             foreach (View view in Owner.Children)
487             {
488                 AddChildToLayoutGroup(view);
489             }
490
491             // Connect to owner ChildAdded signal.
492             Owner.ChildAdded += OnChildAddedToOwner;
493
494             // Removing Child from the owners View will directly call the LayoutGroup removal API.
495         }
496
497         /// <summary>
498         /// Virtual method to allow derived classes to remove any children before it is removed from
499         /// its parent.
500         /// </summary>
501         [EditorBrowsable(EditorBrowsableState.Never)]
502         protected override void OnUnparent()
503         {
504             // Disconnect to owner ChildAdded signal.
505             Owner.ChildAdded -= OnChildAddedToOwner;
506         }
507
508         // Virtual Methods that can be overridden by derived classes.
509
510         /// <summary>
511         /// Callback when child is added to container.<br />
512         /// Derived classes can use this to set their own child properties on the child layout's owner.<br />
513         /// </summary>
514         /// <param name="child">The Layout child.</param>
515         /// <since_tizen> 6 </since_tizen>
516         protected virtual void OnChildAdd(LayoutItem child)
517         {
518         }
519
520         /// <summary>
521         /// Callback when child is removed from container.<br />
522         /// </summary>
523         /// <param name="child">The Layout child.</param>
524         /// <since_tizen> 6 </since_tizen>
525         protected virtual void OnChildRemove(LayoutItem child)
526         {
527         }
528
529         /// <summary>
530         /// Ask all of the children of this view to measure themselves, taking into
531         /// account both the MeasureSpec requirements for this view and its padding.<br />
532         /// The heavy lifting is done in GetChildMeasureSpec.<br />
533         /// </summary>
534         /// <param name="widthMeasureSpec">The width requirements for this view.</param>
535         /// <param name="heightMeasureSpec">The height requirements for this view.</param>
536         /// <since_tizen> 6 </since_tizen>
537         protected virtual void MeasureChildren(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
538         {
539             foreach (LayoutItem childLayout in LayoutChildren)
540             {
541                 MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
542             }
543         }
544
545         /// <summary>
546         /// Ask one of the children of this view to measure itself, taking into
547         /// account both the MeasureSpec requirements for this view and its padding.<br />
548         /// The heavy lifting is done in GetChildMeasureSpecification.<br />
549         /// </summary>
550         /// <param name="child">The child to measure.</param>
551         /// <param name="parentWidthMeasureSpec">The width requirements for this view.</param>
552         /// <param name="parentHeightMeasureSpec">The height requirements for this view.</param>
553         /// <exception cref="ArgumentNullException"> Thrown when child is null. </exception>
554         /// <since_tizen> 6 </since_tizen>
555         protected virtual void MeasureChild(LayoutItem child, MeasureSpecification parentWidthMeasureSpec, MeasureSpecification parentHeightMeasureSpec)
556         {
557             if (null == child)
558             {
559                 throw new ArgumentNullException(nameof(child));
560             }
561
562             View childOwner = child.Owner;
563
564             MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(
565                         new MeasureSpecification(new LayoutLength(parentWidthMeasureSpec.Size), parentWidthMeasureSpec.Mode),
566                         new LayoutLength(Padding.Start + Padding.End),
567                         new LayoutLength(childOwner.WidthSpecification));
568
569             MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(
570                         new MeasureSpecification(new LayoutLength(parentHeightMeasureSpec.Size), parentHeightMeasureSpec.Mode),
571                         new LayoutLength(Padding.Top + Padding.Bottom),
572                         new LayoutLength(childOwner.HeightSpecification));
573
574             child.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
575         }
576
577         /// <summary>
578         /// Ask one of the children of this view to measure itself, taking into
579         /// account both the MeasureSpec requirements for this view and its padding.<br />
580         /// and margins. The heavy lifting is done in GetChildMeasureSpecification.<br />
581         /// </summary>
582         /// <param name="child">The child to measure.</param>
583         /// <param name="parentWidthMeasureSpec">The width requirements for this view.</param>
584         /// <param name="widthUsed">Extra space that has been used up by the parent horizontally (possibly by other children of the parent).</param>
585         /// <param name="parentHeightMeasureSpec">The height requirements for this view.</param>
586         /// <param name="heightUsed">Extra space that has been used up by the parent vertically (possibly by other children of the parent).</param>
587         /// <exception cref="ArgumentNullException"> Thrown when child is null. </exception>
588         /// <since_tizen> 6 </since_tizen>
589         protected virtual void MeasureChildWithMargins(LayoutItem child, MeasureSpecification parentWidthMeasureSpec, LayoutLength widthUsed, MeasureSpecification parentHeightMeasureSpec, LayoutLength heightUsed)
590         {
591             if (null == child)
592             {
593                 throw new ArgumentNullException(nameof(child));
594             }
595
596             View childOwner = child.Owner;
597             Extents margin = childOwner.Margin;
598
599             MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(
600                         new MeasureSpecification(
601                             new LayoutLength(parentWidthMeasureSpec.Size + widthUsed - (margin.Start + margin.End)),
602                             parentWidthMeasureSpec.Mode),
603                         new LayoutLength(Padding.Start + Padding.End),
604                         new LayoutLength(childOwner.WidthSpecification));
605
606             MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(
607                         new MeasureSpecification(
608                             new LayoutLength(parentHeightMeasureSpec.Size + heightUsed - (margin.Top + margin.Bottom)),
609                             parentHeightMeasureSpec.Mode),
610                         new LayoutLength(Padding.Top + Padding.Bottom),
611                         new LayoutLength(childOwner.HeightSpecification));
612             child.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
613
614         }
615
616         /// <summary>
617         /// Ask one of the children of this view to measure itself, taking into
618         /// account both the MeasureSpec requirements for this view and without its padding.<br />
619         /// and margins. The heavy lifting is done in GetChildMeasureSpecification.<br />
620         /// </summary>
621         /// <param name="child">The child to measure.</param>
622         /// <param name="parentWidthMeasureSpec">The width requirements for this view.</param>
623         /// <param name="parentHeightMeasureSpec">The height requirements for this view.</param>
624         [EditorBrowsable(EditorBrowsableState.Never)]
625         protected void MeasureChildWithoutPadding(LayoutItem child, MeasureSpecification parentWidthMeasureSpec, MeasureSpecification parentHeightMeasureSpec)
626         {
627             View childOwner = child.Owner;
628
629             MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(
630                         new MeasureSpecification(new LayoutLength(parentWidthMeasureSpec.Size), parentWidthMeasureSpec.Mode),
631                         new LayoutLength(0),
632                         new LayoutLength(childOwner.WidthSpecification));
633
634             MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(
635                         new MeasureSpecification(new LayoutLength(parentHeightMeasureSpec.Size), parentHeightMeasureSpec.Mode),
636                         new LayoutLength(0),
637                         new LayoutLength(childOwner.HeightSpecification));
638
639             child.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
640         }
641
642         /// <summary>
643         /// Gets the value that is contained in the attached BindableProperty.
644         /// </summary>
645         /// <typeparam name="T">The return type of property</typeparam>
646         /// <param name="bindable">The bindable object.</param>
647         /// <param name="property">The BindableProperty for which to get the value.</param>
648         /// <returns>The value that is contained in the attached BindableProperty.</returns>
649         [EditorBrowsable(EditorBrowsableState.Never)]
650         public static T GetAttachedValue<T>(Binding.BindableObject bindable, Binding.BindableProperty property)
651         {
652             if (bindable == null)
653                 throw new ArgumentNullException(nameof(bindable));
654
655             return (T)bindable.GetValue(property);
656         }
657
658         /// <summary>
659         /// Sets the value of the attached property.
660         /// </summary>
661         /// <param name="bindable">The bindable object.</param>
662         /// <param name="property">The BindableProperty on which to assign a value.</param>
663         /// <param name="value">The value to set.</param>
664         [EditorBrowsable(EditorBrowsableState.Never)]
665         public static void SetAttachedValue(Binding.BindableObject bindable, Binding.BindableProperty property, object value)
666         {
667             if (bindable == null)
668                 throw new ArgumentNullException(nameof(bindable));
669
670             bindable.SetValueCore(property, value, SetValueFlags.None, SetValuePrivateFlags.ManuallySet, false);
671         }
672         internal static void OnChildPropertyChanged(Binding.BindableObject bindable, object oldValue, object newValue)
673         {
674             // Unused parameters
675             _ = oldValue;
676             _ = newValue;
677
678             View view = bindable as View;
679             view?.Layout?.RequestLayout();
680         }
681
682         internal static Binding.BindableProperty.ValidateValueDelegate ValidateEnum(int enumMin, int enumMax)
683         {
684             return (Binding.BindableObject bindable, object value) =>
685             {
686                 int @enum = (int)value;
687                 return enumMin <= @enum && @enum <= enumMax;
688             };
689         }
690     }
691 }