feat: add demo. update overrides with internal implementations.

This commit is contained in:
rabbitism
2024-04-11 23:00:38 +08:00
parent 92f8f1428a
commit 40c1d96f2e
10 changed files with 328 additions and 1 deletions

View File

@@ -0,0 +1,41 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="https://irihi.tech/ursa"
xmlns:iri="https://irihi.tech/shared">
<!-- Add Resources Here -->
<ControlTheme x:Key="{x:Type u:TreeComboBox}" TargetType="u:TreeComboBox">
<Setter Property="Template">
<ControlTemplate TargetType="u:TreeComboBox">
<Grid ColumnDefinitions="*, Auto, 32">
<Border Grid.Column="0" Grid.ColumnSpan="3" Background="Red"></Border>
<ContentPresenter Grid.Column="0" MinHeight="32" Content="{TemplateBinding SelectionBoxItem}"/>
<Popup Grid.Column="0" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}">
<Border Background="{DynamicResource ComboBoxPopupBackground}">
<ScrollViewer>
<ItemsPresenter ItemsPanel="{TemplateBinding ItemsPanel}"/>
</ScrollViewer>
</Border>
</Popup>
</Grid>
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="{x:Type u:TreeComboBoxItem}" TargetType="u:TreeComboBoxItem">
<Setter Property="Template">
<ControlTemplate TargetType="u:TreeComboBoxItem">
<StackPanel>
<Border Name="PART_LayoutRoot" MinHeight="{TemplateBinding MinHeight}" TemplatedControl.IsTemplateFocusTarget="True">
<Grid Name="{x:Static iri:PartNames.PART_Header}" Margin="10" ColumnDefinitions="Auto, *">
<ToggleButton Name="PART_ExpandCollapseChevron" Grid.Column="0" Focusable="False"></ToggleButton>
<ContentPresenter Grid.Column="1" Name="{x:Static iri:PartNames.PART_HeaderPresenter}" Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}" Focusable="False"/>
</Grid>
</Border>
<ItemsPresenter Name="{x:Static iri:PartNames.PART_ItemsPresenter}" ItemsPanel="{TemplateBinding ItemsPanel}"/>
</StackPanel>
</ControlTemplate>
</Setter>
</ControlTheme>
</ResourceDictionary>

View File

@@ -32,6 +32,7 @@
<ResourceInclude Source="TagInput.axaml" />
<ResourceInclude Source="ThemeSelector.axaml" />
<ResourceInclude Source="Timeline.axaml" />
<ResourceInclude Source="TreeComboBox.axaml"/>
<ResourceInclude Source="Skeleton.axaml" />
<ResourceInclude Source="TwoTonePathIcon.axaml" />
<ResourceInclude Source="ToolBar.axaml" />

View File

@@ -0,0 +1,128 @@
using System.Data;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.OpenGL.Controls;
using Irihi.Avalonia.Shared.Common;
namespace Ursa.Controls;
[TemplatePart(PartNames.PART_Popup, typeof(Popup))]
public class TreeComboBox: SelectingItemsControl
{
private static readonly FuncTemplate<Panel?> DefaultPanel =
new FuncTemplate<Panel?>(() => new VirtualizingStackPanel());
public static readonly StyledProperty<double> MaxDropDownHeightProperty =
ComboBox.MaxDropDownHeightProperty.AddOwner<TreeComboBox>();
public double MaxDropDownHeight
{
get => GetValue(MaxDropDownHeightProperty);
set => SetValue(MaxDropDownHeightProperty, value);
}
public static readonly StyledProperty<string?> WatermarkProperty =
TextBox.WatermarkProperty.AddOwner<TreeComboBox>();
public string? Watermark
{
get => GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
}
public static readonly StyledProperty<bool> IsDropDownOpenProperty =
ComboBox.IsDropDownOpenProperty.AddOwner<TreeComboBox>();
public bool IsDropDownOpen
{
get => GetValue(IsDropDownOpenProperty);
set => SetValue(IsDropDownOpenProperty, value);
}
public static readonly StyledProperty<HorizontalAlignment> HorizontalContentAlignmentProperty =
ContentControl.HorizontalContentAlignmentProperty.AddOwner<TreeComboBox>();
public HorizontalAlignment HorizontalContentAlignment
{
get => GetValue(HorizontalContentAlignmentProperty);
set => SetValue(HorizontalContentAlignmentProperty, value);
}
public static readonly StyledProperty<VerticalAlignment> VerticalContentAlignmentProperty =
ContentControl.VerticalContentAlignmentProperty.AddOwner<TreeComboBox>();
public VerticalAlignment VerticalContentAlignment
{
get => GetValue(VerticalContentAlignmentProperty);
set => SetValue(VerticalContentAlignmentProperty, value);
}
public static readonly StyledProperty<IDataTemplate?> SelectedItemTemplateProperty =
AvaloniaProperty.Register<TreeComboBox, IDataTemplate?>(nameof(SelectedItemTemplate));
public IDataTemplate? SelectedItemTemplate
{
get => GetValue(SelectedItemTemplateProperty);
set => SetValue(SelectedItemTemplateProperty, value);
}
public static readonly DirectProperty<TreeComboBox, object?> SelectionBoxItemProperty = AvaloniaProperty.RegisterDirect<TreeComboBox, object?>(
nameof(SelectionBoxItem), o => o.SelectionBoxItem);
private object? _selectionBoxItem;
public object? SelectionBoxItem
{
get => _selectionBoxItem;
protected set => SetAndRaise(SelectionBoxItemProperty, ref _selectionBoxItem, value);
}
static TreeComboBox()
{
ItemsPanelProperty.OverrideDefaultValue<TreeComboBox>(DefaultPanel);
FocusableProperty.OverrideDefaultValue<TreeComboBox>(true);
}
protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
{
return NeedsContainer<TreeComboBoxItem>(item, out recycleKey);
}
internal bool NeedsContainerInternal(object? item, int index, out object? recycleKey)
{
return NeedsContainerOverride(item, index, out recycleKey);
}
protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
{
return new TreeComboBoxItem();
}
internal Control CreateContainerForItemInternal(object? item, int index, object? recycleKey)
{
return CreateContainerForItemOverride(item, index, recycleKey);
}
protected override void ContainerForItemPreparedOverride(Control container, object? item, int index)
{
base.ContainerForItemPreparedOverride(container, item, index);
}
internal void ContainerForItemPreparedInternal(Control container, object? item, int index)
{
ContainerForItemPreparedOverride(container, item, index);
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);
if (e.InitialPressMouseButton == MouseButton.Left)
{
IsDropDownOpen = !IsDropDownOpen;
}
}
}

