feat: layout calculation.
This commit is contained in:
@@ -28,7 +28,6 @@ public class ToolBarItemTemplateSelector: IDataTemplate
|
|||||||
{
|
{
|
||||||
[!ContentControl.ContentProperty] = new Binding() { Path = "Content" },
|
[!ContentControl.ContentProperty] = new Binding() { Path = "Content" },
|
||||||
[!ToggleButton.IsCheckedProperty] = new Binding() { Path = "IsChecked" },
|
[!ToggleButton.IsCheckedProperty] = new Binding() { Path = "IsChecked" },
|
||||||
[!Button.CommandProperty] = new Binding() { Path = "Command" },
|
|
||||||
[!ToolBar.OverflowModeProperty] = new Binding(){ Path = "OverflowMode" }
|
[!ToolBar.OverflowModeProperty] = new Binding(){ Path = "OverflowMode" }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -39,7 +38,7 @@ public class ToolBarItemTemplateSelector: IDataTemplate
|
|||||||
[!ContentControl.ContentProperty] = new Binding() { Path = "Content" },
|
[!ContentControl.ContentProperty] = new Binding() { Path = "Content" },
|
||||||
[!SelectingItemsControl.SelectedItemProperty] = new Binding() { Path = "SelectedItem" },
|
[!SelectingItemsControl.SelectedItemProperty] = new Binding() { Path = "SelectedItem" },
|
||||||
[!ItemsControl.ItemsSourceProperty] = new Binding() { Path = "Items" },
|
[!ItemsControl.ItemsSourceProperty] = new Binding() { Path = "Items" },
|
||||||
[!ToolBar.OverflowModeProperty] = new Binding(){ Path = "OverflowMode" }
|
[ToolBar.OverflowModeProperty] = OverflowMode.Always,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return new Button() { Content = "Undefined Item" };
|
return new Button() { Content = "Undefined Item" };
|
||||||
|
|||||||
@@ -10,15 +10,16 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Ursa.Demo.Pages.ToolBarDemo">
|
x:Class="Ursa.Demo.Pages.ToolBarDemo">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<u:ToolBar>
|
<u:ToolBar HorizontalAlignment="Left">
|
||||||
<Button Content="Button 1" />
|
<Button Content="Button 1" />
|
||||||
<Button Content="Button 2" />
|
<Button Content="Button 2" />
|
||||||
<Button Content="Button 3" />
|
<Button Content="Button 3" u:ToolBar.OverflowMode="Always" />
|
||||||
</u:ToolBar>
|
</u:ToolBar>
|
||||||
<u:ToolBar ItemsSource="{Binding Items}">
|
<u:ToolBar ItemsSource="{Binding Items}" HorizontalAlignment="Left">
|
||||||
<u:ToolBar.ItemTemplate>
|
<u:ToolBar.ItemTemplate>
|
||||||
<template:ToolBarItemTemplateSelector/>
|
<template:ToolBarItemTemplateSelector/>
|
||||||
</u:ToolBar.ItemTemplate>
|
</u:ToolBar.ItemTemplate>
|
||||||
</u:ToolBar>
|
</u:ToolBar>
|
||||||
|
<CheckBox Content="Check"></CheckBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
|
||||||
namespace Ursa.Demo.Views;
|
namespace Ursa.Demo.Views;
|
||||||
|
|
||||||
|
|||||||
@@ -6,30 +6,43 @@
|
|||||||
<ControlTheme x:Key="{x:Type u:ToolBar}" TargetType="u:ToolBar">
|
<ControlTheme x:Key="{x:Type u:ToolBar}" TargetType="u:ToolBar">
|
||||||
<Setter Property="ItemsPanel">
|
<Setter Property="ItemsPanel">
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
<u:ToolBarPanel/>
|
<u:ToolBarPanel />
|
||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
</Setter>
|
</Setter>
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<ControlTemplate TargetType="u:ToolBar">
|
<ControlTemplate TargetType="u:ToolBar">
|
||||||
<Border
|
<Border
|
||||||
Padding="2"
|
Padding="2"
|
||||||
Theme="{DynamicResource CardBorder}"
|
CornerRadius="4"
|
||||||
CornerRadius="4">
|
Theme="{DynamicResource CardBorder}">
|
||||||
<StackPanel Orientation="{TemplateBinding Orientation}">
|
<DockPanel LastChildFill="True">
|
||||||
<Rectangle
|
<Rectangle
|
||||||
Width="1"
|
Width="1"
|
||||||
|
Margin="4,0"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
Margin="4 0"
|
DockPanel.Dock="Left"
|
||||||
Fill="Gray" />
|
Fill="Gray" />
|
||||||
<ContentPresenter Margin="4 0" Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}" />
|
<ContentPresenter
|
||||||
<ItemsPresenter VerticalAlignment="Center" HorizontalAlignment="Left" ItemsPanel="{TemplateBinding ItemsPanel}"/>
|
Margin="4,0"
|
||||||
<ToggleButton Name="button" Content="More"></ToggleButton>
|
Content="{TemplateBinding Header}"
|
||||||
<Popup IsOpen="{Binding #button.IsChecked, Mode=TwoWay}" PlacementTarget="{Binding #button}" IsLightDismissEnabled="True">
|
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||||
<Border Theme="{DynamicResource CardBorder}">
|
DockPanel.Dock="Left" />
|
||||||
<StackPanel Name="{x:Static u:ToolBar.PART_OverflowPanel}"></StackPanel>
|
<Panel DockPanel.Dock="Right">
|
||||||
</Border>
|
<ToggleButton Name="button" Content="More" />
|
||||||
</Popup>
|
<Popup
|
||||||
</StackPanel>
|
IsLightDismissEnabled="True"
|
||||||
|
IsOpen="{Binding #button.IsChecked, Mode=TwoWay}"
|
||||||
|
PlacementTarget="{Binding #button}">
|
||||||
|
<Border Theme="{DynamicResource CardBorder}">
|
||||||
|
<StackPanel Name="{x:Static u:ToolBar.PART_OverflowPanel}" />
|
||||||
|
</Border>
|
||||||
|
</Popup>
|
||||||
|
</Panel>
|
||||||
|
<ItemsPresenter
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
ItemsPanel="{TemplateBinding ItemsPanel}" />
|
||||||
|
</DockPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter>
|
</Setter>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Avalonia.Controls.Presenters;
|
|||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Controls.Templates;
|
using Avalonia.Controls.Templates;
|
||||||
using Avalonia.Layout;
|
using Avalonia.Layout;
|
||||||
|
using Avalonia.Markup.Xaml.Templates;
|
||||||
|
|
||||||
namespace Ursa.Controls;
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
@@ -59,6 +60,27 @@ public class ToolBar: HeaderedItemsControl
|
|||||||
return new ContentPresenter();
|
return new ContentPresenter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void ContainerForItemPreparedOverride(Control container, object? item, int index)
|
||||||
|
{
|
||||||
|
base.ContainerForItemPreparedOverride(container, item, index);
|
||||||
|
if (item is Control s)
|
||||||
|
{
|
||||||
|
container[!ToolBar.OverflowModeProperty] = s[!ToolBar.OverflowModeProperty];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (container is ContentPresenter p)
|
||||||
|
{
|
||||||
|
p.ApplyTemplate();
|
||||||
|
var c = p.Child;
|
||||||
|
if (c != null)
|
||||||
|
{
|
||||||
|
container[!ToolBar.OverflowModeProperty] = c[!ToolBar.OverflowModeProperty];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnApplyTemplate(e);
|
base.OnApplyTemplate(e);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Layout;
|
using Avalonia.Layout;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
|
using Avalonia.VisualTree;
|
||||||
|
|
||||||
namespace Ursa.Controls;
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
@@ -13,6 +14,15 @@ public class ToolBarPanel: StackPanel
|
|||||||
internal Panel? OverflowPanel => _overflowPanel ??= _parent?.OverflowPanel;
|
internal Panel? OverflowPanel => _overflowPanel ??= _parent?.OverflowPanel;
|
||||||
internal ToolBar? ParentToolBar => _parent ??= this.TemplatedParent as ToolBar;
|
internal ToolBar? ParentToolBar => _parent ??= this.TemplatedParent as ToolBar;
|
||||||
|
|
||||||
|
public static readonly StyledProperty<Orientation> OrientationProperty =
|
||||||
|
StackPanel.OrientationProperty.AddOwner<ToolBar>();
|
||||||
|
|
||||||
|
public Orientation Orientation
|
||||||
|
{
|
||||||
|
get => GetValue(OrientationProperty);
|
||||||
|
set => SetValue(OrientationProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
static ToolBarPanel()
|
static ToolBarPanel()
|
||||||
{
|
{
|
||||||
OrientationProperty.OverrideDefaultValue<ToolBarPanel>(Orientation.Horizontal);
|
OrientationProperty.OverrideDefaultValue<ToolBarPanel>(Orientation.Horizontal);
|
||||||
@@ -28,24 +38,110 @@ public class ToolBarPanel: StackPanel
|
|||||||
|
|
||||||
protected override Size MeasureOverride(Size availableSize)
|
protected override Size MeasureOverride(Size availableSize)
|
||||||
{
|
{
|
||||||
var size = base.MeasureOverride(availableSize);
|
var logicalChildren = _parent?.GetLogicalChildren().OfType<Control>().ToList();
|
||||||
var children = this.Children;
|
var parent = this.GetLogicalParent();
|
||||||
var children2 = this.OverflowPanel?.Children;
|
Size size = new Size();
|
||||||
var all = children.ToList();
|
double spacing = 0;
|
||||||
if (children2 != null)
|
Size measureSize = availableSize;
|
||||||
|
bool horizontal = Orientation == Orientation.Horizontal;
|
||||||
|
bool hasVisibleChildren = false;
|
||||||
|
measureSize = horizontal
|
||||||
|
? measureSize.WithWidth(double.PositiveInfinity)
|
||||||
|
: measureSize.WithHeight(double.PositiveInfinity);
|
||||||
|
int index = 0;
|
||||||
|
if (logicalChildren is null) return size;
|
||||||
|
for (int count = logicalChildren.Count; index < count; ++index)
|
||||||
{
|
{
|
||||||
all.AddRange(children2);
|
Control control = logicalChildren[index];
|
||||||
|
var mode = ToolBar.GetOverflowMode(control);
|
||||||
|
if(mode == OverflowMode.Always) continue;
|
||||||
|
bool isVisible = control.IsVisible;
|
||||||
|
if (isVisible)
|
||||||
|
{
|
||||||
|
hasVisibleChildren = true;
|
||||||
|
}
|
||||||
|
control.Measure(measureSize);
|
||||||
|
Size desiredSize = control.DesiredSize;
|
||||||
|
if (horizontal)
|
||||||
|
{
|
||||||
|
size = size.WithWidth(size.Width + desiredSize.Width + (isVisible ? spacing : 0));
|
||||||
|
size = size.WithHeight(Math.Max(size.Height, desiredSize.Height));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size = size.WithHeight(size.Height + desiredSize.Height + (isVisible ? spacing : 0));
|
||||||
|
size = size.WithWidth(Math.Max(size.Width, desiredSize.Width));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.Children.Clear();
|
|
||||||
OverflowPanel?.Children.Clear();
|
if (hasVisibleChildren)
|
||||||
for (int i = 0; i < all.Count - 1; i++)
|
|
||||||
{
|
{
|
||||||
this.Children.Add(all[i]);
|
size = horizontal ? size.WithWidth(size.Width - spacing) : size.WithHeight(size.Height - spacing);
|
||||||
}
|
|
||||||
if (all.Count > 0)
|
|
||||||
{
|
|
||||||
OverflowPanel?.Children.Add(all.Last());
|
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override Size ArrangeOverride(Size finalSize)
|
||||||
|
{
|
||||||
|
if (_parent is null)
|
||||||
|
{
|
||||||
|
return finalSize;
|
||||||
|
}
|
||||||
|
var all = _parent.GetLogicalChildren().OfType<Control>().ToList();
|
||||||
|
Children.Clear();
|
||||||
|
OverflowPanel?.Children.Clear();
|
||||||
|
Size currentSize = new Size();
|
||||||
|
bool horizontal = Orientation == Orientation.Horizontal;
|
||||||
|
double spacing = 0;
|
||||||
|
Rect arrangeRect = new Rect(finalSize);
|
||||||
|
double previousChildSize = 0.0;
|
||||||
|
for (var i = 0; i < all.Count; i++)
|
||||||
|
{
|
||||||
|
var control = all[i];
|
||||||
|
if (!control.IsVisible) continue;
|
||||||
|
var desiredSize = control.DesiredSize;
|
||||||
|
var mode = ToolBar.GetOverflowMode(control);
|
||||||
|
if (mode == OverflowMode.Always)
|
||||||
|
{
|
||||||
|
OverflowPanel?.Children.Add(control);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Children.Add(control);
|
||||||
|
if (horizontal)
|
||||||
|
{
|
||||||
|
arrangeRect = arrangeRect.WithX(arrangeRect.X + previousChildSize);
|
||||||
|
previousChildSize = control.DesiredSize.Width;
|
||||||
|
arrangeRect = arrangeRect.WithWidth(previousChildSize);
|
||||||
|
arrangeRect = arrangeRect.WithHeight(Math.Max(finalSize.Height, control.DesiredSize.Height));
|
||||||
|
previousChildSize += spacing;
|
||||||
|
currentSize = currentSize.WithWidth(currentSize.Width + desiredSize.Width + spacing);
|
||||||
|
currentSize = currentSize.WithHeight(Math.Max(currentSize.Height, desiredSize.Height));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
arrangeRect = arrangeRect.WithY(arrangeRect.Y + previousChildSize);
|
||||||
|
previousChildSize = control.DesiredSize.Height;
|
||||||
|
arrangeRect = arrangeRect.WithHeight(previousChildSize);
|
||||||
|
arrangeRect = arrangeRect.WithWidth(Math.Max(finalSize.Width, control.DesiredSize.Width));
|
||||||
|
previousChildSize += spacing;
|
||||||
|
currentSize = currentSize.WithHeight(currentSize.Height + desiredSize.Height + spacing);
|
||||||
|
currentSize = currentSize.WithWidth(Math.Max(currentSize.Width, desiredSize.Width));
|
||||||
|
}
|
||||||
|
control.Arrange(arrangeRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
var list = OverflowPanel?.Children?.ToList();
|
||||||
|
if (list is not null)
|
||||||
|
{
|
||||||
|
OverflowPanel?.Children?.Clear();
|
||||||
|
this.Children.AddRange(list);
|
||||||
|
}
|
||||||
|
base.OnDetachedFromVisualTree(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user