UI layout types for the UiPro system. Defines NodePoint enum, NodeStyle holding layout/visual properties, and NodeLayout which computes rectangles for a node given a parent layout and style (including clipping and child inner rects).
using System;
namespace Sandbox.UiPro;
public enum NodePoint
{
TopLeft, TopMiddle, TopRight,
CenterLeft, CenterMiddle, CenterRight,
BottomLeft, BottomMiddle, BottomRight,
}
public class NodeStyle
{
[Property] public NodePoint Anchor { get; set; }
[Property] public NodePoint Pivot { get; set; }
[Property] public Vector2 Offset { get; set; }
[Property] public Vector2 Size { get; set; }
[Property] public Vector4 ChildPadding { get; set; }
[Property] public bool ClipChildren { get; set; }
[Property] public bool StretchHorizontal { get; set; }
[Property] public bool StretchVertical { get; set; }
[Property, Hide] public float CornerRadius { get; set; }
[Property, Hide] public float BorderWidth { get; set; }
[Property, Hide] public Color BorderColor { get; set; }
[Property, Hide] public Texture Texture { get; set; }
[Property, Hide] public Color Tint { get; set; }
[Property, Hide] public Vector2 UvScale { get; set; }
[Property, Hide] public Vector2 UvOffset { get; set; }
}
public class NodeLayout
{
public Rect Outer { get; private set; }
public Rect Inner { get; private set; }
public Rect ClipRect { get; private set; }
public float ClipRadius { get; private set; }
public Rect ChildClipRect { get; private set; }
public float ChildClipRadius { get; private set; }
public static NodeLayout GetRootLayout( Vector2 size )
{
Rect rootRect = new Rect( Vector2.Zero, size );
NodeLayout layout = new NodeLayout()
{
Outer = rootRect,
Inner = rootRect,
ClipRect = rootRect,
ClipRadius = 0,
ChildClipRect = rootRect,
ChildClipRadius = 0
};
return layout;
}
public void Compute( NodeLayout parentLayout, NodeStyle style )
{
Vector2 anchor = AnchorFraction( style.Anchor );
Vector2 pivot = AnchorFraction( style.Pivot );
float width = style.StretchHorizontal ? parentLayout.Inner.Size.x : style.Size.x;
float height = style.StretchVertical ? parentLayout.Inner.Size.y : style.Size.y;
float x = style.StretchHorizontal
? parentLayout.Inner.Position.x + style.Offset.x
: parentLayout.Inner.Position.x + anchor.x * parentLayout.Inner.Size.x - pivot.x * width + style.Offset.x;
float y = style.StretchVertical
? parentLayout.Inner.Position.y + style.Offset.y
: parentLayout.Inner.Position.y + anchor.y * parentLayout.Inner.Size.y - pivot.y * height + style.Offset.y;
Outer = new Rect( new Vector2( x, y ), new Vector2( width, height ) );
float innerW = Math.Max( 0f, width - style.ChildPadding.x - style.ChildPadding.z );
float innerH = Math.Max( 0f, height - style.ChildPadding.y - style.ChildPadding.w );
Inner = new Rect( new Vector2( x + style.ChildPadding.x, y + style.ChildPadding.y ), new Vector2( innerW, innerH ) );
ClipRect = parentLayout.ChildClipRect;
ClipRadius = parentLayout.ChildClipRadius;
if ( style.ClipChildren )
{
float bw = Math.Max( 0f, style.BorderWidth );
float clipW = Math.Max( 0f, Outer.Size.x - bw * 2f );
float clipH = Math.Max( 0f, Outer.Size.y - bw * 2f );
ChildClipRect = new Rect( new Vector2( Outer.Position.x + bw, Outer.Position.y + bw ), new Vector2( clipW, clipH ) );
ChildClipRadius = Math.Max( 0f, style.CornerRadius - bw );
}
else
{
ChildClipRect = parentLayout.ChildClipRect;
ChildClipRadius = parentLayout.ChildClipRadius;
}
}
private static Vector2 AnchorFraction( NodePoint point )
{
float fx = point switch
{
NodePoint.TopLeft or NodePoint.CenterLeft or NodePoint.BottomLeft => 0f,
NodePoint.TopMiddle or NodePoint.CenterMiddle or NodePoint.BottomMiddle => 0.5f,
_ => 1f,
};
float fy = point switch
{
NodePoint.TopLeft or NodePoint.TopMiddle or NodePoint.TopRight => 0f,
NodePoint.CenterLeft or NodePoint.CenterMiddle or NodePoint.CenterRight => 0.5f,
_ => 1f,
};
return new Vector2( fx, fy );
}
}