View File

@@ -0,0 +1,110 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using Irihi.Avalonia.Shared.Common;
using Irihi.Avalonia.Shared.Helpers;
namespace Ursa.Controls;
[TemplatePart(PartNames.PART_Header, typeof(Control))]
public class TreeComboBoxItem: HeaderedItemsControl, ISelectable
{
private Control? _header;
private TreeComboBox? _treeComboBox;
public static readonly StyledProperty<bool> IsSelectedProperty = TreeViewItem.IsSelectedProperty.AddOwner<TreeComboBoxItem>();
public bool IsSelected
{
get => GetValue(IsSelectedProperty);
set => SetValue(IsSelectedProperty, value);
}
public static readonly StyledProperty<bool> IsExpandedProperty = TreeViewItem.IsExpandedProperty.AddOwner<TreeComboBoxItem>();
public bool IsExpanded
{
get => GetValue(IsExpandedProperty);
set => SetValue(IsExpandedProperty, value);
}
public static readonly DirectProperty<TreeComboBoxItem, int> LevelProperty = AvaloniaProperty.RegisterDirect<TreeComboBoxItem, int>(
nameof(Level), o => o.Level, (o, v) => o.Level = v);
private int _level;
public int Level
{
get => _level;
protected set => SetAndRaise(LevelProperty, ref _level, value);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
DoubleTappedEvent.RemoveHandler(OnDoubleTapped, _header);
_header = e.NameScope.Find<Control>(PartNames.PART_Header);
DoubleTappedEvent.AddHandler(OnDoubleTapped, _header);
}
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
_treeComboBox = this.FindLogicalAncestorOfType<TreeComboBox>();
Level = CalculateDistanceFromLogicalParent<TreeComboBox>(this);
if (this.ItemTemplate is null && this._treeComboBox?.ItemTemplate is not null)
{
SetCurrentValue(ItemTemplateProperty, this._treeComboBox.ItemTemplate);
}
}
private void OnDoubleTapped(object sender, TappedEventArgs e)
{
if (this.ItemCount <= 0) return;
this.SetCurrentValue(IsExpandedProperty, !IsExpanded);
e.Handled = true;
}
protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
{
return EnsureParent().NeedsContainerInternal(item, index, out recycleKey);
}
protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
{
return EnsureParent().CreateContainerForItemInternal(item, index, recycleKey);
}
protected override void ContainerForItemPreparedOverride(Control container, object? item, int index)
{
EnsureParent().ContainerForItemPreparedInternal(container, item, index);
}
// TODO replace with helper method from shared library.
private static int CalculateDistanceFromLogicalParent<T>(ILogical? logical, int @default = -1) where T: ILogical
{
int distance = 0;
ILogical? parent = logical;
while (parent is not null)
{
if (parent is T) return distance;
parent = parent.LogicalParent;
distance++;
}
return @default;
}
private TreeComboBox EnsureParent()
{
return this._treeComboBox ??
throw new InvalidOperationException("TreeComboBoxItem must be a part of TreeComboBox");
}
}

View File

@@ -17,7 +17,7 @@
<ItemGroup>
<PackageReference Include="Avalonia" Version="$(AvaloniaVersion)"/>
<PackageReference Include="Irihi.Avalonia.Shared" Version="0.1.5"/>
<PackageReference Include="Irihi.Avalonia.Shared" Version="0.1.6" />
</ItemGroup>
<ItemGroup>