feat: initialize tag input
This commit is contained in:
76
src/Ursa/Controls/TagInput/TagInput.cs
Normal file
76
src/Ursa/Controls/TagInput/TagInput.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System.Collections;
|
||||
using Avalonia;
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Styling;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
public class TagInput: TemplatedControl
|
||||
{
|
||||
public static readonly StyledProperty<IList<string>> TagsProperty = AvaloniaProperty.Register<TagInput, IList<string>>(
|
||||
nameof(Tags));
|
||||
|
||||
private TextBox _textBox;
|
||||
|
||||
public IList<string> Tags
|
||||
{
|
||||
get => GetValue(TagsProperty);
|
||||
set => SetValue(TagsProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<IList> ItemsProperty = AvaloniaProperty.Register<TagInput, IList>(
|
||||
nameof(Items));
|
||||
|
||||
public IList Items
|
||||
{
|
||||
get => GetValue(ItemsProperty);
|
||||
set => SetValue(ItemsProperty, value);
|
||||
}
|
||||
|
||||
public TagInput()
|
||||
{
|
||||
_textBox = new TextBox();
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<ControlTheme> InputThemeProperty = AvaloniaProperty.Register<TagInput, ControlTheme>(
|
||||
nameof(InputTheme));
|
||||
|
||||
public ControlTheme InputTheme
|
||||
{
|
||||
get => GetValue(InputThemeProperty);
|
||||
set => SetValue(InputThemeProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<IDataTemplate?> ItemTemplateProperty = AvaloniaProperty.Register<TagInput, IDataTemplate?>(
|
||||
nameof(ItemTemplate));
|
||||
|
||||
public IDataTemplate? ItemTemplate
|
||||
{
|
||||
get => GetValue(ItemTemplateProperty);
|
||||
set => SetValue(ItemTemplateProperty, value);
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
Items = new AvaloniaList<object>();
|
||||
if (IsSet(InputThemeProperty) && InputTheme.TargetType == typeof(TextBox))
|
||||
{
|
||||
_textBox.Theme = InputTheme;
|
||||
}
|
||||
_textBox.KeyDown += (sender, args) =>
|
||||
{
|
||||
if (args.Key == Avalonia.Input.Key.Enter)
|
||||
{
|
||||
Items.Insert(Items.Count - 1, _textBox.Text);
|
||||
// Tags.Insert(Items.Count - 1, _textBox.Text ?? string.Empty);
|
||||
_textBox.Text = "";
|
||||
}
|
||||
};
|
||||
Items.Add(_textBox);
|
||||
}
|
||||
}
|
||||
109
src/Ursa/Controls/TagInput/TagInputPanel.cs
Normal file
109
src/Ursa/Controls/TagInput/TagInputPanel.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Utilities;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// TagInputPanel is a horizontal wrap with last item filling last row.
|
||||
/// </summary>
|
||||
public class TagInputPanel: Panel
|
||||
{
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
// return base.MeasureOverride(availableSize);
|
||||
double currentLineX = 0;
|
||||
double currentLineHeight = 0;
|
||||
double totalHeight = 0;
|
||||
|
||||
var children = Children;
|
||||
for (int i = 0; i < children.Count-1; i++)
|
||||
{
|
||||
var child = children[i];
|
||||
child.Measure(availableSize);
|
||||
double deltaX = availableSize.Width - currentLineX;
|
||||
// Width is enough to place next child
|
||||
if (MathUtilities.GreaterThan(deltaX, child.DesiredSize.Width))
|
||||
{
|
||||
currentLineX+=child.DesiredSize.Width;
|
||||
currentLineHeight = Math.Max(currentLineHeight, child.DesiredSize.Height);
|
||||
}
|
||||
// Width is not enough to place next child
|
||||
// reset currentLineX and currentLineHeight
|
||||
// accumulate last line height to total height.
|
||||
// Notice: last line height accumulation only happens when restarting a new line, so it needs to finally add one more time outside iteration.
|
||||
else
|
||||
{
|
||||
currentLineX = child.DesiredSize.Width;
|
||||
totalHeight += currentLineHeight;
|
||||
currentLineHeight = child.DesiredSize.Height;
|
||||
}
|
||||
}
|
||||
|
||||
var last = children[children.Count - 1];
|
||||
last.Measure(availableSize);
|
||||
double lastDeltaX = availableSize.Width - currentLineX;
|
||||
// If width is not enough, add a new line, and recalculate total height
|
||||
if (lastDeltaX < 30)
|
||||
{
|
||||
totalHeight+=currentLineHeight;
|
||||
totalHeight += last.DesiredSize.Height;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentLineHeight = Math.Max(currentLineHeight, last.DesiredSize.Height);
|
||||
totalHeight += currentLineHeight;
|
||||
}
|
||||
|
||||
return new Size(availableSize.Width, totalHeight);
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
double currentLineX = 0;
|
||||
double currentLineHeight = 0;
|
||||
double totalHeight = 0;
|
||||
var children = Children;
|
||||
for (int i = 0; i < children.Count - 1; i++)
|
||||
{
|
||||
var child = children[i];
|
||||
double deltaX = finalSize.Width - currentLineX;
|
||||
// Width is enough to place next child
|
||||
if (MathUtilities.GreaterThan(deltaX, child.DesiredSize.Width))
|
||||
{
|
||||
child.Arrange(new Rect(currentLineX, totalHeight, child.DesiredSize.Width, child.DesiredSize.Height));
|
||||
currentLineX += child.DesiredSize.Width;
|
||||
currentLineHeight = Math.Max(currentLineHeight, child.DesiredSize.Height);
|
||||
}
|
||||
// Width is not enough to place next child
|
||||
// reset currentLineX and currentLineHeight
|
||||
// accumulate last line height to total height.
|
||||
// Notice: last line height accumulation only happens when restarting a new line, so it needs to finally add one more time outside iteration.
|
||||
else
|
||||
{
|
||||
totalHeight += currentLineHeight;
|
||||
child.Arrange(new Rect(0, totalHeight, finalSize.Width, child.DesiredSize.Height));
|
||||
currentLineX = child.DesiredSize.Width;
|
||||
currentLineHeight = child.DesiredSize.Height;
|
||||
}
|
||||
}
|
||||
|
||||
var last = children[children.Count - 1];
|
||||
double lastDeltaX = finalSize.Width - currentLineX;
|
||||
// If width is not enough, add a new line, and recalculate total height
|
||||
if (lastDeltaX < 10)
|
||||
{
|
||||
totalHeight += currentLineHeight;
|
||||
last.Arrange(new Rect(0, totalHeight, finalSize.Width, last.DesiredSize.Height));
|
||||
totalHeight += last.DesiredSize.Height;
|
||||
}
|
||||
else
|
||||
{
|
||||
last.Arrange(new Rect(currentLineX, totalHeight,lastDeltaX, last.DesiredSize.Height));
|
||||
currentLineHeight = Math.Max(currentLineHeight, last.DesiredSize.Height);
|
||||
totalHeight += currentLineHeight;
|
||||
}
|
||||
|
||||
return new Size(finalSize.Width, totalHeight);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user