Added ImageCropper
This commit is contained in:
@@ -1,21 +1,22 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace Toolkit.Foundation;
|
namespace Toolkit.Foundation;
|
||||||
|
|
||||||
public class DirectoryObserver
|
public class DirectoryObserver
|
||||||
{
|
{
|
||||||
public static async Task<string[]> EnumerateFiles(string path,
|
public static async Task<string[]> EnumerateFiles(string path,
|
||||||
string[] filter,
|
string[] patterns,
|
||||||
int count,
|
int count,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
string[] files = [];
|
string[] files = [];
|
||||||
HashSet<string> extensions = filter.Select(x => $".{x.ToLower()}").ToHashSet();
|
List<Regex> regexPatterns = patterns.Select(pattern => new Regex(pattern, RegexOptions.IgnoreCase)).ToList();
|
||||||
|
|
||||||
bool IsBatchComplete()
|
bool IsBatchComplete()
|
||||||
{
|
{
|
||||||
files = Directory.EnumerateFiles(path, "*.*", SearchOption.TopDirectoryOnly)
|
files = Directory.EnumerateFiles(path, "*.*", SearchOption.TopDirectoryOnly)
|
||||||
.Where(x => extensions.Contains(Path.GetExtension(x).ToLower()))
|
.Where(file => regexPatterns.Any(regex => regex.IsMatch(Path.GetFileName(file))))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
if (files.Length != count)
|
if (files.Length != count)
|
||||||
@@ -24,7 +25,8 @@ public class DirectoryObserver
|
|||||||
}
|
}
|
||||||
|
|
||||||
ConcurrentBag<bool> fileAccessResults = [];
|
ConcurrentBag<bool> fileAccessResults = [];
|
||||||
Parallel.ForEach(files, (file) =>
|
|
||||||
|
Parallel.ForEach(files, file =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
<ResourceDictionary
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:controls="using:Toolkit.UI.Controls.Avalonia">
|
||||||
|
<ControlTheme x:Key="ImageCropperThumbStyle" TargetType="Thumb">
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
<Setter Property="Background" Value="{DynamicResource SliderThumbBackground}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource SliderThumbBorderBrush}" />
|
||||||
|
<Setter Property="ClipToBounds" Value="False" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border
|
||||||
|
Margin="-2"
|
||||||
|
Background="{DynamicResource SliderOuterThumbBackground}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{DynamicResource SliderThumbCornerRadius}">
|
||||||
|
<Ellipse
|
||||||
|
Name="SliderInnerThumb"
|
||||||
|
Width="{DynamicResource SliderInnerThumbWidth}"
|
||||||
|
Height="{DynamicResource SliderInnerThumbHeight}"
|
||||||
|
Fill="{TemplateBinding Background}"
|
||||||
|
RenderTransform="scaleX(0.86) scaleY(0.86)">
|
||||||
|
<Ellipse.Transitions>
|
||||||
|
<Transitions>
|
||||||
|
<TransformOperationsTransition
|
||||||
|
Easing="0,0 0,1"
|
||||||
|
Property="RenderTransform"
|
||||||
|
Duration="00:00:00.167" />
|
||||||
|
</Transitions>
|
||||||
|
</Ellipse.Transitions>
|
||||||
|
</Ellipse>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
|
||||||
|
<Style Selector="^:pointerover /template/ Ellipse#SliderInnerThumb">
|
||||||
|
<Setter Property="RenderTransform" Value="scaleX(1.167) scaleY(1.167)" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:pressed /template/ Ellipse#SliderInnerThumb">
|
||||||
|
<Setter Property="RenderTransform" Value="scaleX(0.71) scaleY(0.71)" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:disabled /template/ Ellipse#SliderInnerThumb">
|
||||||
|
<Setter Property="RenderTransform" Value="scaleX(1.167) scaleY(1.167)" />
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
<ControlTheme x:Key="{x:Type ImageCropper}" TargetType="ImageCropper">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Canvas x:Name="Canvas">
|
||||||
|
<Rectangle
|
||||||
|
x:Name="RectangleLeft"
|
||||||
|
Fill="{DynamicResource SmokeFillColorDefaultBrush}"
|
||||||
|
UseLayoutRounding="True" />
|
||||||
|
<Rectangle
|
||||||
|
x:Name="RectangleTop"
|
||||||
|
Fill="{DynamicResource SmokeFillColorDefaultBrush}"
|
||||||
|
UseLayoutRounding="True" />
|
||||||
|
<Rectangle
|
||||||
|
x:Name="RectangleRight"
|
||||||
|
Fill="{DynamicResource SmokeFillColorDefaultBrush}"
|
||||||
|
UseLayoutRounding="True" />
|
||||||
|
<Rectangle
|
||||||
|
x:Name="RectangleBottom"
|
||||||
|
Fill="{DynamicResource SmokeFillColorDefaultBrush}"
|
||||||
|
UseLayoutRounding="True" />
|
||||||
|
<Border
|
||||||
|
x:Name="Border"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderBrush="Transparent"
|
||||||
|
BorderThickness="2"
|
||||||
|
Cursor="SizeAll" />
|
||||||
|
<Thumb
|
||||||
|
x:Name="TopLeftButton"
|
||||||
|
Width="{DynamicResource SliderVerticalThumbWidth}"
|
||||||
|
Height="{DynamicResource SliderVerticalThumbHeight}"
|
||||||
|
Theme="{StaticResource ImageCropperThumbStyle}" />
|
||||||
|
<Thumb
|
||||||
|
x:Name="TopRightButton"
|
||||||
|
Width="{DynamicResource SliderVerticalThumbWidth}"
|
||||||
|
Height="{DynamicResource SliderVerticalThumbHeight}"
|
||||||
|
Theme="{StaticResource ImageCropperThumbStyle}" />
|
||||||
|
<Thumb
|
||||||
|
x:Name="BottomLeftButton"
|
||||||
|
Width="{DynamicResource SliderVerticalThumbWidth}"
|
||||||
|
Height="{DynamicResource SliderVerticalThumbHeight}"
|
||||||
|
Theme="{StaticResource ImageCropperThumbStyle}" />
|
||||||
|
<Thumb
|
||||||
|
x:Name="BottomRightButton"
|
||||||
|
Width="{DynamicResource SliderVerticalThumbWidth}"
|
||||||
|
Height="{DynamicResource SliderVerticalThumbHeight}"
|
||||||
|
Theme="{StaticResource ImageCropperThumbStyle}" />
|
||||||
|
</Canvas>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</ControlTheme>
|
||||||
|
</ResourceDictionary>
|
||||||
@@ -0,0 +1,406 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Controls.Shapes;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Avalonia.Media.Imaging;
|
||||||
|
|
||||||
|
namespace Toolkit.UI.Controls.Avalonia;
|
||||||
|
|
||||||
|
public class ImageCropper : TemplatedControl
|
||||||
|
{
|
||||||
|
public static readonly StyledProperty<IImage?> CurrentAreaBitmapProperty =
|
||||||
|
AvaloniaProperty.Register<ImageCropper, IImage?>(nameof(CurrentAreaBitmap));
|
||||||
|
|
||||||
|
public static readonly StyledProperty<Rect> CurrentRectProperty =
|
||||||
|
AvaloniaProperty.Register<ImageCropper, Rect>(nameof(CurrentRect));
|
||||||
|
|
||||||
|
public static readonly StyledProperty<bool> IsRatioScaleProperty =
|
||||||
|
AvaloniaProperty.Register<ImageCropper, bool>(nameof(IsRatioScale));
|
||||||
|
|
||||||
|
public static readonly StyledProperty<double> RectScaleProperty =
|
||||||
|
AvaloniaProperty.Register<ImageCropper, double>(nameof(RectScale), 0.5);
|
||||||
|
|
||||||
|
public static readonly StyledProperty<Size> ScaleSizeProperty =
|
||||||
|
AvaloniaProperty.Register<ImageCropper, Size>(nameof(ScaleSize), new Size(2, 1));
|
||||||
|
|
||||||
|
public static readonly StyledProperty<IImage?> SourceProperty =
|
||||||
|
AvaloniaProperty.Register<ImageCropper, IImage?>(nameof(Source));
|
||||||
|
|
||||||
|
private Border? border;
|
||||||
|
private Thumb? bottomLeftButton;
|
||||||
|
private Thumb? bottomRightButton;
|
||||||
|
private Canvas? canvas;
|
||||||
|
private bool isDragging;
|
||||||
|
private double offsetX;
|
||||||
|
private double offsetY;
|
||||||
|
private Rectangle? rectangleBottom;
|
||||||
|
private Rectangle? rectangleLeft;
|
||||||
|
private Rectangle? rectangleRight;
|
||||||
|
private Rectangle? rectangleTop;
|
||||||
|
|
||||||
|
private Thumb? topLeftButton;
|
||||||
|
private Thumb? topRightButton;
|
||||||
|
|
||||||
|
static ImageCropper()
|
||||||
|
{
|
||||||
|
AffectsRender<ImageCropper>(SourceProperty, RectScaleProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IImage? CurrentAreaBitmap
|
||||||
|
{
|
||||||
|
get => GetValue(CurrentAreaBitmapProperty);
|
||||||
|
private set => SetValue(CurrentAreaBitmapProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rect CurrentRect
|
||||||
|
{
|
||||||
|
get => GetValue(CurrentRectProperty);
|
||||||
|
private set => SetValue(CurrentRectProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsRatioScale
|
||||||
|
{
|
||||||
|
get => GetValue(IsRatioScaleProperty);
|
||||||
|
set => SetValue(IsRatioScaleProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double RectScale
|
||||||
|
{
|
||||||
|
get => GetValue(RectScaleProperty);
|
||||||
|
set => SetValue(RectScaleProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Size ScaleSize
|
||||||
|
{
|
||||||
|
get => GetValue(ScaleSizeProperty);
|
||||||
|
set => SetValue(ScaleSizeProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IImage? Source
|
||||||
|
{
|
||||||
|
get => GetValue(SourceProperty);
|
||||||
|
set => SetValue(SourceProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs args)
|
||||||
|
{
|
||||||
|
base.OnApplyTemplate(args);
|
||||||
|
|
||||||
|
canvas = args.NameScope.Find<Canvas>("Canvas");
|
||||||
|
rectangleLeft = args.NameScope.Find<Rectangle>("RectangleLeft");
|
||||||
|
rectangleTop = args.NameScope.Find<Rectangle>("RectangleTop");
|
||||||
|
rectangleRight = args.NameScope.Find<Rectangle>("RectangleRight");
|
||||||
|
rectangleBottom = args.NameScope.Find<Rectangle>("RectangleBottom");
|
||||||
|
border = args.NameScope.Find<Border>("Border");
|
||||||
|
|
||||||
|
topLeftButton = args.NameScope.Find<Thumb>("TopLeftButton");
|
||||||
|
if (topLeftButton is not null)
|
||||||
|
{
|
||||||
|
topLeftButton.DragDelta += OnThumbDragDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
topRightButton = args.NameScope.Find<Thumb>("TopRightButton");
|
||||||
|
if (topRightButton is not null)
|
||||||
|
{
|
||||||
|
topRightButton.DragDelta += OnThumbDragDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomLeftButton = args.NameScope.Find<Thumb>("BottomLeftButton");
|
||||||
|
if (bottomLeftButton is not null)
|
||||||
|
{
|
||||||
|
bottomLeftButton.DragDelta += OnThumbDragDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomRightButton = args.NameScope.Find<Thumb>("BottomRightButton");
|
||||||
|
if (bottomRightButton is not null)
|
||||||
|
{
|
||||||
|
bottomRightButton.DragDelta += OnThumbDragDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnLoaded(RoutedEventArgs args)
|
||||||
|
{
|
||||||
|
base.OnLoaded(args);
|
||||||
|
DrawImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||||
|
{
|
||||||
|
base.OnPropertyChanged(change);
|
||||||
|
|
||||||
|
if (change.Property == SourceProperty ||
|
||||||
|
change.Property == IsRatioScaleProperty ||
|
||||||
|
change.Property == RectScaleProperty)
|
||||||
|
{
|
||||||
|
DrawImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawImage()
|
||||||
|
{
|
||||||
|
if (canvas is null || Source is not Bitmap bitmap)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double maxWidth = DesiredSize.Width;
|
||||||
|
double maxHeight = DesiredSize.Height;
|
||||||
|
|
||||||
|
double imageWidth = Source.Size.Width;
|
||||||
|
double imageHeight = Source.Size.Height;
|
||||||
|
|
||||||
|
double scaleFactor = Math.Min(maxWidth / imageWidth, maxHeight / imageHeight);
|
||||||
|
double width = imageWidth * scaleFactor;
|
||||||
|
double height = imageHeight * scaleFactor;
|
||||||
|
|
||||||
|
canvas.Width = width;
|
||||||
|
canvas.Height = height;
|
||||||
|
|
||||||
|
canvas.Background = new ImageBrush
|
||||||
|
{
|
||||||
|
Source = bitmap
|
||||||
|
};
|
||||||
|
|
||||||
|
UpdatePunchThrough(width, height);
|
||||||
|
Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBorderPointerMoved(object? sender,
|
||||||
|
PointerEventArgs args)
|
||||||
|
{
|
||||||
|
if (!isDragging || canvas is null || border is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point position = args.GetPosition(this);
|
||||||
|
double newX = Math.Clamp(position.X - offsetX, 0, canvas.Bounds.Width - border.Bounds.Width);
|
||||||
|
double newY = Math.Clamp(position.Y - offsetY, 0, canvas.Bounds.Height - border.Bounds.Height);
|
||||||
|
|
||||||
|
Canvas.SetLeft(border, newX);
|
||||||
|
Canvas.SetTop(border, newY);
|
||||||
|
|
||||||
|
PositionThumbs();
|
||||||
|
Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBorderPointerPressed(object? sender,
|
||||||
|
PointerPressedEventArgs args)
|
||||||
|
{
|
||||||
|
if (!isDragging && border is not null)
|
||||||
|
{
|
||||||
|
isDragging = true;
|
||||||
|
Point position = args.GetPosition(this);
|
||||||
|
|
||||||
|
offsetX = position.X - Canvas.GetLeft(border);
|
||||||
|
offsetY = position.Y - Canvas.GetTop(border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBorderPointerReleased(object? sender,
|
||||||
|
PointerReleasedEventArgs args) => isDragging = false;
|
||||||
|
|
||||||
|
private void OnThumbDragDelta(object? sender, VectorEventArgs args)
|
||||||
|
{
|
||||||
|
if (canvas is null || border is null || sender is not Thumb thumb)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double deltaX = args.Vector.X;
|
||||||
|
double deltaY = args.Vector.Y;
|
||||||
|
|
||||||
|
double leftPosition = Canvas.GetLeft(border);
|
||||||
|
double topPosition = Canvas.GetTop(border);
|
||||||
|
double newWidth = border.Width;
|
||||||
|
double newHeight = border.Height;
|
||||||
|
|
||||||
|
switch (thumb.Name)
|
||||||
|
{
|
||||||
|
case "TopLeftButton":
|
||||||
|
newWidth = Math.Max(0, border.Width - deltaX);
|
||||||
|
newHeight = Math.Max(0, border.Height - deltaY);
|
||||||
|
leftPosition += deltaX;
|
||||||
|
topPosition += deltaY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "TopRightButton":
|
||||||
|
newWidth = Math.Max(0, border.Width + deltaX);
|
||||||
|
newHeight = Math.Max(0, border.Height - deltaY);
|
||||||
|
topPosition += deltaY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "BottomLeftButton":
|
||||||
|
newWidth = Math.Max(0, border.Width - deltaX);
|
||||||
|
newHeight = Math.Max(0, border.Height + deltaY);
|
||||||
|
leftPosition += deltaX;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "BottomRightButton":
|
||||||
|
newWidth = Math.Max(0, border.Width + deltaX);
|
||||||
|
newHeight = Math.Max(0, border.Height + deltaY);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newWidth < 0 || newHeight < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thumb.Name == "TopLeftButton" || thumb.Name == "BottomLeftButton")
|
||||||
|
{
|
||||||
|
leftPosition = Math.Max(0, leftPosition);
|
||||||
|
newWidth = Math.Max(0, border.Width - (leftPosition - Canvas.GetLeft(border)));
|
||||||
|
}
|
||||||
|
else if (thumb.Name == "TopRightButton" || thumb.Name == "BottomRightButton")
|
||||||
|
{
|
||||||
|
double rightBoundary = canvas.Width;
|
||||||
|
newWidth = Math.Min(newWidth, rightBoundary - leftPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thumb.Name == "TopLeftButton" || thumb.Name == "TopRightButton")
|
||||||
|
{
|
||||||
|
topPosition = Math.Max(0, topPosition);
|
||||||
|
newHeight = Math.Max(0, border.Height - (topPosition - Canvas.GetTop(border)));
|
||||||
|
}
|
||||||
|
else if (thumb.Name == "BottomLeftButton" || thumb.Name == "BottomRightButton")
|
||||||
|
{
|
||||||
|
double bottomBoundary = canvas.Height;
|
||||||
|
newHeight = Math.Min(newHeight, bottomBoundary - topPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
border.Width = newWidth;
|
||||||
|
border.Height = newHeight;
|
||||||
|
|
||||||
|
Canvas.SetLeft(border, leftPosition);
|
||||||
|
Canvas.SetTop(border, topPosition);
|
||||||
|
|
||||||
|
PositionThumbs();
|
||||||
|
Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void PositionThumbs()
|
||||||
|
{
|
||||||
|
if (border == null ||
|
||||||
|
canvas == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double borderLeft = Canvas.GetLeft(border);
|
||||||
|
double borderTop = Canvas.GetTop(border);
|
||||||
|
double borderWidth = border.Width;
|
||||||
|
double borderHeight = border.Height;
|
||||||
|
|
||||||
|
if (topLeftButton is not null)
|
||||||
|
{
|
||||||
|
Canvas.SetLeft(topLeftButton, borderLeft - (topLeftButton.Width / 2));
|
||||||
|
Canvas.SetTop(topLeftButton, borderTop - (topLeftButton.Height / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topRightButton is not null)
|
||||||
|
{
|
||||||
|
Canvas.SetLeft(topRightButton, borderLeft + borderWidth - (topRightButton.Width / 2));
|
||||||
|
Canvas.SetTop(topRightButton, borderTop - (topRightButton.Height / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bottomLeftButton is not null)
|
||||||
|
{
|
||||||
|
Canvas.SetLeft(bottomLeftButton, borderLeft - (bottomLeftButton.Width / 2));
|
||||||
|
Canvas.SetTop(bottomLeftButton, borderTop + borderHeight - (bottomLeftButton.Height / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bottomRightButton is not null)
|
||||||
|
{
|
||||||
|
Canvas.SetLeft(bottomRightButton, borderLeft + borderWidth - (bottomRightButton.Width / 2));
|
||||||
|
Canvas.SetTop(bottomRightButton, borderTop + borderHeight - (bottomRightButton.Height / 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Render()
|
||||||
|
{
|
||||||
|
if (canvas == null ||
|
||||||
|
border == null ||
|
||||||
|
rectangleLeft == null ||
|
||||||
|
rectangleTop == null ||
|
||||||
|
rectangleRight == null ||
|
||||||
|
rectangleBottom == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double borderTop = Canvas.GetTop(border);
|
||||||
|
double borderLeft = Canvas.GetLeft(border);
|
||||||
|
|
||||||
|
rectangleLeft.Width = Math.Max(0, borderLeft);
|
||||||
|
rectangleLeft.Height = Math.Max(0, border.Height);
|
||||||
|
Canvas.SetTop(rectangleLeft, borderTop);
|
||||||
|
|
||||||
|
rectangleTop.Width = Math.Max(0, canvas.Width);
|
||||||
|
rectangleTop.Height = Math.Max(0, borderTop);
|
||||||
|
|
||||||
|
double rightX = borderLeft + border.Width;
|
||||||
|
|
||||||
|
rectangleRight.Width = Math.Max(0, canvas.Width - rightX);
|
||||||
|
rectangleRight.Height = Math.Max(0, border.Height);
|
||||||
|
Canvas.SetLeft(rectangleRight, rightX);
|
||||||
|
Canvas.SetTop(rectangleRight, borderTop);
|
||||||
|
|
||||||
|
double bottomY = borderTop + border.Height;
|
||||||
|
|
||||||
|
rectangleBottom.Width = Math.Max(0, canvas.Width);
|
||||||
|
rectangleBottom.Height = Math.Max(0, canvas.Height - bottomY);
|
||||||
|
Canvas.SetTop(rectangleBottom, bottomY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdatePunchThrough(double width,
|
||||||
|
double height)
|
||||||
|
{
|
||||||
|
if (canvas == null ||
|
||||||
|
border == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsRatioScale && ScaleSize.Width > 0 && ScaleSize.Height > 0)
|
||||||
|
{
|
||||||
|
if (ScaleSize.Width > ScaleSize.Height)
|
||||||
|
{
|
||||||
|
border.Width = width * RectScale;
|
||||||
|
border.Height = border.Width / ScaleSize.Width;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
border.Height = height * RectScale;
|
||||||
|
border.Width = border.Height * ScaleSize.Height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
border.Width = width * RectScale;
|
||||||
|
border.Height = height * RectScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
double centreX = (canvas.Width - border.Width) / 2;
|
||||||
|
double centreY = (canvas.Height - border.Height) / 2;
|
||||||
|
|
||||||
|
Canvas.SetLeft(border, centreX);
|
||||||
|
Canvas.SetTop(border, centreY);
|
||||||
|
|
||||||
|
PositionThumbs();
|
||||||
|
|
||||||
|
border.PointerPressed -= OnBorderPointerPressed;
|
||||||
|
border.PointerPressed += OnBorderPointerPressed;
|
||||||
|
border.PointerMoved -= OnBorderPointerMoved;
|
||||||
|
border.PointerMoved += OnBorderPointerMoved;
|
||||||
|
border.PointerReleased -= OnBorderPointerReleased;
|
||||||
|
border.PointerReleased += OnBorderPointerReleased;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,336 +1,335 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
|
||||||
namespace Toolkit.UI.Controls.Avalonia
|
namespace Toolkit.UI.Controls.Avalonia;
|
||||||
|
|
||||||
|
public class ResponsiveGrid : Grid
|
||||||
{
|
{
|
||||||
public class ResponsiveGrid : Grid
|
public static readonly AvaloniaProperty<int> ActualColumnProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ActualColumn", 0);
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> ActualRowProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ActualRow", 0);
|
||||||
|
|
||||||
|
public static readonly StyledProperty<SizeThresholds> BreakPointsProperty =
|
||||||
|
AvaloniaProperty.Register<ResponsiveGrid, SizeThresholds>(nameof(Thresholds));
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> LargeOffsetProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("LargeOffset", 0);
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> LargePullProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("LargePull", 0);
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> LargePushProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("LargePush", 0);
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> LargeProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("Large", 0);
|
||||||
|
|
||||||
|
public static readonly StyledProperty<int> MaxDivisionProperty =
|
||||||
|
AvaloniaProperty.Register<ResponsiveGrid, int>(nameof(MaxDivision), 12);
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> MediumOffsetProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("MediumOffset", 0);
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> MediumPullProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("MediumPull", 0);
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> MediumPushProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("MediumPush", 0);
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> MediumProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("Medium", 0);
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> SmallOffsetProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("SmallOffset", 0);
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> SmallPullProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("SmallPull", 0);
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> SmallPushProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("SmallPush", 0);
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> SmallProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("Small", 0);
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> ExtraSmallOffsetProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ExtraSmallOffset", 0);
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> ExtraSmallPullProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ExtraSmallPull", 0);
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> ExtraSmallPushProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ExtraSmallPush", 0);
|
||||||
|
|
||||||
|
public static readonly AvaloniaProperty<int> ExtraSmallProperty =
|
||||||
|
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ExtraSmall", 0);
|
||||||
|
|
||||||
|
static ResponsiveGrid()
|
||||||
{
|
{
|
||||||
public static readonly AvaloniaProperty<int> ActualColumnProperty =
|
AffectsMeasure<ResponsiveGrid>(
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ActualColumn", 0);
|
MaxDivisionProperty,
|
||||||
|
BreakPointsProperty,
|
||||||
|
LargeProperty,
|
||||||
|
MediumProperty,
|
||||||
|
SmallProperty,
|
||||||
|
ExtraSmallProperty,
|
||||||
|
LargeOffsetProperty,
|
||||||
|
LargePullProperty,
|
||||||
|
LargePushProperty,
|
||||||
|
MediumOffsetProperty,
|
||||||
|
MediumPullProperty,
|
||||||
|
MediumPushProperty,
|
||||||
|
SmallOffsetProperty,
|
||||||
|
SmallPullProperty,
|
||||||
|
SmallPushProperty,
|
||||||
|
ExtraSmallOffsetProperty,
|
||||||
|
ExtraSmallPullProperty,
|
||||||
|
ExtraSmallPushProperty
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> ActualRowProperty =
|
public ResponsiveGrid()
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ActualRow", 0);
|
{
|
||||||
|
MaxDivision = 12;
|
||||||
|
Thresholds = new SizeThresholds();
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly StyledProperty<SizeThresholds> BreakPointsProperty =
|
public int MaxDivision
|
||||||
AvaloniaProperty.Register<ResponsiveGrid, SizeThresholds>(nameof(Thresholds));
|
{
|
||||||
|
get => GetValue(MaxDivisionProperty);
|
||||||
|
set => SetValue(MaxDivisionProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> LargeOffsetProperty =
|
public SizeThresholds Thresholds
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("LargeOffset", 0);
|
{
|
||||||
|
get => GetValue(BreakPointsProperty);
|
||||||
|
set => SetValue(BreakPointsProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> LargePullProperty =
|
public static int GetActualColumn(AvaloniaObject avaloniaObject) =>
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("LargePull", 0);
|
avaloniaObject.GetValue<int>(ActualColumnProperty);
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> LargePushProperty =
|
public static int GetActualRow(AvaloniaObject avaloniaObject) =>
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("LargePush", 0);
|
avaloniaObject.GetValue<int>(ActualRowProperty);
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> LargeProperty =
|
public static int GetLarge(AvaloniaObject avaloniaObject) =>
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("Large", 0);
|
avaloniaObject.GetValue<int>(LargeProperty);
|
||||||
|
|
||||||
public static readonly StyledProperty<int> MaxDivisionProperty =
|
public static int GetLargeOffset(AvaloniaObject avaloniaObject) =>
|
||||||
AvaloniaProperty.Register<ResponsiveGrid, int>(nameof(MaxDivision), 12);
|
avaloniaObject.GetValue<int>(LargeOffsetProperty);
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> MediumOffsetProperty =
|
public static int GetLargePull(AvaloniaObject avaloniaObject) =>
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("MediumOffset", 0);
|
avaloniaObject.GetValue<int>(LargePullProperty);
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> MediumPullProperty =
|
public static int GetLargePush(AvaloniaObject avaloniaObject) =>
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("MediumPull", 0);
|
avaloniaObject.GetValue<int>(LargePushProperty);
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> MediumPushProperty =
|
public static int GetMedium(AvaloniaObject avaloniaObject) =>
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("MediumPush", 0);
|
avaloniaObject.GetValue<int>(MediumProperty);
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> MediumProperty =
|
public static int GetMediumOffset(AvaloniaObject avaloniaObject) =>
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("Medium", 0);
|
avaloniaObject.GetValue<int>(MediumOffsetProperty);
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> SmallOffsetProperty =
|
public static int GetMediumPull(AvaloniaObject avaloniaObject) =>
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("SmallOffset", 0);
|
avaloniaObject.GetValue<int>(MediumPullProperty);
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> SmallPullProperty =
|
public static int GetMediumPush(AvaloniaObject avaloniaObject) =>
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("SmallPull", 0);
|
avaloniaObject.GetValue<int>(MediumPushProperty);
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> SmallPushProperty =
|
public static int GetSmall(AvaloniaObject avaloniaObject) =>
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("SmallPush", 0);
|
avaloniaObject.GetValue<int>(SmallProperty);
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> SmallProperty =
|
public static int GetSmallOffset(AvaloniaObject avaloniaObject) =>
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("Small", 0);
|
avaloniaObject.GetValue<int>(SmallOffsetProperty);
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> ExtraSmallOffsetProperty =
|
public static int GetSmallPull(AvaloniaObject avaloniaObject) =>
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ExtraSmallOffset", 0);
|
avaloniaObject.GetValue<int>(SmallPullProperty);
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> ExtraSmallPullProperty =
|
public static int GetSmallPush(AvaloniaObject avaloniaObject) =>
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ExtraSmallPull", 0);
|
avaloniaObject.GetValue<int>(SmallPushProperty);
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> ExtraSmallPushProperty =
|
public static int GetExtraSmall(AvaloniaObject avaloniaObject) =>
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ExtraSmallPush", 0);
|
avaloniaObject.GetValue<int>(ExtraSmallProperty);
|
||||||
|
|
||||||
public static readonly AvaloniaProperty<int> ExtraSmallProperty =
|
public static int GetExtraSmallOffset(AvaloniaObject avaloniaObject) =>
|
||||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ExtraSmall", 0);
|
avaloniaObject.GetValue<int>(ExtraSmallOffsetProperty);
|
||||||
|
|
||||||
static ResponsiveGrid()
|
public static int GetExtraSmallPull(AvaloniaObject avaloniaObject) =>
|
||||||
|
avaloniaObject.GetValue<int>(ExtraSmallPullProperty);
|
||||||
|
|
||||||
|
public static int GetExtraSmallPush(AvaloniaObject avaloniaObject) =>
|
||||||
|
avaloniaObject.GetValue<int>(ExtraSmallPushProperty);
|
||||||
|
|
||||||
|
public static void SetLarge(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(LargeProperty, value);
|
||||||
|
|
||||||
|
public static void SetLargeOffset(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(LargeOffsetProperty, value);
|
||||||
|
|
||||||
|
public static void SetLargePull(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(LargePullProperty, value);
|
||||||
|
|
||||||
|
public static void SetLargePush(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(LargePushProperty, value);
|
||||||
|
|
||||||
|
public static void SetMedium(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(MediumProperty, value);
|
||||||
|
|
||||||
|
public static void SetMediumOffset(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(MediumOffsetProperty, value);
|
||||||
|
|
||||||
|
public static void SetMediumPull(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(MediumPullProperty, value);
|
||||||
|
|
||||||
|
public static void SetMediumPush(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(MediumPushProperty, value);
|
||||||
|
|
||||||
|
public static void SetSmall(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(SmallProperty, value);
|
||||||
|
|
||||||
|
public static void SetSmallOffset(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(SmallOffsetProperty, value);
|
||||||
|
|
||||||
|
public static void SetSmallPull(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(SmallPullProperty, value);
|
||||||
|
|
||||||
|
public static void SetSmallPush(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(SmallPushProperty, value);
|
||||||
|
|
||||||
|
public static void SetExtraSmall(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(ExtraSmallProperty, value);
|
||||||
|
|
||||||
|
public static void SetExtraSmallOffset(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(ExtraSmallOffsetProperty, value);
|
||||||
|
|
||||||
|
public static void SetExtraSmallPull(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(ExtraSmallPullProperty, value);
|
||||||
|
|
||||||
|
public static void SetExtraSmallPush(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(ExtraSmallPushProperty, value);
|
||||||
|
|
||||||
|
protected static void SetActualColumn(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(ActualColumnProperty, value);
|
||||||
|
|
||||||
|
protected static void SetActualRow(AvaloniaObject avaloniaObject, int value) =>
|
||||||
|
avaloniaObject.SetValue(ActualRowProperty, value);
|
||||||
|
|
||||||
|
protected override Size ArrangeOverride(Size finalSize)
|
||||||
|
{
|
||||||
|
double columnWidth = finalSize.Width / MaxDivision;
|
||||||
|
|
||||||
|
IEnumerable<IGrouping<int, Control>> groupedRows = Children.OfType<Control>().GroupBy(GetActualRow);
|
||||||
|
|
||||||
|
double yOffset = 0;
|
||||||
|
foreach (IGrouping<int, Control> row in groupedRows)
|
||||||
{
|
{
|
||||||
AffectsMeasure<ResponsiveGrid>(
|
double maxRowHeight = row.Max(control => control.DesiredSize.Height);
|
||||||
MaxDivisionProperty,
|
|
||||||
BreakPointsProperty,
|
|
||||||
LargeProperty,
|
|
||||||
MediumProperty,
|
|
||||||
SmallProperty,
|
|
||||||
ExtraSmallProperty,
|
|
||||||
LargeOffsetProperty,
|
|
||||||
LargePullProperty,
|
|
||||||
LargePushProperty,
|
|
||||||
MediumOffsetProperty,
|
|
||||||
MediumPullProperty,
|
|
||||||
MediumPushProperty,
|
|
||||||
SmallOffsetProperty,
|
|
||||||
SmallPullProperty,
|
|
||||||
SmallPushProperty,
|
|
||||||
ExtraSmallOffsetProperty,
|
|
||||||
ExtraSmallPullProperty,
|
|
||||||
ExtraSmallPushProperty
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResponsiveGrid()
|
foreach (Control element in row)
|
||||||
{
|
|
||||||
MaxDivision = 12;
|
|
||||||
Thresholds = new SizeThresholds();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int MaxDivision
|
|
||||||
{
|
|
||||||
get => GetValue(MaxDivisionProperty);
|
|
||||||
set => SetValue(MaxDivisionProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SizeThresholds Thresholds
|
|
||||||
{
|
|
||||||
get => GetValue(BreakPointsProperty);
|
|
||||||
set => SetValue(BreakPointsProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetActualColumn(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(ActualColumnProperty);
|
|
||||||
|
|
||||||
public static int GetActualRow(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(ActualRowProperty);
|
|
||||||
|
|
||||||
public static int GetLarge(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(LargeProperty);
|
|
||||||
|
|
||||||
public static int GetLargeOffset(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(LargeOffsetProperty);
|
|
||||||
|
|
||||||
public static int GetLargePull(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(LargePullProperty);
|
|
||||||
|
|
||||||
public static int GetLargePush(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(LargePushProperty);
|
|
||||||
|
|
||||||
public static int GetMedium(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(MediumProperty);
|
|
||||||
|
|
||||||
public static int GetMediumOffset(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(MediumOffsetProperty);
|
|
||||||
|
|
||||||
public static int GetMediumPull(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(MediumPullProperty);
|
|
||||||
|
|
||||||
public static int GetMediumPush(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(MediumPushProperty);
|
|
||||||
|
|
||||||
public static int GetSmall(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(SmallProperty);
|
|
||||||
|
|
||||||
public static int GetSmallOffset(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(SmallOffsetProperty);
|
|
||||||
|
|
||||||
public static int GetSmallPull(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(SmallPullProperty);
|
|
||||||
|
|
||||||
public static int GetSmallPush(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(SmallPushProperty);
|
|
||||||
|
|
||||||
public static int GetExtraSmall(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(ExtraSmallProperty);
|
|
||||||
|
|
||||||
public static int GetExtraSmallOffset(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(ExtraSmallOffsetProperty);
|
|
||||||
|
|
||||||
public static int GetExtraSmallPull(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(ExtraSmallPullProperty);
|
|
||||||
|
|
||||||
public static int GetExtraSmallPush(AvaloniaObject avaloniaObject) =>
|
|
||||||
avaloniaObject.GetValue<int>(ExtraSmallPushProperty);
|
|
||||||
|
|
||||||
public static void SetLarge(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(LargeProperty, value);
|
|
||||||
|
|
||||||
public static void SetLargeOffset(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(LargeOffsetProperty, value);
|
|
||||||
|
|
||||||
public static void SetLargePull(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(LargePullProperty, value);
|
|
||||||
|
|
||||||
public static void SetLargePush(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(LargePushProperty, value);
|
|
||||||
|
|
||||||
public static void SetMedium(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(MediumProperty, value);
|
|
||||||
|
|
||||||
public static void SetMediumOffset(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(MediumOffsetProperty, value);
|
|
||||||
|
|
||||||
public static void SetMediumPull(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(MediumPullProperty, value);
|
|
||||||
|
|
||||||
public static void SetMediumPush(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(MediumPushProperty, value);
|
|
||||||
|
|
||||||
public static void SetSmall(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(SmallProperty, value);
|
|
||||||
|
|
||||||
public static void SetSmallOffset(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(SmallOffsetProperty, value);
|
|
||||||
|
|
||||||
public static void SetSmallPull(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(SmallPullProperty, value);
|
|
||||||
|
|
||||||
public static void SetSmallPush(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(SmallPushProperty, value);
|
|
||||||
|
|
||||||
public static void SetExtraSmall(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(ExtraSmallProperty, value);
|
|
||||||
|
|
||||||
public static void SetExtraSmallOffset(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(ExtraSmallOffsetProperty, value);
|
|
||||||
|
|
||||||
public static void SetExtraSmallPull(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(ExtraSmallPullProperty, value);
|
|
||||||
|
|
||||||
public static void SetExtraSmallPush(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(ExtraSmallPushProperty, value);
|
|
||||||
|
|
||||||
protected static void SetActualColumn(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(ActualColumnProperty, value);
|
|
||||||
|
|
||||||
protected static void SetActualRow(AvaloniaObject avaloniaObject, int value) =>
|
|
||||||
avaloniaObject.SetValue(ActualRowProperty, value);
|
|
||||||
|
|
||||||
protected override Size ArrangeOverride(Size finalSize)
|
|
||||||
{
|
|
||||||
double columnWidth = finalSize.Width / MaxDivision;
|
|
||||||
|
|
||||||
IEnumerable<IGrouping<int, Control>> groupedRows = Children.OfType<Control>().GroupBy(GetActualRow);
|
|
||||||
|
|
||||||
double yOffset = 0;
|
|
||||||
foreach (IGrouping<int, Control> row in groupedRows)
|
|
||||||
{
|
{
|
||||||
double maxRowHeight = row.Max(control => control.DesiredSize.Height);
|
int column = GetActualColumn(element);
|
||||||
|
int span = GetSpan(element, finalSize.Width);
|
||||||
|
|
||||||
foreach (Control element in row)
|
Rect rect = new(column * columnWidth, yOffset, span * columnWidth, maxRowHeight);
|
||||||
{
|
element.Arrange(rect);
|
||||||
int column = GetActualColumn(element);
|
|
||||||
int span = GetSpan(element, finalSize.Width);
|
|
||||||
|
|
||||||
Rect rect = new(column * columnWidth, yOffset, span * columnWidth, maxRowHeight);
|
|
||||||
element.Arrange(rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
yOffset += maxRowHeight;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return finalSize;
|
yOffset += maxRowHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int GetOffset(Control control, double width)
|
return finalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int GetOffset(Control control, double width)
|
||||||
|
{
|
||||||
|
int GetXS(Control control) => GetExtraSmallOffset(control) is 0 ? 0 : GetExtraSmallOffset(control);
|
||||||
|
int GetSM(Control control) => GetSmallOffset(control) is 0 ? GetXS(control) : GetSmallOffset(control);
|
||||||
|
int GetMD(Control control) => GetMediumOffset(control) is 0 ? GetSM(control) : GetMediumOffset(control);
|
||||||
|
int GetLG(Control control) => GetLargeOffset(control) is 0 ? GetMD(control) : GetLargeOffset(control);
|
||||||
|
|
||||||
|
int span = width < Thresholds.ExtraSmallToSmall ? GetXS(control) : width < Thresholds.SmallToMedium ?
|
||||||
|
GetSM(control) : width < Thresholds.MediumToLarge ? GetMD(control) : GetLG(control);
|
||||||
|
return Math.Min(span, MaxDivision);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int GetPull(Control control, double width)
|
||||||
|
{
|
||||||
|
int GetXS(Control control) => GetExtraSmallPull(control) is 0 ? 0 : GetExtraSmallPull(control);
|
||||||
|
int GetSM(Control control) => GetSmallPull(control) is 0 ? GetXS(control) : GetSmallPull(control);
|
||||||
|
int GetMD(Control control) => GetMediumPull(control) is 0 ? GetSM(control) : GetMediumPull(control);
|
||||||
|
int GetLG(Control control) => GetLargePull(control) is 0 ? GetMD(control) : GetLargePull(control);
|
||||||
|
|
||||||
|
int span = width < Thresholds.ExtraSmallToSmall ? GetXS(control) : width < Thresholds.SmallToMedium ?
|
||||||
|
GetSM(control) : width < Thresholds.MediumToLarge ? GetMD(control) : GetLG(control);
|
||||||
|
return Math.Min(span, MaxDivision);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int GetPush(Control control, double width)
|
||||||
|
{
|
||||||
|
int GetXS(Control control) => GetExtraSmallPush(control) is 0 ? 0 : GetExtraSmallPush(control);
|
||||||
|
int GetSM(Control control) => GetSmallPush(control) is 0 ? GetXS(control) : GetSmallPush(control);
|
||||||
|
int GetMD(Control control) => GetMediumPush(control) is 0 ? GetSM(control) : GetMediumPush(control);
|
||||||
|
int GetLG(Control control) => GetLargePush(control) is 0 ? GetMD(control) : GetLargePush(control);
|
||||||
|
|
||||||
|
int span = width < Thresholds.ExtraSmallToSmall ? GetXS(control) : width < Thresholds.SmallToMedium ?
|
||||||
|
GetSM(control) : width < Thresholds.MediumToLarge ? GetMD(control) : GetLG(control);
|
||||||
|
|
||||||
|
return Math.Min(span, MaxDivision);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int GetSpan(Control control, double width)
|
||||||
|
{
|
||||||
|
int GetXS(Control control) => GetExtraSmall(control) is 0 ? MaxDivision : GetExtraSmall(control);
|
||||||
|
int GetSM(Control control) => GetSmall(control) is 0 ? GetXS(control) : GetSmall(control);
|
||||||
|
int GetMD(Control control) => GetMedium(control) is 0 ? GetSM(control) : GetMedium(control);
|
||||||
|
int GetLG(Control control) => GetLarge(control) is 0 ? GetMD(control) : GetLarge(control);
|
||||||
|
|
||||||
|
int span = width < Thresholds.ExtraSmallToSmall ? GetXS(control) : width < Thresholds.SmallToMedium ?
|
||||||
|
GetSM(control) : width < Thresholds.MediumToLarge ? GetMD(control) : GetLG(control);
|
||||||
|
|
||||||
|
return Math.Min(Math.Max(0, span), MaxDivision); ;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Size MeasureOverride(Size availableSize)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
int currentRow = 0;
|
||||||
|
|
||||||
|
double availableWidth = double.IsPositiveInfinity(availableSize.Width)
|
||||||
|
? double.PositiveInfinity
|
||||||
|
: availableSize.Width / MaxDivision;
|
||||||
|
|
||||||
|
foreach (Control control in Children.OfType<Control>())
|
||||||
{
|
{
|
||||||
int GetXS(Control control) => GetExtraSmallOffset(control) is 0 ? 0 : GetExtraSmallOffset(control);
|
if (control.IsVisible)
|
||||||
int GetSM(Control control) => GetSmallOffset(control) is 0 ? GetXS(control) : GetSmallOffset(control);
|
|
||||||
int GetMD(Control control) => GetMediumOffset(control) is 0 ? GetSM(control) : GetMediumOffset(control);
|
|
||||||
int GetLG(Control control) => GetLargeOffset(control) is 0 ? GetMD(control) : GetLargeOffset(control);
|
|
||||||
|
|
||||||
int span = width < Thresholds.ExtraSmallToSmall ? GetXS(control) : width < Thresholds.SmallToMedium ?
|
|
||||||
GetSM(control) : width < Thresholds.MediumToLarge ? GetMD(control) : GetLG(control);
|
|
||||||
return Math.Min(span, MaxDivision);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int GetPull(Control control, double width)
|
|
||||||
{
|
|
||||||
int GetXS(Control control) => GetExtraSmallPull(control) is 0 ? 0 : GetExtraSmallPull(control);
|
|
||||||
int GetSM(Control control) => GetSmallPull(control) is 0 ? GetXS(control) : GetSmallPull(control);
|
|
||||||
int GetMD(Control control) => GetMediumPull(control) is 0 ? GetSM(control) : GetMediumPull(control);
|
|
||||||
int GetLG(Control control) => GetLargePull(control) is 0 ? GetMD(control) : GetLargePull(control);
|
|
||||||
|
|
||||||
int span = width < Thresholds.ExtraSmallToSmall ? GetXS(control) : width < Thresholds.SmallToMedium ?
|
|
||||||
GetSM(control) : width < Thresholds.MediumToLarge ? GetMD(control) : GetLG(control);
|
|
||||||
return Math.Min(span, MaxDivision);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int GetPush(Control control, double width)
|
|
||||||
{
|
|
||||||
int GetXS(Control control) => GetExtraSmallPush(control) is 0 ? 0 : GetExtraSmallPush(control);
|
|
||||||
int GetSM(Control control) => GetSmallPush(control) is 0 ? GetXS(control) : GetSmallPush(control);
|
|
||||||
int GetMD(Control control) => GetMediumPush(control) is 0 ? GetSM(control) : GetMediumPush(control);
|
|
||||||
int GetLG(Control control) => GetLargePush(control) is 0 ? GetMD(control) : GetLargePush(control);
|
|
||||||
|
|
||||||
int span = width < Thresholds.ExtraSmallToSmall ? GetXS(control) : width < Thresholds.SmallToMedium ?
|
|
||||||
GetSM(control) : width < Thresholds.MediumToLarge ? GetMD(control) : GetLG(control);
|
|
||||||
|
|
||||||
return Math.Min(span, MaxDivision);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int GetSpan(Control control, double width)
|
|
||||||
{
|
|
||||||
int GetXS(Control control) => GetExtraSmall(control) is 0 ? MaxDivision : GetExtraSmall(control);
|
|
||||||
int GetSM(Control control) => GetSmall(control) is 0 ? GetXS(control) : GetSmall(control);
|
|
||||||
int GetMD(Control control) => GetMedium(control) is 0 ? GetSM(control) : GetMedium(control);
|
|
||||||
int GetLG(Control control) => GetLarge(control) is 0 ? GetMD(control) : GetLarge(control);
|
|
||||||
|
|
||||||
int span = width < Thresholds.ExtraSmallToSmall ? GetXS(control) : width < Thresholds.SmallToMedium ?
|
|
||||||
GetSM(control) : width < Thresholds.MediumToLarge ? GetMD(control) : GetLG(control);
|
|
||||||
|
|
||||||
return Math.Min(Math.Max(0, span), MaxDivision); ;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Size MeasureOverride(Size availableSize)
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
int currentRow = 0;
|
|
||||||
|
|
||||||
double availableWidth = double.IsPositiveInfinity(availableSize.Width)
|
|
||||||
? double.PositiveInfinity
|
|
||||||
: availableSize.Width / MaxDivision;
|
|
||||||
|
|
||||||
foreach (Control control in Children.OfType<Control>())
|
|
||||||
{
|
{
|
||||||
if (control.IsVisible)
|
int span = GetSpan(control, availableSize.Width);
|
||||||
|
int offset = GetOffset(control, availableSize.Width);
|
||||||
|
int push = GetPush(control, availableSize.Width);
|
||||||
|
int pull = GetPull(control, availableSize.Width);
|
||||||
|
|
||||||
|
if (count + span + offset > MaxDivision)
|
||||||
{
|
{
|
||||||
int span = GetSpan(control, availableSize.Width);
|
currentRow++;
|
||||||
int offset = GetOffset(control, availableSize.Width);
|
count = 0;
|
||||||
int push = GetPush(control, availableSize.Width);
|
|
||||||
int pull = GetPull(control, availableSize.Width);
|
|
||||||
|
|
||||||
if (count + span + offset > MaxDivision)
|
|
||||||
{
|
|
||||||
currentRow++;
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetActualColumn(control, count + offset + push - pull);
|
|
||||||
SetActualRow(control, currentRow);
|
|
||||||
|
|
||||||
count += span + offset;
|
|
||||||
|
|
||||||
Size size = new(availableWidth * span, double.PositiveInfinity);
|
|
||||||
control.Measure(size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetActualColumn(control, count + offset + push - pull);
|
||||||
|
SetActualRow(control, currentRow);
|
||||||
|
|
||||||
|
count += span + offset;
|
||||||
|
|
||||||
|
Size size = new(availableWidth * span, double.PositiveInfinity);
|
||||||
|
control.Measure(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<IGrouping<int, Control>> groupedRows = Children.OfType<Control>().GroupBy(GetActualRow);
|
|
||||||
|
|
||||||
Size totalSize = new(groupedRows.Max(rows => rows.Sum(control => control.DesiredSize.Width)),
|
|
||||||
groupedRows.Sum(rows => rows.Max(control => control.DesiredSize.Height)));
|
|
||||||
|
|
||||||
return totalSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IEnumerable<IGrouping<int, Control>> groupedRows = Children.OfType<Control>().GroupBy(GetActualRow);
|
||||||
|
|
||||||
|
Size totalSize = new(groupedRows.Max(rows => rows.Sum(control => control.DesiredSize.Width)),
|
||||||
|
groupedRows.Sum(rows => rows.Max(control => control.DesiredSize.Height)));
|
||||||
|
|
||||||
|
return totalSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,6 +69,7 @@
|
|||||||
<MergeResourceInclude Source="../Overflow/Overflow.axaml" />
|
<MergeResourceInclude Source="../Overflow/Overflow.axaml" />
|
||||||
<MergeResourceInclude Source="../ContentBadge/ContentBadge.axaml" />
|
<MergeResourceInclude Source="../ContentBadge/ContentBadge.axaml" />
|
||||||
<MergeResourceInclude Source="../TabStrip/TabStrip.axaml" />
|
<MergeResourceInclude Source="../TabStrip/TabStrip.axaml" />
|
||||||
|
<MergeResourceInclude Source="../ImageCropper/ImageCropper.axaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Styles.Resources>
|
</Styles.Resources>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Avalonia" Version="11.2.0-beta2" />
|
<PackageReference Include="Avalonia" Version="11.2.0-beta2" />
|
||||||
|
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.1.0.4" />
|
||||||
<PackageReference Include="Avalonia.Labs.Controls" Version="11.1.0" />
|
<PackageReference Include="Avalonia.Labs.Controls" Version="11.1.0" />
|
||||||
<PackageReference Include="FluentAvaloniaUI" Version="2.1.0" />
|
<PackageReference Include="FluentAvaloniaUI" Version="2.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user