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