Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FlexPanel enhancements (grow, shrink, basis, fixes) #45

Merged
merged 5 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 53 additions & 1 deletion samples/Avalonia.Labs.Catalog/ViewModels/FlexItemViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Reactive.Linq;

using Avalonia.Labs.Panels;

using Avalonia.Layout;
using ReactiveUI;

namespace Avalonia.Labs.Catalog.ViewModels
Expand All @@ -17,6 +17,12 @@ public sealed class FlexItemViewModel : ReactiveObject

private AlignItems _alignSelfItem = AlignSelfAuto;
private int _order;
private double _shrink = 1.0;
private double _grow;
private double _basisValue = 100.0;
private FlexBasisKind _basisKind;
private HorizontalAlignment _horizontalAlignment;
private VerticalAlignment _verticalAlignment;

public FlexItemViewModel(int value)
{
Expand Down Expand Up @@ -53,5 +59,51 @@ public int Order
get => _order;
set => this.RaiseAndSetIfChanged(ref _order, value);
}

public double Shrink
{
get => _shrink;
set => this.RaiseAndSetIfChanged(ref _shrink, value);
}

public double Grow
{
get => _grow;
set => this.RaiseAndSetIfChanged(ref _grow, value);
}

public double BasisValue
{
get => _basisValue;
set
{
this.RaiseAndSetIfChanged(ref _basisValue, value);
this.RaisePropertyChanged(nameof(Basis));
}
}

public FlexBasisKind BasisKind
{
get => _basisKind;
set
{
this.RaiseAndSetIfChanged(ref _basisKind, value);
this.RaisePropertyChanged(nameof(Basis));
}
}

public FlexBasis Basis => new(_basisValue, _basisKind);

public HorizontalAlignment HorizontalAlignment
{
get => _horizontalAlignment;
set => this.RaiseAndSetIfChanged(ref _horizontalAlignment, value);
}

public VerticalAlignment VerticalAlignment
{
get => _verticalAlignment;
set => this.RaiseAndSetIfChanged(ref _verticalAlignment, value);
}
}
}
10 changes: 8 additions & 2 deletions samples/Avalonia.Labs.Catalog/ViewModels/FlexViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

using Avalonia.Labs.Catalog.Views;
using Avalonia.Labs.Panels;

using Avalonia.Layout;
using ReactiveUI;

namespace Avalonia.Labs.Catalog.ViewModels
Expand All @@ -22,7 +22,7 @@ public sealed class FlexViewModel : ViewModelBase
private AlignContent _alignContent = AlignContent.FlexStart;
private FlexWrap _wrap = FlexWrap.Wrap;

private int _columnSpacing = 8;
private int _columnSpacing = 64;
private int _rowSpacing = 32;

private int _currentNumber = 41;
Expand Down Expand Up @@ -56,6 +56,12 @@ public FlexViewModel()

public IEnumerable WrapValues { get; } = Enum.GetValues(typeof(FlexWrap));

public IEnumerable FlexBasisKindValues { get; } = Enum.GetValues(typeof(FlexBasisKind));

public IEnumerable HorizontalAlignmentValues { get; } = Enum.GetValues(typeof(HorizontalAlignment));

public IEnumerable VerticalAlignmentValues { get; } = Enum.GetValues(typeof(VerticalAlignment));

public IEnumerable AlignSelfValues { get; } = Enum.GetValues(typeof(AlignItems)).Cast<AlignItems>().Prepend(FlexItemViewModel.AlignSelfAuto);

public FlexDirection Direction
Expand Down
47 changes: 45 additions & 2 deletions samples/Avalonia.Labs.Catalog/Views/FlexView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
Gestures.Tapped="OnItemTapped">
<ListBoxItem.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Background" Value="LightBlue" />
<Setter Property="Background" Value="#228" />
</Style>
<Style Selector="ListBoxItem:selected">
<Setter Property="Background" Value="DodgerBlue" />
Expand Down Expand Up @@ -126,6 +126,44 @@
Value="{Binding SelectedItem.Order}" />
</StackPanel>

<StackPanel Spacing="8">
<TextBlock Text="SelectedItem Shrink:" />
<NumericUpDown Minimum="0.0"
IsEnabled="{Binding !!SelectedItem}"
Value="{Binding SelectedItem.Shrink}" />
</StackPanel>

