wip
This commit is contained in:
@@ -1,28 +1,7 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Metadata;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class NamedTypeConverter :
|
||||
MarkupExtension,
|
||||
IValueConverter
|
||||
{
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
var d = value is not null ? value.GetType().Name : (object?)null;
|
||||
return d;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override object ProvideValue(IServiceProvider serviceProvider) => this;
|
||||
}
|
||||
public class ConditionalExpression :
|
||||
AvaloniaObject,
|
||||
ICondition
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Toolkit.UI.Avalonia;
|
||||
|
||||
public class NamedTypeConverter :
|
||||
MarkupExtension,
|
||||
IValueConverter
|
||||
{
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
var d = value is not null ? value.GetType().Name : (object?)null;
|
||||
return d;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override object ProvideValue(IServiceProvider serviceProvider) => this;
|
||||
}
|
||||
+4
-4
@@ -2,7 +2,7 @@
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Toolkit.UI.Controls.Avalonia">
|
||||
<ControlTheme x:Key="{x:Type controls:OverflowListBox}" TargetType="controls:OverflowListBox">
|
||||
<ControlTheme x:Key="{x:Type controls:Overflow}" TargetType="controls:Overflow">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Grid Margin="{TemplateBinding Margin}" ColumnDefinitions="*,Auto">
|
||||
@@ -11,14 +11,14 @@
|
||||
Grid.Column="0"
|
||||
ItemTemplate="{TemplateBinding ItemTemplate}"
|
||||
ItemsPanel="{TemplateBinding ItemsPanel}"
|
||||
SelectedItem="{TemplateBinding SelectedItem}" />
|
||||
<Button Grid.Column="1">
|
||||
SelectedItem="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.PrimarySelection, Mode=TwoWay}" />
|
||||
<Button Grid.Column="1" Focusable="False">
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<ListBox
|
||||
x:Name="SecondaryListBox"
|
||||
ItemTemplate="{TemplateBinding ItemTemplate}"
|
||||
SelectedItem="{TemplateBinding SelectedItem}" />
|
||||
SelectedItem="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.SecondarySelection, Mode=TwoWay}" />
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
+90
-15
@@ -2,36 +2,66 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Metadata;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.Core;
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace Toolkit.UI.Controls.Avalonia;
|
||||
|
||||
public class OverflowListBox :
|
||||
public class Overflow :
|
||||
TemplatedControl
|
||||
{
|
||||
public static readonly StyledProperty<ITemplate<Panel?>> ItemsPanelProperty =
|
||||
AvaloniaProperty.Register<OverflowListBox, ITemplate<Panel?>>(nameof(ItemsPanel), new FuncTemplate<Panel?>(() => new StackPanel()));
|
||||
AvaloniaProperty.Register<Overflow, ITemplate<Panel?>>(nameof(ItemsPanel), new FuncTemplate<Panel?>(() => new StackPanel()));
|
||||
|
||||
public static readonly StyledProperty<IEnumerable?> ItemsSourceProperty =
|
||||
AvaloniaProperty.Register<OverflowListBox, IEnumerable?>(nameof(ItemsSource));
|
||||
AvaloniaProperty.Register<Overflow, IEnumerable?>(nameof(ItemsSource));
|
||||
|
||||
public static readonly StyledProperty<IDataTemplate?> ItemTemplateProperty =
|
||||
AvaloniaProperty.Register<OverflowListBox, IDataTemplate?>(nameof(ItemTemplate));
|
||||
AvaloniaProperty.Register<Overflow, IDataTemplate?>(nameof(ItemTemplate));
|
||||
|
||||
public static readonly StyledProperty<object?> SelectedItemProperty =
|
||||
AvaloniaProperty.Register<OverflowListBox, object?>(nameof(SelectedItem), BindingMode.TwoWay);
|
||||
AvaloniaProperty.Register<Overflow, object?>(nameof(SelectedItem));
|
||||
|
||||
private readonly ObservableCollection<object> primaryCollection = new();
|
||||
private readonly ObservableCollection<object> secondaryCollection = new();
|
||||
private static readonly StyledProperty<OverflowTemplateSettings> TemplateSettingsProperty =
|
||||
AvaloniaProperty.Register<Overflow, OverflowTemplateSettings>(nameof(TemplateSettings));
|
||||
|
||||
private readonly ObservableCollection<object> primaryCollection = [];
|
||||
|
||||
private readonly ObservableCollection<object> secondaryCollection = [];
|
||||
|
||||
private ListBox? primaryListBox;
|
||||
|
||||
private ListBox? secondaryListBox;
|
||||
|
||||
public Overflow()
|
||||
{
|
||||
SetValue(TemplateSettingsProperty, new OverflowTemplateSettings());
|
||||
|
||||
TemplateSettings.GetPropertyChangedObservable(OverflowTemplateSettings.PrimarySelectionProperty)
|
||||
.AddClassHandler<OverflowTemplateSettings>(OnPrimarySelectionPropertyChanged);
|
||||
|
||||
TemplateSettings.GetPropertyChangedObservable(OverflowTemplateSettings.SecondarySelectionProperty)
|
||||
.AddClassHandler<OverflowTemplateSettings>(OnSecondarySelectionPropertyChanged);
|
||||
}
|
||||
|
||||
private void OnPrimarySelectionPropertyChanged(OverflowTemplateSettings sender,
|
||||
AvaloniaPropertyChangedEventArgs args)
|
||||
{
|
||||
object? selection = args.GetNewValue<object>();
|
||||
SetValue(SelectedItemProperty, selection);
|
||||
}
|
||||
|
||||
private void OnSecondarySelectionPropertyChanged(OverflowTemplateSettings sender,
|
||||
AvaloniaPropertyChangedEventArgs args)
|
||||
{
|
||||
object? selection = args.GetNewValue<object>();
|
||||
SetValue(SelectedItemProperty, selection);
|
||||
}
|
||||
|
||||
public ITemplate<Panel?> ItemsPanel
|
||||
{
|
||||
get => GetValue(ItemsPanelProperty);
|
||||
@@ -57,15 +87,26 @@ public class OverflowListBox :
|
||||
set => SetValue(SelectedItemProperty, value);
|
||||
}
|
||||
|
||||
public OverflowTemplateSettings TemplateSettings
|
||||
{
|
||||
get => GetValue(TemplateSettingsProperty);
|
||||
set => SetValue(TemplateSettingsProperty, value);
|
||||
}
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs args)
|
||||
{
|
||||
base.OnApplyTemplate(args);
|
||||
|
||||
primaryListBox = args.NameScope.Get<ListBox>("PrimaryListBox");
|
||||
primaryListBox?.SetValue(ItemsControl.ItemsSourceProperty, primaryCollection);
|
||||
if (primaryListBox is not null)
|
||||
{
|
||||
primaryListBox.SetValue(ItemsControl.ItemsSourceProperty, primaryCollection);
|
||||
}
|
||||
|
||||
secondaryListBox = args.NameScope.Get<ListBox>("SecondaryListBox");
|
||||
secondaryListBox?.SetValue(ItemsControl.ItemsSourceProperty, secondaryCollection);
|
||||
if (secondaryListBox is not null)
|
||||
{
|
||||
secondaryListBox.SetValue(ItemsControl.ItemsSourceProperty, secondaryCollection);
|
||||
}
|
||||
|
||||
InitializeCollections();
|
||||
UpdateOverflow();
|
||||
@@ -74,6 +115,12 @@ public class OverflowListBox :
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs args)
|
||||
{
|
||||
base.OnPropertyChanged(args);
|
||||
|
||||
if (args.Property == SelectedItemProperty)
|
||||
{
|
||||
UpdateSelectedItem();
|
||||
}
|
||||
|
||||
if (args.Property == ItemsSourceProperty)
|
||||
{
|
||||
if (args.OldValue is IEnumerable oldCollection && oldCollection is INotifyCollectionChanged oldNotifyCollectionChanged)
|
||||
@@ -105,7 +152,8 @@ public class OverflowListBox :
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSourceCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args)
|
||||
private void OnSourceCollectionChanged(object? sender,
|
||||
NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
switch (args.Action)
|
||||
{
|
||||
@@ -205,7 +253,7 @@ public class OverflowListBox :
|
||||
double accumulatedWidth = 0;
|
||||
double itemSpacing = 6;
|
||||
|
||||
List<(object item, int originalIndex)> itemsToMoveToSecondary = new();
|
||||
List<object> itemsToMoveToSecondary = [];
|
||||
|
||||
for (int i = 0; i < primaryCollection.Count; i++)
|
||||
{
|
||||
@@ -217,7 +265,7 @@ public class OverflowListBox :
|
||||
|
||||
if (accumulatedWidth + itemWidth + (itemsToMoveToSecondary.Count * itemSpacing) > controlWidth)
|
||||
{
|
||||
itemsToMoveToSecondary.Add((item, i));
|
||||
itemsToMoveToSecondary.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -226,14 +274,41 @@ public class OverflowListBox :
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (item, originalIndex) in itemsToMoveToSecondary.OrderByDescending(x => x.originalIndex))
|
||||
foreach (object item in itemsToMoveToSecondary)
|
||||
{
|
||||
primaryCollection.Remove(item);
|
||||
int insertIndexInSecondary = originalIndex - primaryCollection.Count;
|
||||
|
||||
int insertIndexInSecondary = secondaryCollection.Count;
|
||||
if (ItemsSource.Contains(item))
|
||||
{
|
||||
int indexInItemsSource = ItemsSource.IndexOf(item);
|
||||
insertIndexInSecondary = Math.Min(indexInItemsSource, secondaryCollection.Count);
|
||||
}
|
||||
|
||||
secondaryCollection.Insert(insertIndexInSecondary, item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateSelectedItem()
|
||||
{
|
||||
if (SelectedItem is not null)
|
||||
{
|
||||
if (primaryCollection.Contains(SelectedItem))
|
||||
{
|
||||
TemplateSettings.SetValue(OverflowTemplateSettings.PrimarySelectionProperty, SelectedItem);
|
||||
}
|
||||
|
||||
if (secondaryCollection.Contains(SelectedItem))
|
||||
{
|
||||
TemplateSettings.SetValue(OverflowTemplateSettings.SecondarySelectionProperty, SelectedItem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TemplateSettings.SetValue(OverflowTemplateSettings.PrimarySelectionProperty, null);
|
||||
TemplateSettings.SetValue(OverflowTemplateSettings.SecondarySelectionProperty, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
using Avalonia;
|
||||
|
||||
namespace Toolkit.UI.Controls.Avalonia;
|
||||
|
||||
public class OverflowTemplateSettings :
|
||||
AvaloniaObject
|
||||
{
|
||||
public static readonly StyledProperty<object?> PrimarySelectionProperty =
|
||||
AvaloniaProperty.Register<OverflowTemplateSettings, object?>(nameof(PrimarySelection));
|
||||
|
||||
public static readonly StyledProperty<object?> SecondarySelectionProperty =
|
||||
AvaloniaProperty.Register<OverflowTemplateSettings, object?>(nameof(SecondarySelection));
|
||||
|
||||
private bool isSelectionChanging;
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs args)
|
||||
{
|
||||
base.OnPropertyChanged(args);
|
||||
|
||||
if (!isSelectionChanging)
|
||||
{
|
||||
isSelectionChanging = true;
|
||||
|
||||
if (args.Property == PrimarySelectionProperty)
|
||||
{
|
||||
SecondarySelection = null;
|
||||
}
|
||||
else if (args.Property == SecondarySelectionProperty)
|
||||
{
|
||||
PrimarySelection = null;
|
||||
}
|
||||
|
||||
isSelectionChanging = false;
|
||||
}
|
||||
}
|
||||
|
||||
public object? PrimarySelection
|
||||
{
|
||||
get => GetValue(PrimarySelectionProperty);
|
||||
set => SetValue(PrimarySelectionProperty, value);
|
||||
}
|
||||
|
||||
public object? SecondarySelection
|
||||
{
|
||||
get => GetValue(SecondarySelectionProperty);
|
||||
set => SetValue(SecondarySelectionProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<MergeResourceInclude Source="../CarouselView/CarouselView.axaml" />
|
||||
<MergeResourceInclude Source="../PersonPicture/PersonPicture.axaml" />
|
||||
<MergeResourceInclude Source="../SettingsExpander/SettingsExpander.axaml" />
|
||||
<MergeResourceInclude Source="../OverflowListBox/OverflowListBox.axaml" />
|
||||
<MergeResourceInclude Source="../Overflow/Overflow.axaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Styles.Resources>
|
||||
|
||||
Reference in New Issue
Block a user