Files
MicrosoftIris/UIX/Microsoft/Iris/Layouts/AnchorLayout.cs
T

425 lines
18 KiB
C#
Raw Normal View History

2022-03-03 08:14:05 -06:00
// Decompiled with JetBrains decompiler
// Type: Microsoft.Iris.Layouts.AnchorLayout
// Assembly: UIX, Version=4.8.0.0, Culture=neutral, PublicKeyToken=ddd0da4d3e678217
// MVID: A56C6C9D-B7F6-46A9-8BDE-B3D9B8D60B11
// Assembly location: C:\Program Files\Zune\UIX.dll
using Microsoft.Iris.Layout;
using Microsoft.Iris.Library;
using Microsoft.Iris.Render;
using Microsoft.Iris.Session;
using Microsoft.Iris.UI;
using System;
namespace Microsoft.Iris.Layouts
{
internal class AnchorLayout : ILayout
{
private bool _sizeToHorizontalChildrenFlag = true;
private bool _sizeToVerticalChildrenFlag = true;
private ItemAlignment _defaultChildAlignment;
private static readonly DataCookie s_dataProperty = DataCookie.ReserveSlot();
private static AnchorEdge s_anchorParent0 = new AnchorEdge("Parent", 0.0f);
private static AnchorEdge s_anchorParent1 = new AnchorEdge("Parent", 1f);
private static AnchorEdge s_anchorParentActual0 = new AnchorEdge("ParentActual", 0.0f);
private static AnchorEdge s_anchorParentActual1 = new AnchorEdge("ParentActual", 1f);
public bool SizeToHorizontalChildren
{
get => _sizeToHorizontalChildrenFlag;
set => _sizeToHorizontalChildrenFlag = value;
}
public bool SizeToVerticalChildren
{
get => _sizeToVerticalChildrenFlag;
set => _sizeToVerticalChildrenFlag = value;
}
public ItemAlignment DefaultChildAlignment
{
get => _defaultChildAlignment;
set => _defaultChildAlignment = value;
}
Size ILayout.Measure(ILayoutNode layoutNode, Size constraint)
{
if (!(layoutNode.MeasureData is AnchorLayout.Packet packet))
{
packet = new AnchorLayout.Packet();
layoutNode.MeasureData = packet;
packet.ParentRecord = new AnchorLayout.Record("Parent", new Rectangle(Point.Zero, constraint), LayoutPhase.Done);
packet.ParentActualRecord = new AnchorLayout.Record("ParentActual", Rectangle.Zero, LayoutPhase.Arrange);
packet.AreaOfInterestRecord = new AnchorLayout.Record("Focus", Rectangle.Zero, LayoutPhase.Arrange);
packet.Records = new Vector<AnchorLayout.Record>(layoutNode.LayoutChildrenCount);
}
else
{
packet.ParentRecord.Bounds = new Rectangle(Point.Zero, constraint);
packet.CircularitiesDetected = false;
packet.CircularityBreakerRecord = null;
}
packet.Subject = layoutNode;
packet.Constraint = constraint;
packet.TotalBounds = new Rectangle(int.MaxValue, int.MaxValue, int.MinValue, int.MinValue);
Size zero = Size.Zero;
if (layoutNode.LayoutChildrenCount == 0)
{
packet.Records.Clear();
if (!_sizeToHorizontalChildrenFlag)
zero.Width = constraint.Width;
if (!_sizeToVerticalChildrenFlag)
zero.Height = constraint.Height;
layoutNode.RequestMoreChildren(int.MaxValue);
return zero;
}
int index = 0;
foreach (ILayoutNode layoutChild in layoutNode.LayoutChildren)
{
if (index < packet.Records.Count)
{
packet.Records[index].Initialize(layoutChild);
}
else
{
AnchorLayout.Record record = new AnchorLayout.Record(layoutChild);
packet.Records.Add(record);
}
++index;
}
packet.Records.RemoveRange(index, packet.Records.Count - index);
foreach (AnchorLayout.Record record in packet.Records)
{
if (record.Phase == LayoutPhase.Untouched)
{
packet.CircularityBreakerRecord = null;
MeasureChild(packet, record);
}
}
if (packet.CircularitiesDetected)
{
foreach (AnchorLayout.Record record in packet.Records)
{
if (record.Phase == LayoutPhase.Circular)
record.Phase = LayoutPhase.Untouched;
}
packet.CircularitiesDetected = false;
foreach (AnchorLayout.Record record in packet.Records)
{
if (record.Phase == LayoutPhase.Untouched)
MeasureChild(packet, record);
}
}
Size size = constraint;
if (_sizeToHorizontalChildrenFlag || _sizeToVerticalChildrenFlag)
{
if (_sizeToHorizontalChildrenFlag)
size.Width = packet.TotalBounds.Width;
if (_sizeToVerticalChildrenFlag)
size.Height = packet.TotalBounds.Height;
foreach (AnchorLayout.Record record in packet.Records)
{
Rectangle bounds = record.Bounds;
if (_sizeToHorizontalChildrenFlag)
bounds.X -= packet.TotalBounds.X;
if (_sizeToVerticalChildrenFlag)
bounds.Y -= packet.TotalBounds.Y;
record.Bounds = bounds;
}
}
layoutNode.RequestMoreChildren(int.MaxValue);
return size;
}
private void MeasureChild(AnchorLayout.Packet packet, AnchorLayout.Record record)
{
AnchorLayoutInput input = record.Input;
record.Phase = LayoutPhase.InProgress;
bool allArrangePhase = true;
bool anyArrangePhase = false;
int edge1 = ComputeEdge(packet, "Left", input.Left, s_anchorParent0, Orientation.Horizontal, record, out AnchorLayout.Record _, ref allArrangePhase, ref anyArrangePhase);
int edge2 = ComputeEdge(packet, "Top", input.Top, s_anchorParent0, Orientation.Vertical, record, out AnchorLayout.Record _, ref allArrangePhase, ref anyArrangePhase);
int edge3 = ComputeEdge(packet, "Right", input.Right, s_anchorParent1, Orientation.Horizontal, record, out AnchorLayout.Record _, ref allArrangePhase, ref anyArrangePhase);
int edge4 = ComputeEdge(packet, "Bottom", input.Bottom, s_anchorParent1, Orientation.Vertical, record, out AnchorLayout.Record _, ref allArrangePhase, ref anyArrangePhase);
if (anyArrangePhase)
{
if (!allArrangePhase)
{
ErrorManager.ReportError("All AnchorEdges must refer to actual positions on AnchorLayoutInput {0}.", record.Input);
record.Invalid = true;
}
if (record.Input.ContributesToHeight || record.Input.ContributesToWidth)
{
ErrorManager.ReportError("AnchorLayoutInput {0} cannot contribute to width or height.", record.Input);
record.Invalid = true;
}
record.Phase = LayoutPhase.Arrange;
}
else
{
Normalize(ref edge1, ref edge3);
Normalize(ref edge2, ref edge4);
int constraintMaxValue1;
ComputeConstraint(edge1, edge3, out constraintMaxValue1);
int constraintMaxValue2;
ComputeConstraint(edge2, edge4, out constraintMaxValue2);
Size constraint = new Size(constraintMaxValue1, constraintMaxValue2);
record.LayoutNode.Measure(constraint);
int size1;
int x = LocationFromEdges(input.Left, input.Right, edge1, edge3, record.LayoutNode.DesiredSize.Width, record.LayoutNode.AlignedSize.Width, record.LayoutNode.AlignmentOffset.X, out size1);
int size2;
int y = LocationFromEdges(input.Top, input.Bottom, edge2, edge4, record.LayoutNode.DesiredSize.Height, record.LayoutNode.AlignedSize.Height, record.LayoutNode.AlignmentOffset.Y, out size2);
record.Bounds = new Rectangle(x, y, size1, size2);
bool flag = packet.CircularityBreakerRecord == record;
if (record.Phase != LayoutPhase.InProgress && !flag)
return;
record.Phase = LayoutPhase.Done;
Rectangle bounds = record.Bounds;
if (!input.ContributesToWidth)
{
bounds.X = int.MaxValue;
bounds.Width = int.MinValue;
}
if (!input.ContributesToHeight)
{
bounds.Y = int.MaxValue;
bounds.Height = int.MinValue;
}
packet.TotalBounds = Rectangle.Union(packet.TotalBounds, bounds);
}
}
private int ComputeEdge(
AnchorLayout.Packet packet,
string edgeName,
AnchorEdge anchor,
AnchorEdge fallback,
Orientation orientation,
AnchorLayout.Record record,
out AnchorLayout.Record recordRef,
ref bool allArrangePhase,
ref bool anyArrangePhase)
{
anchor = anchor != null ? anchor : fallback;
recordRef = GetReferenceRecord(packet, anchor.Id);
if (recordRef == null)
{
allArrangePhase = false;
return 0;
}
bool flag = recordRef.Phase == LayoutPhase.Arrange;
allArrangePhase &= flag;
anyArrangePhase |= flag;
record.Visible &= recordRef.Visible;
Rectangle bounds = recordRef.Bounds;
if (recordRef.Phase != LayoutPhase.Done && recordRef.Phase != LayoutPhase.Arrange)
{
record.Phase = LayoutPhase.Circular;
bounds = packet.ParentRecord.Bounds;
if (recordRef.Phase == LayoutPhase.InProgress && packet.CircularityBreakerRecord == null)
{
packet.CircularityBreakerRecord = recordRef;
packet.CircularitiesDetected = true;
}
}
int val1 = Weigh(orientation == Orientation.Horizontal ? bounds.Width : bounds.Height, anchor.Percent) + (orientation == Orientation.Horizontal ? bounds.X : bounds.Y) + anchor.Offset;
int num = orientation == Orientation.Horizontal ? packet.ParentRecord.Bounds.Width : packet.ParentRecord.Bounds.Height;
if (anchor.MaximumSet)
{
int val2 = Weigh(num, anchor.MaximumPercent) + anchor.MaximumOffset;
val1 = Math.Min(val1, val2);
}
if (anchor.MinimumSet)
{
int val2 = Weigh(num, anchor.MinimumPercent) + anchor.MinimumOffset;
val1 = Math.Max(val1, val2);
}
return val1;
}
private static void ComputeConstraint(int nearValue, int farValue, out int constraintMaxValue) => constraintMaxValue = farValue - nearValue;
private static int LocationFromEdges(
AnchorEdge near,
AnchorEdge far,
int nearValue,
int farValue,
int desiredSize,
int alignedSize,
int alignmentOffset,
out int size)
{
size = desiredSize;
int num;
if (near == null && far == null)
num = (nearValue + farValue - desiredSize) / 2;
else if (near != null && far == null)
num = nearValue;
else if (near == null && far != null)
{
num = farValue - desiredSize;
}
else
{
num = nearValue + alignmentOffset;
size = alignedSize;
}
return num;
}
private static void Normalize(ref int nearValue, ref int farValue)
{
if (farValue >= nearValue)
return;
farValue = nearValue;
}
private static int Weigh(int value, float percentValue) => (int)(value * (double)percentValue);
void ILayout.Arrange(ILayoutNode layoutNode, LayoutSlot slot)
{
AnchorLayout.Packet measureData = (AnchorLayout.Packet)layoutNode.MeasureData;
if (measureData.Records == null)
return;
measureData.ParentActualRecord.Bounds = new Rectangle(Point.Zero, slot.Bounds);
measureData.AreaOfInterestRecord.Bounds = Rectangle.Zero;
Vector<AnchorLayout.Record> vector = null;
AnchorLayout.Record record1 = null;
foreach (AnchorLayout.Record record2 in measureData.Records)
{
if (record2.Phase != LayoutPhase.Arrange)
{
record2.LayoutNode.Arrange(slot, record2.Bounds);
if (record1 == null && (record2.LayoutNode.ContainsAreaOfInterest(AreaOfInterestID.FocusOverride) || record2.LayoutNode.ContainsAreaOfInterest(AreaOfInterestID.Focus)))
record1 = record2;
}
else if (!record2.Invalid)
{
if (vector == null)
vector = new Vector<AnchorLayout.Record>();
vector.Add(record2);
}
else
record2.LayoutNode.MarkHidden();
}
if (vector == null)
return;
if (record1 != null)
{
AreaOfInterest area;
if (!record1.LayoutNode.TryGetAreaOfInterest(AreaOfInterestID.FocusOverride, out area))
record1.LayoutNode.TryGetAreaOfInterest(AreaOfInterestID.Focus, out area);
measureData.AreaOfInterestRecord.Bounds = Rectangle.Offset(area.DisplayRectangle, record1.Bounds.Location);
}
foreach (AnchorLayout.Record record2 in vector)
{
AnchorLayoutInput input = record2.Input;
bool flag = false;
AnchorLayout.Record recordRef;
int edge1 = ComputeEdge(measureData, "Left", input.Left, null, Orientation.Horizontal, record2, out recordRef, ref flag, ref flag);
int edge2 = ComputeEdge(measureData, "Top", input.Top, null, Orientation.Vertical, record2, out recordRef, ref flag, ref flag);
int edge3 = ComputeEdge(measureData, "Right", input.Right, null, Orientation.Horizontal, record2, out recordRef, ref flag, ref flag);
int edge4 = ComputeEdge(measureData, "Bottom", input.Bottom, null, Orientation.Vertical, record2, out recordRef, ref flag, ref flag);
int width = edge3 - edge1;
int height = edge4 - edge2;
record2.LayoutNode.Measure(new Size(width, height));
record2.LayoutNode.Arrange(slot, new Rectangle(edge1, edge2, width, height));
}
}
private AnchorLayout.Record GetReferenceRecord(
AnchorLayout.Packet packet,
string id)
{
if (id == "Parent")
return packet.ParentRecord;
if (id == "ParentActual")
return packet.ParentActualRecord;
if (id == "Focus")
return packet.AreaOfInterestRecord;
foreach (AnchorLayout.Record record in packet.Records)
{
if (record.ID == id)
{
if (record.Phase == LayoutPhase.Untouched)
MeasureChild(packet, record);
return record;
}
}
ErrorManager.ReportError("Anchor layout: {0} cannot find the '{1}' child", GetType().Name, id);
return null;
}
2024-02-18 22:23:59 -06:00
public bool Equals(ILayout other)
{
if (other is not AnchorLayout o) return false;
return DefaultChildAlignment == o.DefaultChildAlignment
&& SizeToHorizontalChildren == o.SizeToHorizontalChildren
&& SizeToVerticalChildren == o.SizeToVerticalChildren;
}
2022-03-03 08:14:05 -06:00
internal static DataCookie InputData => s_dataProperty;
internal enum LayoutPhase
{
Untouched,
InProgress,
Circular,
Done,
Arrange,
}
internal class Record
{
public ILayoutNode LayoutNode;
public string ID;
public AnchorLayoutInput Input;
public Rectangle Bounds;
public AnchorLayout.LayoutPhase Phase;
public bool Visible = true;
public bool Invalid;
private static AnchorLayoutInput s_defaultInput = new AnchorLayoutInput();
public Record(ILayoutNode layoutNode) => Initialize(layoutNode);
public void Initialize(ILayoutNode layoutNode)
{
LayoutNode = layoutNode;
Input = layoutNode.GetLayoutInput(InputData) as AnchorLayoutInput;
if (Input == null)
Input = s_defaultInput;
ID = ((ViewItem)layoutNode).Name;
Bounds = Rectangle.Zero;
Phase = LayoutPhase.Untouched;
Visible = true;
Invalid = false;
}
public Record(string idName, Rectangle bounds, AnchorLayout.LayoutPhase phaseOverride)
{
ID = idName;
Bounds = bounds;
Phase = phaseOverride;
}
public override string ToString() => string.Format("AnchorLayout.Record({0}, Phase={1})", ID, Phase);
}
private class Packet
{
public ILayoutNode Subject;
public Vector<AnchorLayout.Record> Records;
public AnchorLayout.Record ParentRecord;
public AnchorLayout.Record ParentActualRecord;
public AnchorLayout.Record AreaOfInterestRecord;
public Size Constraint;
public Rectangle TotalBounds;
public bool CircularitiesDetected;
public AnchorLayout.Record CircularityBreakerRecord;
public override string ToString() => string.Format("AnchorLayout.Packet({0})", Subject);
}
}
}