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