<StackPanel Spacing="8">
<TextBlock Text="SelectedItem Grow:" />
<NumericUpDown Minimum="0.0"
IsEnabled="{Binding !!SelectedItem}"
Value="{Binding SelectedItem.Grow}" />
</StackPanel>

<StackPanel Spacing="8">
<TextBlock Text="SelectedItem Basis:" />
<ComboBox IsEnabled="{Binding !!SelectedItem}"
ItemsSource="{Binding FlexBasisKindValues}"
SelectedItem="{Binding SelectedItem.BasisKind}" />
<NumericUpDown Minimum="0.0"
IsEnabled="{Binding !!SelectedItem}"
Value="{Binding SelectedItem.BasisValue}" />
</StackPanel>

<StackPanel Spacing="8">
<TextBlock Text="SelectedItem HorizontalAlignment:" />
<ComboBox IsEnabled="{Binding !!SelectedItem}"
ItemsSource="{Binding HorizontalAlignmentValues}"
SelectedItem="{Binding SelectedItem.HorizontalAlignment}" />
</StackPanel>

<StackPanel Spacing="8">
<TextBlock Text="SelectedItem VerticalAlignment:" />
<ComboBox IsEnabled="{Binding !!SelectedItem}"
ItemsSource="{Binding VerticalAlignmentValues}"
SelectedItem="{Binding SelectedItem.VerticalAlignment}" />
</StackPanel>

<Grid ColumnDefinitions="*,8,*">

<Button Grid.Column="0"
Expand All @@ -147,7 +185,7 @@
</ScrollViewer>

<ItemsControl IsVisible="{Binding IsItemsControl}"
BorderBrush="Black"
BorderBrush="#666"
BorderThickness="1"
ItemsSource="{Binding Numbers}"
ItemTemplate="{StaticResource ItemTemplate}">
Expand All @@ -156,6 +194,11 @@
x:DataType="vm:FlexItemViewModel">
<Setter Property="panels:Flex.AlignSelf" Value="{Binding AlignSelf}" />
<Setter Property="panels:Flex.Order" Value="{Binding Order}" />
<Setter Property="panels:Flex.Shrink" Value="{Binding Shrink}" />
<Setter Property="panels:Flex.Grow" Value="{Binding Grow}" />
<Setter Property="panels:Flex.Basis" Value="{Binding Basis}" />
<Setter Property="HorizontalAlignment" Value="{Binding HorizontalAlignment}" />
<Setter Property="VerticalAlignment" Value="{Binding VerticalAlignment}" />
<Setter Property="IsVisible" Value="{Binding IsVisible}" />
</Style>
</ItemsControl.Styles>
Expand Down
115 changes: 115 additions & 0 deletions src/Avalonia.Labs.Panels/Flex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ public static class Flex
public static readonly AttachedProperty<int> OrderProperty =
AvaloniaProperty.RegisterAttached<Layoutable, int>("Order", typeof(Flex));

public static readonly AttachedProperty<FlexBasis> BasisProperty =
AvaloniaProperty.RegisterAttached<Layoutable, FlexBasis>("Basis", typeof(Flex), FlexBasis.Auto);

public static readonly AttachedProperty<double> ShrinkProperty =
AvaloniaProperty.RegisterAttached<Layoutable, double>("Shrink", typeof(Flex), 1.0, validate: v => v >= 0.0);

public static readonly AttachedProperty<double> GrowProperty =
AvaloniaProperty.RegisterAttached<Layoutable, double>("Grow", typeof(Flex), 0.0, validate: v => v >= 0.0);

internal static readonly AttachedProperty<double> BaseLengthProperty =
AvaloniaProperty.RegisterAttached<Layoutable, double>("BaseLength", typeof(Flex), 0.0);

internal static readonly AttachedProperty<double> CurrentLengthProperty =
AvaloniaProperty.RegisterAttached<Layoutable, double>("CurrentLength", typeof(Flex), 0.0);

/// <summary>
/// Gets the child alignment in a flex layout
/// </summary>
Expand Down Expand Up @@ -69,5 +84,105 @@ public static void SetOrder(Layoutable layoutable, int value)

layoutable.SetValue(OrderProperty, value);
}

public static FlexBasis GetBasis(Layoutable layoutable)
{
if (layoutable is null)
{
throw new ArgumentNullException(nameof(layoutable));
}

return layoutable.GetValue(BasisProperty);
}

public static void SetBasis(Layoutable layoutable, FlexBasis value)
{
if (layoutable is null)
{
throw new ArgumentNullException(nameof(layoutable));
}

layoutable.SetValue(BasisProperty, value);
}

