Skip to content

Conversation

@emmauss
Copy link
Contributor

@emmauss emmauss commented Dec 11, 2025

What does the pull request do?

Adds FlexPanel control, adapted from css's flex container.

This implementation was created by @jp2masa and initially contributed to the Labs library here, AvaloniaUI/Avalonia.Labs#37 . The core team has decided that this would be a good time to move that panel to the core repository.

The following API have been added;

public sealed class FlexPanel : Panel
{
    /// <summary>
    /// Gets or sets the direction of the <see cref="FlexPanel"/>'s main-axis,
    /// determining the orientation in which child controls are laid out.
    /// </summary>
    /// <remarks>
    /// When omitted, it is set to <see cref="FlexDirection.Row"/>.
    /// Equivalent to CSS flex-direction property
    /// </remarks>
    public FlexDirection Direction { get; set; }

    /// <summary>
    /// Gets or sets the main-axis alignment of child items inside a line of the <see cref="FlexPanel"/>.
    /// Typically used to distribute extra free space leftover after flexible lengths and margins have been resolved.
    /// </summary>
    /// <remarks>
    /// When omitted, it is set to <see cref="JustifyContent.FlexStart"/>.
    /// Equivalent to CSS justify-content property.
    /// </remarks>
    public JustifyContent JustifyContent { get; set; }

    /// <summary>
    /// Gets or sets the cross-axis alignment of all child items inside a line of the <see cref="FlexPanel"/>.
    /// Similar to <see cref="JustifyContent"/>, but in the perpendicular direction.
    /// </summary>
    /// <remarks>
    /// When omitted, it is set to <see cref="AlignItems.Stretch"/>.
    /// Equivalent to CSS align-items property.
    /// </remarks>
    public AlignItems AlignItems { get; set; }

    /// <summary>
    /// Gets or sets the cross-axis alignment of lines in the <see cref="FlexPanel"/> when there is extra space. 
    /// Similar to <see cref="AlignItems"/>, but for entire lines.
    /// <see cref="FlexPanel.Wrap"/> property set to <see cref="FlexWrap.Wrap"/> mode
    /// allows controls to be arranged on multiple lines.
    /// </summary>
    /// <remarks>
    /// When omitted, it is set to <see cref="AlignContent.Stretch"/>.
    /// Equivalent to CSS align-content property.
    /// </remarks>
    public AlignContent AlignContent { get; set; }

    /// <summary>
    /// Gets or sets the wrap mode, controlling whether the <see cref="FlexPanel"/> is single-line or multi-line.
    /// Additionally, it determines the cross-axis stacking direction for new lines.
    /// </summary>
    /// <remarks>
    /// When omitted, it is set to <see cref="FlexWrap.NoWrap"/>.
    /// Equivalent to CSS flex-wrap property.
    /// </remarks>
    public FlexWrap Wrap { get; set; }

    /// <summary>
    /// Gets or sets the minimum horizontal spacing between child items or lines,
    /// depending on main-axis direction of the <see cref="FlexPanel"/>.
    /// </summary>
    /// <remarks>
    /// When omitted, it is set to 0.
    /// Similar to CSS column-gap property.
    /// </remarks>
    public double ColumnSpacing { get; set; }

    /// <summary>
    /// Gets or sets the minimum vertical spacing between child items or lines,
    /// depending on main-axis direction of the <see cref="FlexPanel"/>.
    /// </summary>
    /// <remarks>
    /// When omitted, it is set to 0.
    /// Similar to CSS row-gap property.
    /// </remarks>
    public double RowSpacing { get; set; }

    /// <summary>
    /// Defines an attached property to control the initial main-axis size of a specific child in a flex layout.
    /// </summary>
    public static readonly AttachedProperty<FlexBasis> BasisProperty;

    /// <summary>
    /// Defines an attached property to control the cross-axis alignment of a specific child in a flex layout.
    /// </summary>
    public static readonly AttachedProperty<AlignItems?> AlignSelfProperty;

