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