public static double GetShrink(Layoutable layoutable)
{
if (layoutable is null)
{
throw new ArgumentNullException(nameof(layoutable));
}

return layoutable.GetValue(ShrinkProperty);
}

public static void SetShrink(Layoutable layoutable, double value)
{
if (layoutable is null)
{
throw new ArgumentNullException(nameof(layoutable));
}

layoutable.SetValue(ShrinkProperty, value);
}

public static double GetGrow(Layoutable layoutable)
{
if (layoutable is null)
{
throw new ArgumentNullException(nameof(layoutable));
}

return layoutable.GetValue(GrowProperty);
}

public static void SetGrow(Layoutable layoutable, double value)
{
if (layoutable is null)
{
throw new ArgumentNullException(nameof(layoutable));
}

layoutable.SetValue(GrowProperty, value);
}

internal static double GetBaseLength(Layoutable layoutable)
{
if (layoutable is null)
{
throw new ArgumentNullException(nameof(layoutable));
}

return layoutable.GetValue(BaseLengthProperty);
}

internal static void SetBaseLength(Layoutable layoutable, double value)
{
if (layoutable is null)
{
throw new ArgumentNullException(nameof(layoutable));
}

layoutable.SetValue(BaseLengthProperty, value);
}

internal static double GetCurrentLength(Layoutable layoutable)
{
if (layoutable is null)
{
throw new ArgumentNullException(nameof(layoutable));
}

return layoutable.GetValue(CurrentLengthProperty);
}

internal static void SetCurrentLength(Layoutable layoutable, double value)
{
if (layoutable is null)
{
throw new ArgumentNullException(nameof(layoutable));
}

layoutable.SetValue(CurrentLengthProperty, value);
}
}
}
70 changes: 70 additions & 0 deletions src/Avalonia.Labs.Panels/FlexBasis.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;

namespace Avalonia.Labs.Panels;

public readonly struct FlexBasis : IEquatable<FlexBasis>
{
public double Value { get; }

public FlexBasisKind Kind { get; }

public FlexBasis(double value, FlexBasisKind kind)
{
if (value < 0 || double.IsNaN(value) || double.IsInfinity(value))
throw new ArgumentException($"Invalid basis value: {value}", nameof(value));
if (kind < FlexBasisKind.Auto || kind > FlexBasisKind.Relative)
throw new ArgumentException($"Invalid basis kind: {kind}", nameof(kind));
Value = value;
Kind = kind;
}

public FlexBasis(double value) : this(value, FlexBasisKind.Absolute) { }

public static FlexBasis Auto => new(0.0, FlexBasisKind.Auto);

public bool IsAuto => Kind == FlexBasisKind.Auto;

public bool IsAbsolute => Kind == FlexBasisKind.Absolute;

public bool IsRelative => Kind == FlexBasisKind.Relative;

[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")]
public bool Equals(FlexBasis other) =>
(IsAuto && other.IsAuto) || (Value == other.Value && Kind == other.Kind);

public override bool Equals(object? obj) =>
obj is FlexBasis other && Equals(other);

public override int GetHashCode() =>
HashCode.Combine(Value, (int)Kind);

public static bool operator ==(FlexBasis left, FlexBasis right) =>
left.Equals(right);

public static bool operator !=(FlexBasis left, FlexBasis right) =>
!left.Equals(right);

public override string ToString()
{
return Kind switch
{
FlexBasisKind.Auto => "Auto",
FlexBasisKind.Absolute => FormattableString.Invariant($"{Value:G17}"),
FlexBasisKind.Relative => FormattableString.Invariant($"{Value * 100:G17}%"),
_ => throw new InvalidOperationException(),
};
}

public static FlexBasis Parse(string str)
{
return str.ToUpperInvariant() switch
{
"AUTO" => Auto,
var s when s.EndsWith("%") => new FlexBasis(ParseDouble(s[..^1].Trim()) / 100, FlexBasisKind.Relative),
_ => new FlexBasis(ParseDouble(str), FlexBasisKind.Absolute),
};
double ParseDouble(string s) => double.Parse(s, CultureInfo.InvariantCulture);
}
}
8 changes: 8 additions & 0 deletions src/Avalonia.Labs.Panels/FlexBasisKind.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Avalonia.Labs.Panels;

public enum FlexBasisKind
{
Auto,
Absolute,
Relative,
}
Loading