    /// <summary>
    /// Defines an attached property to control the order of a specific child in a flex layout.
    /// </summary>
    public static readonly AttachedProperty<int> OrderProperty;

    /// <summary>
    /// Defines an attached property to control the factor by which a specific child can shrink
    /// along the main-axis in a flex layout.
    /// </summary>
    public static readonly AttachedProperty<double> ShrinkProperty;

    /// <summary>
    /// Defines an attached property to control the factor by which a specific child can grow
    /// along the main-axis in a flex layout.
    /// </summary>
    public static readonly AttachedProperty<double> GrowProperty;
}

/// <summary>
/// Defines the alignment mode of the lines inside a <see cref="FlexPanel"/> along the cross-axis. 
/// </summary>
public enum AlignContent
{
    /// <summary>
    /// Lines are packed toward the start of the container.
    /// </summary>
    FlexStart,
    
    /// <summary>
    /// Lines are packed toward the end of the container.
    /// </summary>
    FlexEnd,
    
    /// <summary>
    /// Lines are packed toward the center of the container
    /// </summary>
    Center,
    
    /// <summary>
    /// Lines are stretched to take up the remaining space.
    /// </summary>
    /// <remarks>
    /// This is the default value.
    /// </remarks>
    Stretch,
    
    /// <summary>
    /// Lines are evenly distributed in the container, with no space on either end.
    /// </summary>
    SpaceBetween,
    
    /// <summary>
    /// Lines are evenly distributed in the container, with half-size spaces on either end.
    /// </summary>
    SpaceAround,
    
    /// <summary>
    /// Lines are evenly distributed in the container, with equal-size spaces between each line and on either end. 
    /// </summary>
    SpaceEvenly
}

public enum AlignItems
{
    /// <summary>
    /// Items are aligned to the cross-axis start margin edge of the line.
    /// </summary>
    FlexStart,

    /// <summary>
    /// Items are aligned to the cross-axis end margin edge of the line.
    /// </summary>
    FlexEnd,

    /// <summary>
    /// Items are aligned to the cross-axis center of the line.
    /// </summary>
    /// <remarks>
    /// If the cross size of the line is less than that of the child item,
    /// it will overflow equally in both directions.
    /// </remarks>
    Center,

    /// <summary>
    /// Items are stretched to fill the cross size of the line.
    /// </summary>
    /// <remarks>
    /// This is the default value.
    /// </remarks>
    Stretch
}

/// <summary>
/// Describes the main-axis alignment of items inside a <see cref="FlexPanel"/> line.
/// </summary>
public enum JustifyContent
{
    /// <summary>
    /// Child items are packed toward the start of the line.
    /// </summary>
    /// <remarks>
    /// This is the default value.
    /// </remarks>
    FlexStart,

    /// <summary>
    /// Child items are packed toward the end of the line.
    /// </summary>
    FlexEnd,

    /// <summary>
    /// Child items are packed toward the center of the line.
    /// </summary>
    /// <remarks>
    /// If the leftover free-space is negative, the child items will overflow equally in both directions.
    /// </remarks>
    Center,

    /// <summary>
    /// Child items are evenly distributed in the line, with no space on either end.
    /// </summary>
    /// <remarks>
    /// If the leftover free-space is negative or there is only a single child item on the line,
    /// this value is identical to <see cref="FlexStart"/>.
    /// </remarks>
    SpaceBetween,

    /// <summary>
    /// Child items are evenly distributed in the line, with half-size spaces on either end.
    /// </summary>
    /// <remarks>
    /// If the leftover free-space is negative or there is only a single child item on the line,
    /// this value is identical to <see cref="Center"/>.
    /// </remarks>
    SpaceAround,

    /// <summary>
    /// Child items are evenly distributed in the line, with equal-size spaces between each item and on either end.
    /// </summary>
    SpaceEvenly
}

/// <summary>
/// Describes the orientation and direction along which items are placed inside the <see cref="FlexPanel"/>
/// </summary>
public enum FlexDirection
{
    /// <summary>
    /// Items are placed along the horizontal axis, starting from the left
    /// </summary>
    /// <remarks>
    /// This is the default value.
    /// </remarks>
    Row,

