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