/// <summary> /// Runs a layout pass in the X direction, resolving edges that can be resolved. /// </summary> /// <param name="children">The children to resolve.</param> /// <returns>true if all children have all X edges constrained, or false otherwise.</returns> internal static bool RunPassX(this IEnumerable <RelativeLayoutResults> children) { bool done = true; foreach (var child in children) { float width = child.EffectiveWidth; EdgeStatus l = child.LeftEdge, r = child.RightEdge; if (LockEdgeAnchor(l, r)) { child.UseSizeDeltaX = true; } LockEdgeAnchor(l); LockEdgeAnchor(r); LockEdgeRelative(l, -width, r); LockEdgeRelative(r, width, l); // Lock to other components if (child.LeftParams != null) { LockEdgeComponent(l, child.LeftParams.RightEdge); } if (child.RightParams != null) { LockEdgeComponent(r, child.RightParams.LeftEdge); } if (!l.Locked || !r.Locked) { done = false; } } return(done); }
/// <summary> /// Runs a layout pass in the Y direction, resolving edges that can be resolved. /// </summary> /// <param name="children">The children to resolve.</param> /// <returns>true if all children have all Y edges constrained, or false otherwise.</returns> internal static bool RunPassY(this IEnumerable <RelativeLayoutResults> children) { bool done = true; foreach (var child in children) { float height = child.EffectiveHeight; EdgeStatus t = child.TopEdge, b = child.BottomEdge; if (LockEdgeAnchor(t, b)) { child.UseSizeDeltaY = true; } LockEdgeAnchor(b); LockEdgeAnchor(t); LockEdgeRelative(b, -height, t); LockEdgeRelative(t, height, b); // Lock to other components if (child.BottomParams != null) { LockEdgeComponent(b, child.BottomParams.TopEdge); } if (child.TopParams != null) { LockEdgeComponent(t, child.TopParams.BottomEdge); } if (!t.Locked || !b.Locked) { done = false; } } return(done); }
/// <summary> /// Executes the vertical layout. /// </summary> /// <param name="children">The components to lay out.</param> /// <param name="scratch">The location where components will be temporarily stored.</param> /// <param name="mBottom">The bottom margin.</param> /// <param name="mTop">The top margin.</param> internal static void ExecuteY(this IEnumerable <RelativeLayoutResults> children, List <ILayoutController> scratch, float mBottom = 0.0f, float mTop = 0.0f) { foreach (var child in children) { var rt = child.Transform; var insets = child.Insets; EdgeStatus t = child.TopEdge, b = child.BottomEdge; // Set corner positions rt.anchorMin = new Vector2(rt.anchorMin.x, b.FromAnchor); rt.anchorMax = new Vector2(rt.anchorMax.x, t.FromAnchor); // If anchored by pivot, resize with current anchors if (child.UseSizeDeltaY) { rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, child. PreferredHeight); } else { // Bottom rt.offsetMin = new Vector2(rt.offsetMin.x, b.Offset + insets.bottom + (b.FromAnchor <= 0.0f ? mBottom : 0.0f)); // Top rt.offsetMax = new Vector2(rt.offsetMax.x, t.Offset - insets.top - (t.FromAnchor >= 1.0f ? mTop : 0.0f)); } // Execute layout controllers if present scratch.Clear(); rt.gameObject.GetComponents(scratch); foreach (var component in scratch) { component.SetLayoutVertical(); } } }
/// <summary> /// Executes the horizontal layout. /// </summary> /// <param name="children">The components to lay out.</param> /// <param name="scratch">The location where components will be temporarily stored.</param> /// <param name="mLeft">The left margin.</param> /// <param name="mRight">The right margin.</param> internal static void ExecuteX(this IEnumerable <RelativeLayoutResults> children, List <ILayoutController> scratch, float mLeft = 0.0f, float mRight = 0.0f) { foreach (var child in children) { var rt = child.Transform; var insets = child.Insets; EdgeStatus l = child.LeftEdge, r = child.RightEdge; // Set corner positions rt.anchorMin = new Vector2(l.FromAnchor, 0.0f); rt.anchorMax = new Vector2(r.FromAnchor, 1.0f); // If anchored by pivot, resize with current anchors if (child.UseSizeDeltaX) { rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, child. PreferredWidth); } else { // Left rt.offsetMin = new Vector2(l.Offset + insets.left + (l.FromAnchor <= 0.0f ? mLeft : 0.0f), rt.offsetMin.y); // Right rt.offsetMax = new Vector2(r.Offset - insets.right - (r.FromAnchor >= 1.0f ? mRight : 0.0f), rt.offsetMax.y); } // Execute layout controllers if present scratch.Clear(); rt.gameObject.GetComponents(scratch); foreach (var component in scratch) { component.SetLayoutHorizontal(); } } }
/// <summary> /// Locks an edge if it is constrained to an anchor. /// </summary> /// <param name="edge">The edge to check.</param> private static void LockEdgeAnchor(EdgeStatus edge) { if (edge.Constraint == RelativeConstraintType.ToAnchor) { edge.Constraint = RelativeConstraintType.Locked; edge.Offset = 0.0f; } }
/// <summary> /// Locks an edge if it can be determined from another component. /// </summary> /// <param name="edge">The edge to check.</param> /// <param name="offset">The component's offset in that direction.</param> /// <param name="otherEdge">The opposing edge of the referenced component.</param> private static void LockEdgeComponent(EdgeStatus edge, EdgeStatus otherEdge) { if (edge.Constraint == RelativeConstraintType.ToComponent && otherEdge.Locked) { edge.Constraint = RelativeConstraintType.Locked; edge.FromAnchor = otherEdge.FromAnchor; edge.Offset = otherEdge.Offset; } }
/// <summary> /// Resolves a component reference if needed. /// </summary> /// <param name="edge">The edge to resolve.</param> /// <param name="lookup">The location where the component can be looked up.</param> /// <returns>The linked parameters for that edge if needed.</returns> private static RelativeLayoutResults InitResolve(EdgeStatus edge, IDictionary <GameObject, RelativeLayoutResults> lookup) { RelativeLayoutResults result = null; if (edge.Constraint == RelativeConstraintType.ToComponent) { if (!lookup.TryGetValue(edge.FromComponent, out result)) { edge.Constraint = RelativeConstraintType.Unconstrained; } } return(result); }
/// <summary> /// Locks both edges if they are constrained to the same anchor. /// </summary> /// <param name="edge">The edge to check.</param> /// <param name="otherEdge">The other edge to check.</param> /// <returns>true if it was able to lock, or false otherwise.</returns> private static bool LockEdgeAnchor(EdgeStatus edge, EdgeStatus otherEdge) { bool useDelta = edge.Constraint == RelativeConstraintType.ToAnchor && otherEdge. Constraint == RelativeConstraintType.ToAnchor && edge.FromAnchor == otherEdge. FromAnchor; if (useDelta) { edge.Constraint = RelativeConstraintType.Locked; otherEdge.Constraint = RelativeConstraintType.Locked; edge.Offset = 0.0f; otherEdge.Offset = 0.0f; } return(useDelta); }
/// <summary> /// Calculates the minimum size in the Y direction. /// </summary> /// <param name="children">The components to lay out.</param> /// <returns>The minimum vertical size.</returns> internal static float GetMinSizeY(this IEnumerable <RelativeLayoutResults> children) { float maxHeight = 0.0f, height; foreach (var child in children) { var insets = child.Insets; EdgeStatus bottom = child.BottomEdge, top = child.TopEdge; float aMin = bottom.FromAnchor, aMax = top.FromAnchor; if (aMax > aMin) { // "Elbow room" method height = (child.EffectiveHeight + bottom.Offset - top.Offset) / (aMax - aMin); } else { // Anchors are together float oMin = bottom.Offset, oMax = top.Offset; if (oMin == oMax) { oMin = oMax = child.EffectiveHeight * 0.5f; } if (oMin < 0.0f) { oMin /= -Math.Max(MIN_SIZE_RATIO, aMax); } else { oMin /= Math.Max(MIN_SIZE_RATIO, 1.0f - aMax); } if (oMax < 0.0f) { oMax /= -Math.Max(MIN_SIZE_RATIO, aMax); } else { oMax /= Math.Max(MIN_SIZE_RATIO, 1.0f - aMax); } height = Math.Max(oMin, oMax); } if (height > maxHeight) { maxHeight = height; } } return(maxHeight); }
/// <summary> /// Calculates the minimum size in the X direction. /// </summary> /// <param name="children">The components to lay out.</param> /// <returns>The minimum horizontal size.</returns> internal static float GetMinSizeX(this IEnumerable <RelativeLayoutResults> children) { float maxWidth = 0.0f, width; foreach (var child in children) { var insets = child.Insets; EdgeStatus left = child.LeftEdge, right = child.RightEdge; float aMin = left.FromAnchor, aMax = right.FromAnchor; if (aMax > aMin) { // "Elbow room" method width = (child.EffectiveWidth + left.Offset - right.Offset) / (aMax - aMin); } else { // Anchors are together float oMin = left.Offset, oMax = right.Offset; if (oMin == oMax) { oMin = oMax = child.EffectiveWidth * 0.5f; } if (oMin < 0.0f) { oMin /= -Math.Max(MIN_SIZE_RATIO, aMax); } else { oMin /= Math.Max(MIN_SIZE_RATIO, 1.0f - aMax); } if (oMax < 0.0f) { oMax /= -Math.Max(MIN_SIZE_RATIO, aMax); } else { oMax /= Math.Max(MIN_SIZE_RATIO, 1.0f - aMax); } width = Math.Max(oMin, oMax); } if (width > maxWidth) { maxWidth = width; } } return(maxWidth); }
/// <summary> /// Calculates the minimum size the component must be to support a specific child /// component. /// </summary> /// <param name="min">The lower edge constraint.</param> /// <param name="max">The upper edge constraint.</param> /// <param name="effective">The component size in that dimension plus margins.</param> /// <returns>The minimum parent component size to fit the child.</returns> internal static float ElbowRoom(EdgeStatus min, EdgeStatus max, float effective) { float aMin = min.FromAnchor, aMax = max.FromAnchor, result, offMin = min.Offset, offMax = max.Offset; if (aMax > aMin) { // "Elbow room" method result = (effective + offMin - offMax) / (aMax - aMin); } else { // Anchors are together result = Math.Max(effective, Math.Max(Math.Abs(offMin), Math.Abs(offMax))); } return(result); }
/// <summary> /// Locks an edge if it can be determined from the other edge. /// </summary> /// <param name="edge">The edge to check.</param> /// <param name="size">The component's effective size in that direction.</param> /// <param name="opposing">The component's other edge.</param> private static void LockEdgeRelative(EdgeStatus edge, float size, EdgeStatus opposing) { if (edge.Constraint == RelativeConstraintType.Unconstrained) { if (opposing.Locked) { edge.Constraint = RelativeConstraintType.Locked; edge.FromAnchor = opposing.FromAnchor; edge.Offset = opposing.Offset + size; } else if (opposing.Constraint == RelativeConstraintType.Unconstrained) { // Both unconstrained, full size edge.Constraint = RelativeConstraintType.Locked; edge.FromAnchor = 0.0f; edge.Offset = 0.0f; opposing.Constraint = RelativeConstraintType.Locked; opposing.FromAnchor = 1.0f; opposing.Offset = 0.0f; } } }
/// <summary> /// Calculates the minimum size the component must be to support a specific child /// component. /// </summary> /// <param name="min">The lower edge constraint.</param> /// <param name="max">The upper edge constraint.</param> /// <param name="effective">The component size in that dimension plus margins.</param> /// <returns>The minimum parent component size to fit the child.</returns> internal static float ElbowRoom(EdgeStatus min, EdgeStatus max, float effective) { float aMin = min.FromAnchor, aMax = max.FromAnchor, result; if (aMax > aMin) { // "Elbow room" method result = (effective + min.Offset - max.Offset) / (aMax - aMin); } else { // Anchors are together float oMin = min.Offset, oMax = max.Offset; if (oMin == oMax) { oMin = oMax = effective * 0.5f; } if (oMin < 0.0f) { oMin /= -Math.Max(MIN_SIZE_RATIO, aMax); } else { oMin /= Math.Max(MIN_SIZE_RATIO, 1.0f - aMax); } if (oMax < 0.0f) { oMax /= -Math.Max(MIN_SIZE_RATIO, aMax); } else { oMax /= Math.Max(MIN_SIZE_RATIO, 1.0f - aMax); } result = Math.Max(oMin, oMax); } return(result); }