    /// <summary>
    /// Items are placed along the horizontal axis, starting from the right
    /// </summary>
    RowReverse,

    /// <summary>
    /// Items are placed along the vertical axis, starting from the top
    /// </summary>
    Column,

    /// <summary>
    /// Items are placed along the vertical axis, starting from the bottom
    /// </summary>
    ColumnReverse
}

/// <summary>
/// Describes the wrap behavior of the <see cref="FlexPanel"/>
/// </summary>
public enum FlexWrap
{
    /// <summary>
    /// The <see cref="FlexPanel"/> is single line.
    /// </summary>
    /// <remarks>
    /// This is the default value.
    /// </remarks>
    NoWrap,
    
    /// <summary>
    /// The <see cref="FlexPanel"/> is multi line.
    /// </summary>
    Wrap,
    
    /// <summary>
    /// Same as <see cref="Wrap"/> but new lines are added in the opposite cross-axis direction.
    /// </summary>
    WrapReverse
}

/// <summary>
/// Specifies the initial size of a flex item.
/// </summary>
public readonly struct FlexBasis : IEquatable<FlexBasis>
{
    public double Value { get; }

    public FlexBasisKind Kind { get; }

    /// <summary>
    /// Initializes an instance of <see cref="FlexBasis"/> and sets the value and <see cref="FlexBasisKind"/>
    /// </summary>
    /// <param name="value">The value of the <see cref="FlexBasis"/></param>
    /// <param name="kind">The <see cref="FlexBasisKind">. This determines how the value affects the size of the flex item</see>/></param>
    /// <exception cref="ArgumentException"></exception>
    public FlexBasis(double value, FlexBasisKind kind);

    /// <summary>
    /// Initializes an instance of <see cref="FlexBasis"/> and sets the absolute value
    /// </summary>
    /// <param name="value">The absolute value of the <see cref="FlexBasis"/></param>
    /// <exception cref="ArgumentException"></exception>
    public FlexBasis(double value) ;

    public static FlexBasis Auto { get; }

    public bool IsAuto  { get; }
    
    public bool IsAbsolute { get; }
    
    public bool IsRelative { get; }

    /// <summary>
    /// Converts a string flex-basis value to a <see cref="FlexBasis"/> instance.
    /// </summary>
    /// <param name="str">The value to parse.</param>
    /// <returns></returns>
    public static FlexBasis Parse(string str);
}

What is the current behavior?

What is the updated/expected behavior with this PR?

How was the solution implemented (if it's not obvious)?

Checklist

Breaking changes

Obsoletions / Deprecations

Fixed issues

Fixes #8081

@emmauss emmauss changed the title [Fe]Add Flex Panel [Feature] Add Flex Panel Dec 11, 2025
@emmauss emmauss added enhancement feature needs-api-review The PR adds new public APIs that should be reviewed. labels Dec 11, 2025
@Athari
Copy link
Contributor

Athari commented Dec 15, 2025

Awesome to see FlexPanel being merged; using it every day. Kudos to @Al12rs for fixing and documenting it after my upgrade.

Something like this could be added for FlexBasis too, I think?

class AvaloniaXamlIlGridLengthAstNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
{
private readonly AvaloniaXamlIlWellKnownTypes _types;
private readonly GridLength _gridLength;
public AvaloniaXamlIlGridLengthAstNode(IXamlLineInfo lineInfo, AvaloniaXamlIlWellKnownTypes types, GridLength gridLength) : base(lineInfo)
{
_types = types;
_gridLength = gridLength;
Type = new XamlAstClrTypeReference(lineInfo, types.GridLength, false);
}
public IXamlAstTypeReference Type { get; }
public XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
codeGen
.Ldc_R8(_gridLength.Value)
.Ldc_I4((int)_gridLength.GridUnitType)
.Newobj(_types.GridLengthConstructorValueType);
return XamlILNodeEmitResult.Type(0, Type.GetClrType());
}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement feature needs-api-review The PR adds new public APIs that should be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FlexPanel

3 participants