feat: Add ElementGroup control, styles and demo.
This commit is contained in:
1 parent
700abba273
commit
2ac7dfaf96
6 files changed
+289
No files matched your search
@@ -0,0 +1,45 @@
|
||||
<UserControl x:Class="HandyControlDemo.UserControl.ElementGroupDemo"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
xmlns:langs="clr-namespace:HandyControlDemo.Properties.Langs"
|
||||
Background="{DynamicResource RegionBrush}">
|
||||
<TransitioningContentControl>
|
||||
<hc:UniformSpacingPanel Margin="32" VerticalAlignment="Center" Orientation="Vertical" Spacing="16">
|
||||
<hc:ElementGroup Orientation="Horizontal" Layout="Stack">
|
||||
<Button HorizontalAlignment="Stretch" Width="NaN" Content="{x:Static langs:Lang.Button}"/>
|
||||
<Button HorizontalAlignment="Stretch" Width="NaN" Content="{x:Static langs:Lang.Button}"/>
|
||||
<Button HorizontalAlignment="Stretch" Width="NaN" Content="{x:Static langs:Lang.Button}"/>
|
||||
<Button HorizontalAlignment="Stretch" Width="NaN" Content="{x:Static langs:Lang.Button}"/>
|
||||
</hc:ElementGroup>
|
||||
<hc:ElementGroup Orientation="Horizontal" MaxWidth="500">
|
||||
<ToggleButton HorizontalAlignment="Stretch" Width="NaN" Content="{x:Static langs:Lang.ToggleButton}"/>
|
||||
<Button HorizontalAlignment="Stretch" Width="NaN" Content="{x:Static langs:Lang.Button}"/>
|
||||
<ToggleButton HorizontalAlignment="Stretch" Width="NaN" Content="{x:Static langs:Lang.ToggleButton}"/>
|
||||
<Button HorizontalAlignment="Stretch" Width="NaN" Content="{x:Static langs:Lang.Button}"/>
|
||||
</hc:ElementGroup>
|
||||
<hc:ElementGroup Orientation="Horizontal" Layout="Stack">
|
||||
<Border Theme="{StaticResource BorderRegion}" Padding="6,0">
|
||||
<Path Data="{StaticResource ClockGeometry}" Width="16" Height="16" Stretch="Uniform" Fill="{DynamicResource BorderBrush}"/>
|
||||
</Border>
|
||||
<TextBox MinWidth="200"/>
|
||||
<Button Content="{x:Static langs:Lang.Button}"/>
|
||||
<ToggleButton Content="{x:Static langs:Lang.ToggleButton}"/>
|
||||
</hc:ElementGroup>
|
||||
<hc:ElementGroup>
|
||||
<hc:ElementGroup.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
</Grid>
|
||||
</ItemsPanelTemplate>
|
||||
</hc:ElementGroup.ItemsPanel>
|
||||
<TextBox HorizontalAlignment="Stretch"/>
|
||||
<Button Grid.Column="1" Width="100" Content="{x:Static langs:Lang.Button}" HorizontalAlignment="Right" Theme="{StaticResource ButtonPrimary}"/>
|
||||
</hc:ElementGroup>
|
||||
</hc:UniformSpacingPanel>
|
||||
</TransitioningContentControl>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace HandyControlDemo.UserControl;
|
||||
|
||||
public partial class ElementGroupDemo : Avalonia.Controls.UserControl
|
||||
{
|
||||
public ElementGroupDemo()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Layout;
|
||||
using HandyControl.Data;
|
||||
using HandyControl.Tools;
|
||||
|
||||
namespace HandyControl.Controls;
|
||||
|
||||
public class ElementGroup : ItemsControl
|
||||
{
|
||||
private static readonly Dictionary<Orientation, Dictionary<ChildLocation, CornerRadius>> CornerRadiusDict;
|
||||
|
||||
public static readonly StyledProperty<Orientation> OrientationProperty =
|
||||
StackPanel.OrientationProperty.AddOwner<ElementGroup>(
|
||||
new StyledPropertyMetadata<Orientation>(defaultValue: Orientation.Horizontal));
|
||||
|
||||
public Orientation Orientation
|
||||
{
|
||||
get => GetValue(OrientationProperty);
|
||||
set => SetValue(OrientationProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<LinearLayout> LayoutProperty =
|
||||
AvaloniaProperty.Register<ElementGroup, LinearLayout>(nameof(Layout), defaultValue: LinearLayout.Uniform);
|
||||
|
||||
public LinearLayout Layout
|
||||
{
|
||||
get => GetValue(LayoutProperty);
|
||||
set => SetValue(LayoutProperty, value);
|
||||
}
|
||||
|
||||
static ElementGroup()
|
||||
{
|
||||
AffectsMeasure<ElementGroup>(OrientationProperty, LayoutProperty);
|
||||
|
||||
var defaultCornerRadius = ResourceHelper.GetResource<CornerRadius>("DefaultCornerRadius");
|
||||
|
||||
CornerRadiusDict = new Dictionary<Orientation, Dictionary<ChildLocation, CornerRadius>>
|
||||
{
|
||||
[Orientation.Horizontal] = new()
|
||||
{
|
||||
[ChildLocation.Single] = defaultCornerRadius,
|
||||
[ChildLocation.First] = new CornerRadius(defaultCornerRadius.TopLeft, 0, 0, defaultCornerRadius.BottomLeft),
|
||||
[ChildLocation.Middle] = new CornerRadius(),
|
||||
[ChildLocation.Last] = new CornerRadius(0, defaultCornerRadius.TopRight, defaultCornerRadius.BottomRight, 0)
|
||||
},
|
||||
[Orientation.Vertical] = new()
|
||||
{
|
||||
[ChildLocation.Single] = defaultCornerRadius,
|
||||
[ChildLocation.First] = new CornerRadius(defaultCornerRadius.TopLeft, defaultCornerRadius.TopRight, 0, 0),
|
||||
[ChildLocation.Middle] = new CornerRadius(),
|
||||
[ChildLocation.Last] = new CornerRadius(0, 0, defaultCornerRadius.BottomRight, defaultCornerRadius.BottomLeft)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void PrepareContainerForItemOverride(Control container, object? item, int index)
|
||||
{
|
||||
base.PrepareContainerForItemOverride(container, item, index);
|
||||
|
||||
container.GotFocus += ElementAdded_GotFocus;
|
||||
container.LostFocus += ElementAdded_LostFocus;
|
||||
container.PropertyChanged += Container_PropertyChanged;
|
||||
|
||||
UpdateChildrenCornerRadius();
|
||||
}
|
||||
|
||||
protected override void ClearContainerForItemOverride(Control container)
|
||||
{
|
||||
base.ClearContainerForItemOverride(container);
|
||||
|
||||
container.GotFocus -= ElementAdded_GotFocus;
|
||||
container.LostFocus -= ElementAdded_LostFocus;
|
||||
container.PropertyChanged -= Container_PropertyChanged;
|
||||
|
||||
UpdateChildrenCornerRadius();
|
||||
}
|
||||
|
||||
private void Container_PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == IsVisibleProperty)
|
||||
{
|
||||
UpdateChildrenCornerRadius();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateChildrenCornerRadius()
|
||||
{
|
||||
var visibleChildren = GetVisibleChildren();
|
||||
var count = visibleChildren.Count;
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var orientation = Orientation;
|
||||
|
||||
if (count == 1)
|
||||
{
|
||||
UpdateChildCornerRadius(visibleChildren[0], CornerRadiusDict[orientation][ChildLocation.Single]);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateChildCornerRadius(visibleChildren[0], CornerRadiusDict[orientation][ChildLocation.First]);
|
||||
|
||||
var childMargin = orientation == Orientation.Horizontal
|
||||
? new Thickness(-1, 0, 0, 0)
|
||||
: new Thickness(0, -1, 0, 0);
|
||||
|
||||
for (var childIndex = 1; childIndex < count; childIndex++)
|
||||
{
|
||||
var child = visibleChildren[childIndex];
|
||||
child.SetCurrentValue(MarginProperty, childMargin);
|
||||
UpdateChildCornerRadius(child, CornerRadiusDict[orientation][ChildLocation.Middle]);
|
||||
}
|
||||
|
||||
var lastChild = visibleChildren[count - 1];
|
||||
lastChild.SetCurrentValue(MarginProperty, childMargin);
|
||||
UpdateChildCornerRadius(lastChild, CornerRadiusDict[orientation][ChildLocation.Last]);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Control> GetVisibleChildren()
|
||||
{
|
||||
return Items.OfType<Control>().Where(element => element.IsVisible).ToList();
|
||||
}
|
||||
|
||||
private static void UpdateChildCornerRadius(AvaloniaObject child, CornerRadius cornerRadius)
|
||||
{
|
||||
if (child is Border border)
|
||||
{
|
||||
border.SetCurrentValue(Border.CornerRadiusProperty, cornerRadius);
|
||||
}
|
||||
else
|
||||
{
|
||||
BorderElement.SetCornerRadius(child, cornerRadius);
|
||||
}
|
||||
}
|
||||
|
||||
private void ElementAdded_LostFocus(object? sender, RoutedEventArgs e) => ResetElementZIndex(e.Source);
|
||||
|
||||
private void ElementAdded_GotFocus(object? sender, RoutedEventArgs e) => MaximizeElementZIndex(e.Source);
|
||||
|
||||
private static void ResetElementZIndex(object? element)
|
||||
{
|
||||
if (element is Control control)
|
||||
{
|
||||
control.ZIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static void MaximizeElementZIndex(object? element)
|
||||
{
|
||||
if (element is Control control)
|
||||
{
|
||||
control.ZIndex = int.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
private enum ChildLocation
|
||||
{
|
||||
Single,
|
||||
First,
|
||||
Middle,
|
||||
Last
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace HandyControl.Data;
|
||||
|
||||
public enum LinearLayout
|
||||
{
|
||||
Stack,
|
||||
|
||||
Uniform
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:ClassModifier="internal"
|
||||
xmlns:hc="clr-namespace:HandyControl.Controls">
|
||||
|
||||
<ItemsPanelTemplate x:Key="UniformHorizontalItemsPanelTemplate">
|
||||
<UniformGrid Rows="1" />
|
||||
</ItemsPanelTemplate>
|
||||
|
||||
<ItemsPanelTemplate x:Key="UniformVerticalItemsPanelTemplate">
|
||||
<UniformGrid Columns="1" />
|
||||
</ItemsPanelTemplate>
|
||||
|
||||
<ItemsPanelTemplate x:Key="StackHorizontalItemsPanelTemplate">
|
||||
<StackPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
|
||||
<ItemsPanelTemplate x:Key="StackVerticalItemsPanelTemplate">
|
||||
<StackPanel Orientation="Vertical" />
|
||||
</ItemsPanelTemplate>
|
||||
|
||||
<ControlTheme x:Key="ElementGroupBaseStyle"
|
||||
TargetType="hc:ElementGroup">
|
||||
<Setter Property="VerticalAlignment" Value="Top" />
|
||||
<Setter Property="Layout" Value="Uniform" />
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="ItemsPanel" Value="{StaticResource UniformHorizontalItemsPanelTemplate}" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="hc:ElementGroup">
|
||||
<Border Padding="{TemplateBinding Padding}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<ItemsPresenter Name="PART_ItemsPresenter"
|
||||
ItemsPanel="{TemplateBinding ItemsPanel}" />
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
|
||||
<Style Selector="^[Layout=Uniform][Orientation=Vertical]">
|
||||
<Setter Property="ItemsPanel" Value="{StaticResource UniformVerticalItemsPanelTemplate}" />
|
||||
</Style>
|
||||
<Style Selector="^[Layout=Stack][Orientation=Horizontal]">
|
||||
<Setter Property="ItemsPanel" Value="{StaticResource StackHorizontalItemsPanelTemplate}" />
|
||||
</Style>
|
||||
<Style Selector="^[Layout=Stack][Orientation=Vertical]">
|
||||
<Setter Property="ItemsPanel" Value="{StaticResource StackVerticalItemsPanelTemplate}" />
|
||||
</Style>
|
||||
</ControlTheme>
|
||||
|
||||
<ControlTheme x:Key="{x:Type hc:ElementGroup}"
|
||||
BasedOn="{StaticResource ElementGroupBaseStyle}"
|
||||
TargetType="hc:ElementGroup" />
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -58,6 +58,7 @@
|
||||
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/SelectableTextBlock.axaml" />
|
||||
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/HeaderedContentControl.axaml" />
|
||||
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/Card.axaml" />
|
||||
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/ElementGroup.axaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Styles.Resources>
|
||||
|
||||
Reference in new issue
Block a user