private void HandleFixedAspectRatio(LayoutNode parentNode, LayoutNode child) { if (child.Size.IsFixedAspectRatio()) { // Right now both sides of FixedAspectRatio think they're streched, we need to figure out which one is actually stretched based on how much room is thinks it has var aspectRatioOfAvailableSpace = new AspectRatio(this.measurer.GetMeasuredSize(child.Size)); var childAspectRatio = child.Size.GetAspectRatio(); var isStretchedAlong = AspectRatio.IsStretchedAlong(childAspectRatio, aspectRatioOfAvailableSpace, parentNode.Orientation); var isStretchedPerpendicular = AspectRatio.IsStretchedPerpendicular(childAspectRatio, aspectRatioOfAvailableSpace, parentNode.Orientation); var isStretchedBoth = isStretchedAlong && isStretchedPerpendicular; var oppositeOrientation = parentNode.Orientation.Opposite(); // If it's stretched both we do nothing because we already assume it's stretched on both sides if (!isStretchedBoth) { if (isStretchedAlong) { var alongSize = this.measurer.MeasureEdgeOfNode(child, parentNode.Orientation); this.measurer.Add(child.Size.GetValueFromOrientation(oppositeOrientation), (int)(alongSize * childAspectRatio.AlongOverPerpendicular(oppositeOrientation))); } if (isStretchedPerpendicular) { var perpendicularSize = this.measurer.MeasureEdgeOfNode(child, oppositeOrientation); this.measurer.Add(child.Size.GetValueFromOrientation(parentNode.Orientation), (int)(perpendicularSize * childAspectRatio.AlongOverPerpendicular(parentNode.Orientation))); } } } }
public BakedLayoutNode[] GetDirectChildrenOfNode(LayoutNode node) { if (!this.rawToBakedLookup.ContainsKey(node)) { throw new Exception($"No node matches {node}"); } if (node.Name.IsNameless) { throw new Exception($"Found node {node} but its nameless so it can't have children"); } var result = new List <BakedLayoutNode>(); foreach (var child in node.Children) { if (child.IsBakable) { result.Add(this.rawToBakedLookup[child]); } } return(result.ToArray()); }
public void AddNodeToLayout(BakedLayout inProgressLayout, Point position, LayoutNode node, int nestingLevel) { if (node.IsBakable) { inProgressLayout.Add(node, new BakedLayoutNode(position, this.measurer.GetMeasuredSize(node.Size), nestingLevel)); } }
private static int GetRemainingAlongSizeFromEasyNodes(LayoutNode parentNode, Point groupSize) { var isVertical = parentNode.Orientation == Orientation.Vertical; var totalAlongSize = isVertical ? groupSize.Y : groupSize.X; var alongMargin = isVertical ? parentNode.Margin.Y : parentNode.Margin.X; var remainingAlongSize = totalAlongSize - alongMargin * 2; var lastIndex = parentNode.Children.Length - 1; var index = 0; foreach (var child in parentNode.Children) { if (child.Size.IsMeasurableAlong(parentNode.Orientation)) { remainingAlongSize -= child.Size.GetValueFromOrientation(parentNode.Orientation).ActualSize; } if (index != lastIndex) { remainingAlongSize -= parentNode.Padding; } index++; } return(remainingAlongSize); }
private static RawFlowLayout OrientedFlowParent(Orientation orientation, string name, LayoutSize size, FlowLayoutStyle style, params LayoutNodeOrInstruction[] children) { var workableAreaStyle = new LayoutStyle(margin: style.Margin, alignment: style.Alignment); var workableArea = LayoutNode.NamelessOneOffParent(size, workableAreaStyle, LayoutNode.Leaf("workableArea", LayoutSize.StretchedBoth())).Bake().GetNode("workableArea"); var rows = new FlowLayoutRows(workableArea.Size, style, orientation); foreach (var item in children) { if (item.IsLayoutNode) { if (!rows.CanFitItemPerpendicular(item)) { break; } if (rows.CanFitItemAlongCurrentRow(item)) { rows.AddItemToCurrentRow(item); } else { rows.CreateNextRowAndAdd(item); } } else if (item.IsInstruction) { rows.ConsumeInstruction(item); } } return(new RawFlowLayout(name, size, workableAreaStyle, orientation, style, rows)); }
private void CreateActorsForChildren(LayoutNode parent, IBakedLayout layout) { if (!parent.HasChildren) { return; } var parentActor = GetActor(parent.Name.Text); foreach (var childNode in parent.Children) { if (childNode.Name.Exists) { var actorName = childNode.Name.Text; var actor = parentActor.transform.AddActorAsChild(actorName); new LayoutSiblingWithCachedOrientation(actor, parent.Orientation); new BoundingRect(actor, layout.GetNode(actorName).Size); AddActorToTable(actorName, actor); SetupChildActor(actor, actorName, layout); CreateActorsForChildren(childNode, layout); } } }
private void HandleStretchedNodes(LayoutNode parentNode, int remainingAlongSize, int perpendicularStretchSize) { int stretchAlongCount = 0; int stretchPerpendicularCount = 0; foreach (var child in parentNode.Children) { if (child.Size.IsStretchedAlong(parentNode.Orientation)) { stretchAlongCount++; } if (child.Size.IsStretchedPerpendicular(parentNode.Orientation)) { stretchPerpendicularCount++; } } // Update size of along stretch elements if (stretchAlongCount > 0) { var alongSizeOfEachStretchedChild = remainingAlongSize / stretchAlongCount; var fractionalLossIncrement = (float)remainingAlongSize / stretchAlongCount % 1; var fractionalLoss = 0f; foreach (var child in parentNode.Children) { if (child.Size.IsStretchedAlong(parentNode.Orientation)) { fractionalLoss += fractionalLossIncrement; int extraPixelIfApplicable = 0; var epsilon = 0.001f; if (1 - fractionalLoss < epsilon) { extraPixelIfApplicable = 1; fractionalLoss -= 1; } this.measurer.Add(child.Size.GetValueFromOrientation(parentNode.Orientation), alongSizeOfEachStretchedChild + extraPixelIfApplicable); } } } // Update perp elements (we can inline this in the first loop if (stretchPerpendicularCount > 0) { foreach (var child in parentNode.Children) { if (child.Size.IsStretchedPerpendicular(parentNode.Orientation)) { this.measurer.Add(child.Size.GetValueFromOrientation(parentNode.Orientation.Opposite()), perpendicularStretchSize); HandleFixedAspectRatio(parentNode, child); } } } }
public void ConsumeInstruction(FlowLayoutInstruction instruction) { if (instruction == FlowLayoutInstruction.Linebreak) { if (HasRoomForAnotherRow(LayoutNode.NamelessLeaf(LayoutSize.Pixels(0, 0)))) { AddNewRow(); } } }
public LayoutNode[] GetLayoutNodesOfEachRow() { var nodes = new LayoutNode[Content.Count]; for (int i = 0; i < Content.Count; i++) { nodes[i] = Content[i].GetLayoutNode($"row {i}"); } return(nodes); }
internal RawFlowLayout(string name, LayoutSize size, LayoutStyle workableAreaStyle, Orientation orientation, FlowLayoutStyle style, FlowLayoutRows rows) : base( LayoutNode.OneOffParent(name, size, workableAreaStyle, LayoutNode.OrientedParent(orientation.Opposite(), "rows", LayoutSize.Pixels(rows.UsedSize), new LayoutStyle(padding: style.PaddingBetweenRows), rows.GetLayoutNodesOfEachRow() ) )) { this.orientation = orientation; this.rowNodes = rows.GetLayoutNodesOfEachRow(); this.rowUsedSpace = rows.GetUsedSpaceOfEachRow(); }
public bool CanFitItemPerpendicular(LayoutNode item) { if (Style.OverflowRule.HasInfiniteRows) { return(true); } var usedPerpendicular = PerpendicularSizeOfAllRowsExceptCurrent; var itemPerpendicular = item.Size.GetValueFromOrientation(Orientation.Opposite()).ActualSize; return(AvailablePerpendicularSize >= usedPerpendicular + itemPerpendicular); }
private bool HasRoomForAnotherRow(LayoutNode itemToAdd) { if (Style.OverflowRule.HasInfiniteRows) { return(true); } var possibleNewRow = new FlowLayoutRow(AvailableAlongSize, Style, Orientation); possibleNewRow.AddItem(itemToAdd); var totalSizeAfterAddingRow = UsedSize.OppositeAxisValue(Orientation.ToAxis()) + possibleNewRow.UsedPerpendicularSize; return(totalSizeAfterAddingRow <= AvailablePerpendicularSize); }
private void BakeGroup(BakedLayout inProgressLayout, LayoutNode parentNode, Point parentNodeLocation, int parentNestingLevel) { var isVertical = parentNode.Orientation == Orientation.Vertical; var groupSize = this.measurer.GetMeasuredSize(parentNode.Size); int remainingAlongSize = GetRemainingAlongSizeFromEasyNodes(parentNode, groupSize); var perpendicularStretchSize = isVertical ? groupSize.X - parentNode.Margin.X * 2 : groupSize.Y - parentNode.Margin.Y * 2; HandleStretchedNodes(parentNode, remainingAlongSize, perpendicularStretchSize); // Place elements PlaceAndBakeMeasuredElements(inProgressLayout, parentNode, parentNodeLocation, parentNestingLevel + 1); }
public void CreateNextRowAndAdd(LayoutNode itemToAdd) { if (CurrentRow.Content.Count == 0) { AddItemToCurrentRow(itemToAdd); } else if (!HasRoomForAnotherRow(itemToAdd)) { AddItemToCurrentRow(itemToAdd); } else { AddNewRow(); AddItemToCurrentRow(itemToAdd); } }
private Point CalculateTotalUsedSpace(LayoutNode parentNode) { var totalUsedAlongSpace = 0; var totalUsedPerpendicularSpace = 0; foreach (var child in parentNode.Children) { totalUsedAlongSpace += this.measurer.MeasureEdgeOfNode(child, parentNode.Orientation); totalUsedAlongSpace += parentNode.Padding; totalUsedPerpendicularSpace = Math.Max(totalUsedPerpendicularSpace, this.measurer.MeasureEdgeOfNode(child, parentNode.Orientation.Opposite())); } // subtract 1 padding since the previous loop adds an extra (thanks foreach) totalUsedAlongSpace -= parentNode.Padding; return(parentNode.Orientation.GetPointFromAlongPerpendicular(totalUsedAlongSpace, totalUsedPerpendicularSpace)); }
public void AddItemToCurrentRow(LayoutNode itemToAdd) { if (StopAddingNewItems) { return; } CurrentRow.AddItem(itemToAdd); var overflowedAlong = CurrentRow.UsedAlongSize > AvailableAlongSize; var overflowedPerpendicular = UsedSize.OppositeAxisValue(Orientation.ToAxis()) > AvailablePerpendicularSize; var failed = overflowedPerpendicular || overflowedAlong; if (failed) { } }
private void PlaceAndBakeMeasuredElements(BakedLayout inProgressLayout, LayoutNode parentNode, Point parentNodeLocation, int currentNestingLevel) { var parentSize = this.measurer.GetMeasuredSize(parentNode.Size); var nextPosition = parentNodeLocation + parentNode.Alignment.GetRelativePositionOfElement(parentSize, CalculateTotalUsedSpace(parentNode)) + parentNode.Alignment.AddPostionDeltaFromMargin(parentNode.Margin) ; foreach (var child in parentNode.Children) { var alignmentOffset = parentNode.Alignment.GetRelativePositionOfElement(CalculateTotalUsedSpace(parentNode), this.measurer.GetMeasuredSize(child.Size)).WithJustAxisValue(parentNode.Orientation.Opposite().ToAxis()); var childPosition = nextPosition + alignmentOffset; AddNodeToLayout(inProgressLayout, childPosition, child, currentNestingLevel); nextPosition += parentNode.Orientation.GetPointForAlongAxis(this.measurer.MeasureEdgeOfNode(child, parentNode.Orientation) + parentNode.Padding); if (child.HasChildren) { BakeGroup(inProgressLayout, child, childPosition, currentNestingLevel); } } }
public LayoutNode GetLayoutNode(string rowNodeName) { var size = Orientation.GetPointFromAlongPerpendicular(AvailableAlongSize, UsedPerpendicularSize); return(LayoutNode.OrientedParent(Orientation, rowNodeName, LayoutSize.Pixels(size), RowStyle, Content.ToArray())); }
public void AddItem(LayoutNode child) { Content.Add(child); UpdateEstimatedSize(); }
public BakedLayoutNode GetNode(LayoutNode unbakedNode) { return(this.rawToBakedLookup[unbakedNode]); }
public void Add(LayoutNode key, BakedLayoutNode value) { this.rawToBakedLookup[key] = value; }
public bool CanFitItemAlongCurrentRow(LayoutNode item) { return(RemainingAlongSizeInCurrentRow >= item.Size.GetValueFromOrientation(Orientation).ActualSize); }
public static RawLayout HorizontalFlexParent(string name, FlexLayoutStyle style, params LayoutNode[] children) { return(LayoutNode.HorizontalParent(name, FlexParentSize(Orientation.Horizontal, style, children), style.InnerStyle, children)); }
public LayoutBaker(LayoutNode rootNode) { this.rootNode = rootNode; this.measurer = new LayoutMeasurer(); }
public LayoutNodeOrInstruction(LayoutNode layoutNode) { InternalLayoutNode = layoutNode; }
public static RawLayout OneOffParent(string name, LayoutSize size, LayoutStyle style, LayoutNode child) { // Horizontal/Vertical does not matter here return(HorizontalParent(name, size, style, child)); }
public static RawLayout NamelessOneOffParent(LayoutSize size, LayoutStyle style, LayoutNode child) { // Horizontal/Vertical does not matter here return(HorizontalParent("root", size, style, child)); }
public BakedLayout(LayoutNode originalRoot) { OriginalRoot = originalRoot; }
public RawLayout(LayoutNode rootNode) : base(rootNode) { }
public int MeasureEdgeOfNode(LayoutNode node, Orientation orientation) { return(MeasureEdge(node.Size.GetValueFromOrientation(orientation))); }