Merge pull request #261 from irihitech/ElasticWrapPanel
Add ElasticWrapPanel control
This commit is contained in:
0
demo/Ursa.Demo/Models/MenuKeys.cs
Normal file
0
demo/Ursa.Demo/Models/MenuKeys.cs
Normal file
319
demo/Ursa.Demo/Pages/ElasticWrapPanelDemo.axaml
Normal file
319
demo/Ursa.Demo/Pages/ElasticWrapPanelDemo.axaml
Normal file
@@ -0,0 +1,319 @@
|
||||
<UserControl x:Class="Ursa.Demo.Pages.ElasticWrapPanelDemo"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:u="https://irihi.tech/ursa"
|
||||
xmlns:viewModels="clr-namespace:Ursa.Demo.ViewModels"
|
||||
d:DesignHeight="800"
|
||||
d:DesignWidth="800"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:ElasticWrapPanelDemoViewModel"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Styles>
|
||||
<Style Selector="u|FormItem">
|
||||
<Setter Property="Width" Value="320" />
|
||||
</Style>
|
||||
<Style Selector="TabItem#FixToRBTabItem WrapPanel > Border, u|ElasticWrapPanel > Border">
|
||||
<Setter Property="Width" Value="{Binding ItemSelfWidth}" />
|
||||
<Setter Property="Height" Value="{Binding ItemSelfHeight}" />
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
<DockPanel>
|
||||
<u:Form DockPanel.Dock="Left"
|
||||
LabelAlignment="Left"
|
||||
LabelPosition="Left"
|
||||
LabelWidth="*">
|
||||
<u:FormGroup>
|
||||
<u:FormItem Label="Orientation">
|
||||
<u:EnumSelector EnumType="Orientation" Value="{Binding SelectedOrientation}" />
|
||||
</u:FormItem>
|
||||
<u:FormItem Label="HorizontalScrollBar">
|
||||
<u:EnumSelector EnumType="ScrollBarVisibility" Value="{Binding HorizontalVisibility}" />
|
||||
</u:FormItem>
|
||||
<u:FormItem Label="VerticalScrollBar">
|
||||
<u:EnumSelector EnumType="ScrollBarVisibility" Value="{Binding VerticalVisibility}" />
|
||||
</u:FormItem>
|
||||
<u:FormItem>
|
||||
<u:FormItem.Label>
|
||||
<CheckBox Content="IsFillHorizontal" IsChecked="{Binding IsFillHorizontal}" />
|
||||
</u:FormItem.Label>
|
||||
</u:FormItem>
|
||||
<u:FormItem Label="ItemWidth">
|
||||
<u:NumericDoubleUpDown
|
||||
Step="1"
|
||||
AllowDrag="True"
|
||||
Maximum="1000"
|
||||
Minimum="0"
|
||||
Value="{Binding ItemWidth}" />
|
||||
</u:FormItem>
|
||||
<u:FormItem>
|
||||
<u:FormItem.Label>
|
||||
<CheckBox Content="IsFillVertical" IsChecked="{Binding IsFillVertical}" />
|
||||
</u:FormItem.Label>
|
||||
</u:FormItem>
|
||||
<u:FormItem Label="ItemHeight">
|
||||
<u:NumericDoubleUpDown
|
||||
AllowDrag="True"
|
||||
Step="1"
|
||||
Maximum="1000"
|
||||
Minimum="0"
|
||||
Value="{Binding ItemHeight}" />
|
||||
</u:FormItem>
|
||||
</u:FormGroup>
|
||||
<u:FormGroup Header="FixToRB Extension" IsEnabled="{Binding #FixToRBTabItem.IsSelected}">
|
||||
<u:FormItem>
|
||||
<u:FormItem.Label>
|
||||
<CheckBox Content="AutoWidth" IsChecked="{Binding AutoWidth}" />
|
||||
</u:FormItem.Label>
|
||||
</u:FormItem>
|
||||
<u:FormItem Label="ItemSelfWidth">
|
||||
<u:NumericDoubleUpDown
|
||||
AllowDrag="True"
|
||||
Maximum="1000"
|
||||
Minimum="0"
|
||||
IsEnabled="{Binding !AutoWidth}"
|
||||
Value="{Binding ItemSelfWidth}" />
|
||||
</u:FormItem>
|
||||
<u:FormItem>
|
||||
<u:FormItem.Label>
|
||||
<CheckBox Content="AutoHeight" IsChecked="{Binding AutoHeight}" />
|
||||
</u:FormItem.Label>
|
||||
</u:FormItem>
|
||||
<u:FormItem Label="ItemSelfHeight">
|
||||
<u:NumericDoubleUpDown
|
||||
AllowDrag="True"
|
||||
Maximum="1000"
|
||||
Minimum="0"
|
||||
IsEnabled="{Binding !AutoHeight}"
|
||||
Value="{Binding ItemSelfHeight}" />
|
||||
</u:FormItem>
|
||||
<u:FormItem Label="HorizontalAlignment">
|
||||
<u:EnumSelector EnumType="HorizontalAlignment" Value="{Binding CmbHAlign}" />
|
||||
</u:FormItem>
|
||||
<u:FormItem Label="VerticalAlignment">
|
||||
<u:EnumSelector EnumType="VerticalAlignment" Value="{Binding CmbVAlign}" />
|
||||
</u:FormItem>
|
||||
</u:FormGroup>
|
||||
</u:Form>
|
||||
<TabControl>
|
||||
<TabItem Header="Flat">
|
||||
<TabControl>
|
||||
<TabItem Header="Common">
|
||||
<u:ElasticWrapPanel IsFillHorizontal="{Binding IsFillHorizontal}"
|
||||
IsFillVertical="{Binding IsFillVertical}"
|
||||
ItemHeight="{Binding ItemHeight}"
|
||||
ItemWidth="{Binding ItemWidth}"
|
||||
Orientation="{Binding SelectedOrientation}">
|
||||
<Border Background="{DynamicResource SemiRed5Color}" />
|
||||
<Border Background="{DynamicResource SemiPink5Color}" />
|
||||
<Border Background="{DynamicResource SemiPurple5Color}" />
|
||||
<Border Background="{DynamicResource SemiViolet5Color}" />
|
||||
<Border Background="{DynamicResource SemiIndigo5Color}" />
|
||||
<Border Background="{DynamicResource SemiBlue5Color}" />
|
||||
<Border Background="{DynamicResource SemiLightBlue5Color}" />
|
||||
<Border Background="{DynamicResource SemiCyan5Color}" />
|
||||
<Border Background="{DynamicResource SemiTeal5Color}" />
|
||||
<Border Background="{DynamicResource SemiGreen5Color}" />
|
||||
<Border Background="{DynamicResource SemiLightGreen5Color}" />
|
||||
<Border Background="{DynamicResource SemiLime5Color}" />
|
||||
<Border Background="{DynamicResource SemiYellow5Color}" />
|
||||
<Border Background="{DynamicResource SemiAmber5Color}" />
|
||||
<Border Background="{DynamicResource SemiOrange5Color}" />
|
||||
<Border Background="{DynamicResource SemiGrey5Color}" />
|
||||
</u:ElasticWrapPanel>
|
||||
</TabItem>
|
||||
<TabItem Header="Grid Auto">
|
||||
<Grid RowDefinitions="auto,*">
|
||||
<u:ElasticWrapPanel Grid.Row="0"
|
||||
IsFillHorizontal="{Binding IsFillHorizontal}"
|
||||
IsFillVertical="{Binding IsFillVertical}"
|
||||
ItemHeight="{Binding ItemHeight}"
|
||||
ItemWidth="{Binding ItemWidth}"
|
||||
Orientation="{Binding SelectedOrientation}">
|
||||
<Border Background="{DynamicResource SemiRed5Color}" />
|
||||
<Border Background="{DynamicResource SemiPink5Color}" />
|
||||
<Border Background="{DynamicResource SemiPurple5Color}" />
|
||||
<Border Background="{DynamicResource SemiViolet5Color}" />
|
||||
<Border Background="{DynamicResource SemiIndigo5Color}" />
|
||||
<Border Background="{DynamicResource SemiBlue5Color}" />
|
||||
<Border Background="{DynamicResource SemiLightBlue5Color}" />
|
||||
<Border Background="{DynamicResource SemiCyan5Color}" />
|
||||
<Border Background="{DynamicResource SemiTeal5Color}" />
|
||||
<Border Background="{DynamicResource SemiGreen5Color}" />
|
||||
<Border Background="{DynamicResource SemiLightGreen5Color}" />
|
||||
<Border Background="{DynamicResource SemiLime5Color}" />
|
||||
<Border Background="{DynamicResource SemiYellow5Color}" />
|
||||
<Border Background="{DynamicResource SemiAmber5Color}" />
|
||||
<Border Background="{DynamicResource SemiOrange5Color}" />
|
||||
<Border Background="{DynamicResource SemiGrey5Color}" />
|
||||
</u:ElasticWrapPanel>
|
||||
<Border Grid.Row="1" Margin="0,4" Theme="{StaticResource CardBorder}">
|
||||
<TextBlock Text="This is blank" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem Header="ScrollViewer">
|
||||
<Border Margin="0,4">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="{Binding HorizontalVisibility}"
|
||||
VerticalScrollBarVisibility="{Binding VerticalVisibility}">
|
||||
<u:ElasticWrapPanel IsFillHorizontal="{Binding IsFillHorizontal}"
|
||||
IsFillVertical="{Binding IsFillVertical}"
|
||||
ItemHeight="{Binding ItemHeight}"
|
||||
ItemWidth="{Binding ItemWidth}"
|
||||
Orientation="{Binding SelectedOrientation}">
|
||||
<Border Background="{DynamicResource SemiRed5Color}" />
|
||||
<Border Background="{DynamicResource SemiPink5Color}" />
|
||||
<Border Background="{DynamicResource SemiPurple5Color}" />
|
||||
<Border Background="{DynamicResource SemiViolet5Color}" />
|
||||
<Border Background="{DynamicResource SemiIndigo5Color}" />
|
||||
<Border Background="{DynamicResource SemiBlue5Color}" />
|
||||
<Border Background="{DynamicResource SemiLightBlue5Color}" />
|
||||
<Border Background="{DynamicResource SemiCyan5Color}" />
|
||||
<Border Background="{DynamicResource SemiTeal5Color}" />
|
||||
<Border Background="{DynamicResource SemiGreen5Color}" />
|
||||
<Border Background="{DynamicResource SemiLightGreen5Color}" />
|
||||
<Border Background="{DynamicResource SemiLime5Color}" />
|
||||
<Border Background="{DynamicResource SemiYellow5Color}" />
|
||||
<Border Background="{DynamicResource SemiAmber5Color}" />
|
||||
<Border Background="{DynamicResource SemiOrange5Color}" />
|
||||
<Border Background="{DynamicResource SemiGrey5Color}" />
|
||||
</u:ElasticWrapPanel>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="FixToRB" Name="FixToRBTabItem">
|
||||
<DockPanel>
|
||||
<TabControl>
|
||||
<TabItem Header="Single">
|
||||
<StackPanel>
|
||||
<ScrollViewer HorizontalScrollBarVisibility="{Binding HorizontalVisibility}"
|
||||
VerticalScrollBarVisibility="{Binding VerticalVisibility}">
|
||||
<StackPanel>
|
||||
<u:Divider HorizontalContentAlignment="Left" Content="WrapPanel" />
|
||||
<WrapPanel ItemHeight="{Binding ItemHeight}"
|
||||
ItemWidth="{Binding ItemWidth}"
|
||||
Orientation="{Binding SelectedOrientation}">
|
||||
<Border Background="{DynamicResource SemiRed5Color}" />
|
||||
<Border Background="{DynamicResource SemiPink5Color}" />
|
||||
<Border Background="{DynamicResource SemiPurple5Color}" />
|
||||
<Border Background="{DynamicResource SemiViolet5Color}" />
|
||||
<UniformGrid MinWidth="200"
|
||||
HorizontalAlignment="{Binding CmbHAlign}"
|
||||
VerticalAlignment="{Binding CmbVAlign}"
|
||||
Rows="1">
|
||||
<Button Content="Search" />
|
||||
<Button Content="Export" />
|
||||
</UniformGrid>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
<ScrollViewer HorizontalScrollBarVisibility="{Binding HorizontalVisibility}"
|
||||
VerticalScrollBarVisibility="{Binding VerticalVisibility}">
|
||||
<StackPanel>
|
||||
<u:Divider HorizontalContentAlignment="Left" Content="ElasticWrapPanel" />
|
||||
<u:ElasticWrapPanel IsFillHorizontal="{Binding IsFillHorizontal}"
|
||||
IsFillVertical="{Binding IsFillVertical}"
|
||||
ItemHeight="{Binding ItemHeight}"
|
||||
ItemWidth="{Binding ItemWidth}"
|
||||
Orientation="{Binding SelectedOrientation}">
|
||||
<Border Background="{DynamicResource SemiRed5Color}" />
|
||||
<Border Background="{DynamicResource SemiPink5Color}" />
|
||||
<Border Background="{DynamicResource SemiPurple5Color}" />
|
||||
<Border Background="{DynamicResource SemiViolet5Color}" />
|
||||
<UniformGrid MinWidth="200"
|
||||
HorizontalAlignment="{Binding CmbHAlign}"
|
||||
VerticalAlignment="{Binding CmbVAlign}"
|
||||
u:ElasticWrapPanel.FixToRB="True"
|
||||
Rows="1">
|
||||
<Button Content="Search" />
|
||||
<Button Content="Export" />
|
||||
</UniformGrid>
|
||||
</u:ElasticWrapPanel>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
<TabItem Header="Multiply">
|
||||
<StackPanel>
|
||||
<ScrollViewer HorizontalScrollBarVisibility="{Binding HorizontalVisibility}"
|
||||
VerticalScrollBarVisibility="{Binding VerticalVisibility}">
|
||||
<StackPanel>
|
||||
<u:Divider HorizontalContentAlignment="Left" Content="WrapPanel" />
|
||||
<WrapPanel ItemHeight="{Binding ItemHeight}"
|
||||
ItemWidth="{Binding ItemWidth}"
|
||||
Orientation="{Binding SelectedOrientation}">
|
||||
<Border Background="{DynamicResource SemiRed5Color}" />
|
||||
<Border Background="{DynamicResource SemiPink5Color}" />
|
||||
<Border Background="{DynamicResource SemiPurple5Color}" />
|
||||
<Border Background="{DynamicResource SemiViolet5Color}" />
|
||||
<UniformGrid MinWidth="200"
|
||||
HorizontalAlignment="{Binding CmbHAlign}"
|
||||
VerticalAlignment="{Binding CmbVAlign}"
|
||||
u:ElasticWrapPanel.FixToRB="True"
|
||||
Rows="1">
|
||||
<Button Content="Search" />
|
||||
<Button Content="Export" />
|
||||
</UniformGrid>
|
||||
<Border Background="{DynamicResource SemiIndigo5Color}" />
|
||||
<Border Background="{DynamicResource SemiBlue5Color}" />
|
||||
<Border Background="{DynamicResource SemiLightBlue5Color}" />
|
||||
<Border Background="{DynamicResource SemiCyan5Color}" />
|
||||
<UniformGrid MinWidth="200"
|
||||
HorizontalAlignment="{Binding CmbHAlign}"
|
||||
VerticalAlignment="{Binding CmbVAlign}"
|
||||
u:ElasticWrapPanel.FixToRB="True"
|
||||
Rows="1">
|
||||
<Button Content="Search" />
|
||||
<Button Content="Export" />
|
||||
</UniformGrid>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
<ScrollViewer HorizontalScrollBarVisibility="{Binding HorizontalVisibility}"
|
||||
VerticalScrollBarVisibility="{Binding VerticalVisibility}">
|
||||
<StackPanel>
|
||||
<u:Divider HorizontalContentAlignment="Left" Content="ElasticWrapPanel" />
|
||||
<u:ElasticWrapPanel IsFillHorizontal="{Binding IsFillHorizontal}"
|
||||
IsFillVertical="{Binding IsFillVertical}"
|
||||
ItemHeight="{Binding ItemHeight}"
|
||||
ItemWidth="{Binding ItemWidth}"
|
||||
Orientation="{Binding SelectedOrientation}">
|
||||
<Border Background="{DynamicResource SemiRed5Color}" />
|
||||
<Border Background="{DynamicResource SemiPink5Color}" />
|
||||
<Border Background="{DynamicResource SemiPurple5Color}" />
|
||||
<Border Background="{DynamicResource SemiViolet5Color}" />
|
||||
<UniformGrid MinWidth="200"
|
||||
HorizontalAlignment="{Binding CmbHAlign}"
|
||||
VerticalAlignment="{Binding CmbVAlign}"
|
||||
u:ElasticWrapPanel.FixToRB="True"
|
||||
Rows="1">
|
||||
<Button Content="Search" />
|
||||
<Button Content="Export" />
|
||||
</UniformGrid>
|
||||
<Border Background="{DynamicResource SemiIndigo5Color}" />
|
||||
<Border Background="{DynamicResource SemiBlue5Color}" />
|
||||
<Border Background="{DynamicResource SemiLightBlue5Color}" />
|
||||
<Border Background="{DynamicResource SemiCyan5Color}" />
|
||||
<UniformGrid MinWidth="200"
|
||||
HorizontalAlignment="{Binding CmbHAlign}"
|
||||
VerticalAlignment="{Binding CmbVAlign}"
|
||||
u:ElasticWrapPanel.FixToRB="True"
|
||||
Rows="1">
|
||||
<Button Content="Search" />
|
||||
<Button Content="Export" />
|
||||
</UniformGrid>
|
||||
</u:ElasticWrapPanel>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</UserControl>
|
||||
13
demo/Ursa.Demo/Pages/ElasticWrapPanelDemo.axaml.cs
Normal file
13
demo/Ursa.Demo/Pages/ElasticWrapPanelDemo.axaml.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Avalonia.Controls;
|
||||
using Ursa.Demo.ViewModels;
|
||||
|
||||
namespace Ursa.Demo.Pages;
|
||||
|
||||
public partial class ElasticWrapPanelDemo : UserControl
|
||||
{
|
||||
public ElasticWrapPanelDemo()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new ElasticWrapPanelDemoViewModel();
|
||||
}
|
||||
}
|
||||
54
demo/Ursa.Demo/ViewModels/ElasticWrapPanelDemoViewModel.cs
Normal file
54
demo/Ursa.Demo/ViewModels/ElasticWrapPanelDemoViewModel.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Layout;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Ursa.Demo.ViewModels;
|
||||
|
||||
public partial class ElasticWrapPanelDemoViewModel : ObservableObject
|
||||
{
|
||||
[ObservableProperty] private Orientation _selectedOrientation = Orientation.Horizontal;
|
||||
[ObservableProperty] private ScrollBarVisibility _horizontalVisibility = ScrollBarVisibility.Auto;
|
||||
[ObservableProperty] private ScrollBarVisibility _verticalVisibility = ScrollBarVisibility.Auto;
|
||||
|
||||
[ObservableProperty] private bool _isFillHorizontal;
|
||||
[ObservableProperty] private bool _isFillVertical;
|
||||
[ObservableProperty] private double _itemWidth = 40d;
|
||||
[ObservableProperty] private double _itemHeight = 40d;
|
||||
|
||||
[ObservableProperty] private bool _autoWidth = true;
|
||||
[ObservableProperty] private bool _autoHeight = true;
|
||||
[ObservableProperty] private double _itemSelfWidth = double.NaN;
|
||||
[ObservableProperty] private double _itemSelfHeight = double.NaN;
|
||||
|
||||
[ObservableProperty] private HorizontalAlignment _cmbHAlign = HorizontalAlignment.Left;
|
||||
[ObservableProperty] private VerticalAlignment _cmbVAlign = VerticalAlignment.Stretch;
|
||||
|
||||
private double _oldItemSelfWidth;
|
||||
private double _oldItemSelfHeight;
|
||||
|
||||
partial void OnAutoWidthChanged(bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
_oldItemSelfWidth = ItemSelfWidth;
|
||||
ItemSelfWidth = double.NaN;
|
||||
}
|
||||
else
|
||||
{
|
||||
ItemSelfWidth = _oldItemSelfWidth;
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnAutoHeightChanged(bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
_oldItemSelfHeight = ItemSelfHeight;
|
||||
ItemSelfHeight = double.NaN;
|
||||
}
|
||||
else
|
||||
{
|
||||
ItemSelfHeight = _oldItemSelfHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ public class MainViewViewModel : ViewModelBase
|
||||
MenuKeys.MenuKeyDisableContainer => new DisableContainerDemoViewModel(),
|
||||
MenuKeys.MenuKeyDrawer => new DrawerDemoViewModel(),
|
||||
MenuKeys.MenuKeyDualBadge => new DualBadgeDemoViewModel(),
|
||||
MenuKeys.MenuKeyElasticWrapPanel => new ElasticWrapPanelDemoViewModel(),
|
||||
MenuKeys.MenuKeyEnumSelector => new EnumSelectorDemoViewModel(),
|
||||
MenuKeys.MenuKeyForm => new FormDemoViewModel(),
|
||||
MenuKeys.MenuKeyImageViewer => new ImageViewerDemoViewModel(),
|
||||
|
||||
@@ -23,6 +23,7 @@ public class MenuViewModel: ViewModelBase
|
||||
new() { MenuHeader = "Divider", Key = MenuKeys.MenuKeyDivider },
|
||||
new() { MenuHeader = "Drawer", Key = MenuKeys.MenuKeyDrawer },
|
||||
new() { MenuHeader = "DualBadge", Key = MenuKeys.MenuKeyDualBadge },
|
||||
new() { MenuHeader = "ElasticWrapPanel", Key = MenuKeys.MenuKeyElasticWrapPanel },
|
||||
new() { MenuHeader = "Enum Selector", Key = MenuKeys.MenuKeyEnumSelector },
|
||||
new() { MenuHeader = "Form", Key = MenuKeys.MenuKeyForm },
|
||||
new() { MenuHeader = "Icon Button", Key = MenuKeys.MenuKeyIconButton },
|
||||
@@ -69,6 +70,7 @@ public static class MenuKeys
|
||||
public const string MenuKeyDisableContainer = "DisableContainer";
|
||||
public const string MenuKeyDrawer = "Drawer";
|
||||
public const string MenuKeyDualBadge = "DualBadge";
|
||||
public const string MenuKeyElasticWrapPanel = "ElasticWrapPanel";
|
||||
public const string MenuKeyEnumSelector = "EnumSelector";
|
||||
public const string MenuKeyForm = "Form";
|
||||
public const string MenuKeyImageViewer = "ImageViewer";
|
||||
|
||||
3
src/Ursa.Themes.Semi/Controls/ElasticWrapPanel.axaml
Normal file
3
src/Ursa.Themes.Semi/Controls/ElasticWrapPanel.axaml
Normal file
@@ -0,0 +1,3 @@
|
||||
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
</ResourceDictionary>
|
||||
@@ -13,6 +13,7 @@
|
||||
<ResourceInclude Source="Divider.axaml" />
|
||||
<ResourceInclude Source="Drawer.axaml" />
|
||||
<ResourceInclude Source="DualBadge.axaml" />
|
||||
<ResourceInclude Source="ElasticWrapPanel.axaml" />
|
||||
<ResourceInclude Source="EnumSelector.axaml" />
|
||||
<ResourceInclude Source="Form.axaml" />
|
||||
<ResourceInclude Source="IconButton.axaml" />
|
||||
|
||||
481
src/Ursa/Controls/ElasticWrapPanel.cs
Normal file
481
src/Ursa/Controls/ElasticWrapPanel.cs
Normal file
@@ -0,0 +1,481 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Utilities;
|
||||
using static System.Math;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
public class ElasticWrapPanel : WrapPanel
|
||||
{
|
||||
static ElasticWrapPanel()
|
||||
{
|
||||
IsFillHorizontalProperty.Changed.AddClassHandler<Control>(OnIsFillPropertyChanged);
|
||||
IsFillVerticalProperty.Changed.AddClassHandler<Control>(OnIsFillPropertyChanged);
|
||||
|
||||
AffectsMeasure<ElasticWrapPanel>(IsFillHorizontalProperty, IsFillVerticalProperty);
|
||||
}
|
||||
|
||||
#region AttachedProperty
|
||||
|
||||
public static void SetFixToRB(Control element, bool value)
|
||||
{
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(FixToRBProperty, value);
|
||||
}
|
||||
|
||||
public static bool GetIsFixToRB(Control element)
|
||||
{
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(FixToRBProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fixed to [Right (Horizontal Mode) | Bottom (Vertical Mode)]
|
||||
/// which will cause line breaks
|
||||
/// </summary>
|
||||
public static readonly AttachedProperty<bool> FixToRBProperty =
|
||||
AvaloniaProperty.RegisterAttached<ElasticWrapPanel, Control, bool>("FixToRB");
|
||||
|
||||
#endregion
|
||||
|
||||
#region StyledProperty
|
||||
|
||||
public bool IsFillHorizontal
|
||||
{
|
||||
get => GetValue(IsFillHorizontalProperty);
|
||||
set => SetValue(IsFillHorizontalProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<bool> IsFillHorizontalProperty =
|
||||
AvaloniaProperty.Register<ElasticWrapPanel, bool>(nameof(IsFillHorizontal));
|
||||
|
||||
public bool IsFillVertical
|
||||
{
|
||||
get => GetValue(IsFillVerticalProperty);
|
||||
set => SetValue(IsFillVerticalProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<bool> IsFillVerticalProperty =
|
||||
AvaloniaProperty.Register<ElasticWrapPanel, bool>(nameof(IsFillVertical));
|
||||
|
||||
private static void OnIsFillPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
(d as ElasticWrapPanel)?.InvalidateMeasure();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected override Size MeasureOverride(Size constraint)
|
||||
{
|
||||
double itemWidth = ItemWidth;
|
||||
double itemHeight = ItemHeight;
|
||||
var orientation = Orientation;
|
||||
var children = Children;
|
||||
|
||||
// Determine the space required for items in the same row/column based on horizontal/vertical arrangement
|
||||
var curLineSize = new UVSize(orientation);
|
||||
|
||||
// Calculate the total space requirement for this ElasticWrapPanel
|
||||
var panelSize = new UVSize(orientation);
|
||||
|
||||
// Measure UVSize with the given space constraint, used for measuring elements when ItemWidth and ItemHeight are not set
|
||||
var uvConstraint = new UVSize(orientation, constraint.Width, constraint.Height);
|
||||
bool itemWidthSet = !double.IsNaN(itemWidth);
|
||||
bool itemHeightSet = !double.IsNaN(itemHeight);
|
||||
|
||||
var childConstraint = new Size(
|
||||
itemWidthSet ? itemWidth : constraint.Width,
|
||||
itemHeightSet ? itemHeight : constraint.Height);
|
||||
|
||||
// Measurement space for elements with FixToRB=True
|
||||
Size childFixConstraint = new Size(constraint.Width, constraint.Height);
|
||||
switch (orientation)
|
||||
{
|
||||
case Orientation.Horizontal when itemHeightSet:
|
||||
childFixConstraint = new Size(constraint.Width, itemHeight);
|
||||
break;
|
||||
case Orientation.Vertical when itemWidthSet:
|
||||
childFixConstraint = new Size(itemWidth, constraint.Height);
|
||||
break;
|
||||
}
|
||||
|
||||
// This is the size for non-space measurement
|
||||
UVSize itemSetSize = new UVSize(orientation,
|
||||
itemWidthSet ? itemWidth : 0,
|
||||
itemHeightSet ? itemHeight : 0);
|
||||
|
||||
foreach (var child in children)
|
||||
{
|
||||
UVSize sz;
|
||||
if (GetIsFixToRB(child))
|
||||
{
|
||||
// Measure the element when it needs to be fixed to the right/bottom
|
||||
child.Measure(childFixConstraint);
|
||||
sz = new UVSize(orientation, child.DesiredSize.Width, child.DesiredSize.Height);
|
||||
|
||||
// Ensure the width/height is within the constraint limits
|
||||
if (sz.U > 0 && itemSetSize.U > 0)
|
||||
{
|
||||
if (sz.U < itemSetSize.U)
|
||||
{
|
||||
sz.U = itemSetSize.U;
|
||||
}
|
||||
else
|
||||
{
|
||||
sz.U = Min(sz.U, uvConstraint.U);
|
||||
}
|
||||
}
|
||||
|
||||
if (sz.V > 0 && itemSetSize.V > 0 && sz.V < itemSetSize.V)
|
||||
{
|
||||
sz.V = itemSetSize.V;
|
||||
}
|
||||
|
||||
if (MathUtilities.GreaterThan(curLineSize.U + sz.U, uvConstraint.U))
|
||||
{
|
||||
panelSize.U = Max(curLineSize.U, panelSize.U);
|
||||
panelSize.V += curLineSize.V;
|
||||
curLineSize = sz;
|
||||
}
|
||||
else
|
||||
{
|
||||
curLineSize.U += sz.U;
|
||||
curLineSize.V = Max(sz.V, curLineSize.V);
|
||||
panelSize.U = Max(curLineSize.U, panelSize.U);
|
||||
panelSize.V += curLineSize.V;
|
||||
}
|
||||
|
||||
curLineSize = new UVSize(orientation);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Flow passes its own constraint to children
|
||||
child.Measure(childConstraint);
|
||||
|
||||
// This is the size of the child in UV space
|
||||
sz = new UVSize(orientation,
|
||||
itemWidthSet ? itemWidth : child.DesiredSize.Width,
|
||||
itemHeightSet ? itemHeight : child.DesiredSize.Height);
|
||||
|
||||
if (MathUtilities.GreaterThan(curLineSize.U + sz.U, uvConstraint.U)) // Need to switch to another line
|
||||
{
|
||||
panelSize.U = Max(curLineSize.U, panelSize.U);
|
||||
panelSize.V += curLineSize.V;
|
||||
curLineSize = sz;
|
||||
|
||||
if (MathUtilities.GreaterThan(sz.U, uvConstraint.U)) // The element is wider than the constraint - give it a separate line
|
||||
{
|
||||
panelSize.U = Max(sz.U, panelSize.U);
|
||||
panelSize.V += sz.V;
|
||||
curLineSize = new UVSize(orientation);
|
||||
}
|
||||
}
|
||||
else // Continue to accumulate a line
|
||||
{
|
||||
curLineSize.U += sz.U;
|
||||
curLineSize.V = Max(sz.V, curLineSize.V);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The last line size, if any should be added
|
||||
panelSize.U = Max(curLineSize.U, panelSize.U);
|
||||
panelSize.V += curLineSize.V;
|
||||
|
||||
// Go from UV space to W/H space
|
||||
return new Size(panelSize.Width, panelSize.Height);
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
bool itemWidthSet = !double.IsNaN(ItemWidth);
|
||||
bool itemHeightSet = !double.IsNaN(ItemHeight);
|
||||
|
||||
// This is the size for non-space measurement
|
||||
UVSize itemSetSize = new UVSize(Orientation,
|
||||
itemWidthSet ? ItemWidth : 0,
|
||||
itemHeightSet ? ItemHeight : 0);
|
||||
|
||||
// Measure UVSize with the given space constraint, used for measuring elements when ItemWidth and ItemHeight are not set
|
||||
UVSize uvFinalSize = new UVSize(Orientation, finalSize.Width, finalSize.Height);
|
||||
|
||||
// Collection of elements in the same direction (row/column)
|
||||
List<UVCollection> lineUVCollection = new List<UVCollection>();
|
||||
|
||||
#region Get the collection of elements in the same direction
|
||||
|
||||
// Current collection of elements in a row/column
|
||||
UVCollection curLineUIs = new UVCollection(Orientation, itemSetSize);
|
||||
|
||||
// Iterate over the child elements
|
||||
var children = Children;
|
||||
foreach (var child in children)
|
||||
{
|
||||
UVSize sz;
|
||||
if (GetIsFixToRB(child))
|
||||
{
|
||||
// Measure the element when it needs to be fixed to the right/bottom
|
||||
sz = new UVSize(Orientation, child.DesiredSize.Width, child.DesiredSize.Height);
|
||||
double lengthCount = 1;
|
||||
if (sz.U > 0 && itemSetSize.U > 0)
|
||||
{
|
||||
if (sz.U < itemSetSize.U)
|
||||
{
|
||||
sz.U = itemSetSize.U;
|
||||
}
|
||||
else
|
||||
{
|
||||
lengthCount = Ceiling(sz.U / itemSetSize.U);
|
||||
sz.U = Min(sz.U, uvFinalSize.U);
|
||||
}
|
||||
}
|
||||
|
||||
if (sz.V > 0 && itemSetSize.V > 0 && sz.V < itemSetSize.V)
|
||||
{
|
||||
sz.V = itemSetSize.V;
|
||||
}
|
||||
|
||||
if (MathUtilities.GreaterThan(curLineUIs.TotalU + sz.U, uvFinalSize.U))
|
||||
{
|
||||
if (curLineUIs.Count > 0)
|
||||
{
|
||||
lineUVCollection.Add(curLineUIs);
|
||||
}
|
||||
|
||||
curLineUIs = new UVCollection(Orientation, itemSetSize);
|
||||
curLineUIs.Add(child, sz, Convert.ToInt32(lengthCount));
|
||||
}
|
||||
else
|
||||
{
|
||||
curLineUIs.Add(child, sz, Convert.ToInt32(lengthCount));
|
||||
}
|
||||
|
||||
lineUVCollection.Add(curLineUIs);
|
||||
curLineUIs = new UVCollection(Orientation, itemSetSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
sz = new UVSize(Orientation,
|
||||
itemWidthSet ? ItemWidth : child.DesiredSize.Width,
|
||||
itemHeightSet ? ItemHeight : child.DesiredSize.Height);
|
||||
|
||||
if (MathUtilities.GreaterThan(curLineUIs.TotalU + sz.U, uvFinalSize.U)) // Need to switch to another line
|
||||
{
|
||||
if (curLineUIs.Count > 0)
|
||||
{
|
||||
lineUVCollection.Add(curLineUIs);
|
||||
}
|
||||
|
||||
curLineUIs = new UVCollection(Orientation, itemSetSize);
|
||||
curLineUIs.Add(child, sz);
|
||||
if (MathUtilities.GreaterThan(sz.U, uvFinalSize.U))
|
||||
{
|
||||
lineUVCollection.Add(curLineUIs);
|
||||
curLineUIs = new UVCollection(Orientation, itemSetSize);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
curLineUIs.Add(child, sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (curLineUIs.Count > 0 && !lineUVCollection.Contains(curLineUIs))
|
||||
{
|
||||
lineUVCollection.Add(curLineUIs);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
bool isFillU = false;
|
||||
bool isFillV = false;
|
||||
switch (Orientation)
|
||||
{
|
||||
case Orientation.Horizontal:
|
||||
isFillU = IsFillHorizontal;
|
||||
isFillV = IsFillVertical;
|
||||
break;
|
||||
|
||||
case Orientation.Vertical:
|
||||
isFillU = IsFillVertical;
|
||||
isFillV = IsFillHorizontal;
|
||||
break;
|
||||
}
|
||||
|
||||
if (lineUVCollection.Count > 0)
|
||||
{
|
||||
double accumulatedV = 0;
|
||||
double adaptULength = 0;
|
||||
bool isAdaptV = false;
|
||||
double adaptVLength = 0;
|
||||
if (isFillU)
|
||||
{
|
||||
if (itemSetSize.U > 0)
|
||||
{
|
||||
int maxElementCount = lineUVCollection
|
||||
.Max(uiSet => uiSet.UICollection
|
||||
.Sum(p => p.Value.ULengthCount));
|
||||
adaptULength = (uvFinalSize.U - maxElementCount * itemSetSize.U) / maxElementCount;
|
||||
adaptULength = Max(adaptULength, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (isFillV)
|
||||
{
|
||||
if (itemSetSize.V > 0)
|
||||
{
|
||||
isAdaptV = true;
|
||||
adaptVLength = uvFinalSize.V / lineUVCollection.Count;
|
||||
}
|
||||
}
|
||||
|
||||
bool isHorizontal = Orientation == Orientation.Horizontal;
|
||||
foreach (var uvCollection in lineUVCollection)
|
||||
{
|
||||
double u = 0;
|
||||
var lineUIEles = uvCollection.UICollection.Keys.ToList();
|
||||
double linevV = isAdaptV ? adaptVLength : uvCollection.LineV;
|
||||
foreach (var child in lineUIEles)
|
||||
{
|
||||
UVLengthSize childSize = uvCollection.UICollection[child];
|
||||
|
||||
double layoutSlotU = childSize.UVSize.U + childSize.ULengthCount * adaptULength;
|
||||
double layoutSlotV = isAdaptV ? linevV : childSize.UVSize.V;
|
||||
if (ElasticWrapPanel.GetIsFixToRB(child) == false)
|
||||
{
|
||||
child.Arrange(new Rect(
|
||||
isHorizontal ? u : accumulatedV,
|
||||
isHorizontal ? accumulatedV : u,
|
||||
isHorizontal ? layoutSlotU : layoutSlotV,
|
||||
isHorizontal ? layoutSlotV : layoutSlotU));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (itemSetSize.U > 0)
|
||||
{
|
||||
layoutSlotU = childSize.ULengthCount * itemSetSize.U +
|
||||
childSize.ULengthCount * adaptULength;
|
||||
double leaveULength = uvFinalSize.U - u;
|
||||
layoutSlotU = Min(leaveULength, layoutSlotU);
|
||||
}
|
||||
|
||||
child.Arrange(new Rect(
|
||||
isHorizontal ? Max(0, uvFinalSize.U - layoutSlotU) : accumulatedV,
|
||||
isHorizontal ? accumulatedV : Max(0, uvFinalSize.U - layoutSlotU),
|
||||
isHorizontal ? layoutSlotU : layoutSlotV,
|
||||
isHorizontal ? layoutSlotV : layoutSlotU));
|
||||
}
|
||||
|
||||
u += layoutSlotU;
|
||||
}
|
||||
|
||||
accumulatedV += linevV;
|
||||
lineUIEles.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
lineUVCollection.ForEach(col => col.Dispose());
|
||||
lineUVCollection.Clear();
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
private struct UVSize
|
||||
{
|
||||
internal UVSize(Orientation orientation, double width, double height)
|
||||
{
|
||||
U = V = 0d;
|
||||
_orientation = orientation;
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
internal UVSize(Orientation orientation)
|
||||
{
|
||||
U = V = 0d;
|
||||
_orientation = orientation;
|
||||
}
|
||||
|
||||
internal double U;
|
||||
internal double V;
|
||||
private Orientation _orientation;
|
||||
|
||||
internal double Width
|
||||
{
|
||||
get { return _orientation == Orientation.Horizontal ? U : V; }
|
||||
set
|
||||
{
|
||||
if (_orientation == Orientation.Horizontal) U = value;
|
||||
else V = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal double Height
|
||||
{
|
||||
get { return _orientation == Orientation.Horizontal ? V : U; }
|
||||
set
|
||||
{
|
||||
if (_orientation == Orientation.Horizontal) V = value;
|
||||
else U = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class UVLengthSize
|
||||
{
|
||||
public UVSize UVSize { get; set; }
|
||||
|
||||
public int ULengthCount { get; set; }
|
||||
|
||||
public UVLengthSize(UVSize uvSize, int uLengthCount)
|
||||
{
|
||||
this.UVSize = uvSize;
|
||||
this.ULengthCount = uLengthCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Elements used to store the same row/column
|
||||
/// </summary>
|
||||
private class UVCollection : IDisposable
|
||||
{
|
||||
public Dictionary<Control, UVLengthSize> UICollection { get; }
|
||||
|
||||
private UVSize LineDesireUVSize;
|
||||
|
||||
private UVSize ItemSetSize;
|
||||
|
||||
public UVCollection(Orientation orientation, UVSize itemSetSize)
|
||||
{
|
||||
UICollection = new Dictionary<Control, UVLengthSize>();
|
||||
LineDesireUVSize = new UVSize(orientation);
|
||||
ItemSetSize = itemSetSize;
|
||||
}
|
||||
|
||||
public double TotalU => LineDesireUVSize.U;
|
||||
|
||||
public double LineV => LineDesireUVSize.V;
|
||||
|
||||
public void Add(Control element, UVSize childSize, int itemULength = 1)
|
||||
{
|
||||
if (UICollection.ContainsKey(element))
|
||||
throw new InvalidOperationException("The element already exists and cannot be added repeatedly.");
|
||||
|
||||
UICollection[element] = new UVLengthSize(childSize, itemULength);
|
||||
LineDesireUVSize.U += childSize.U;
|
||||
LineDesireUVSize.V = Max(LineDesireUVSize.V, childSize.V);
|
||||
}
|
||||
|
||||
public int Count => UICollection.Count;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
UICollection.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user