diff --git a/Toolkit.Foundation/DirectoryObserver.cs b/Toolkit.Foundation/DirectoryObserver.cs index 188f1f1..7ea8b87 100644 --- a/Toolkit.Foundation/DirectoryObserver.cs +++ b/Toolkit.Foundation/DirectoryObserver.cs @@ -1,21 +1,22 @@ using System.Collections.Concurrent; +using System.Text.RegularExpressions; namespace Toolkit.Foundation; public class DirectoryObserver { public static async Task EnumerateFiles(string path, - string[] filter, + string[] patterns, int count, CancellationToken cancellationToken = default) { string[] files = []; - HashSet extensions = filter.Select(x => $".{x.ToLower()}").ToHashSet(); + List regexPatterns = patterns.Select(pattern => new Regex(pattern, RegexOptions.IgnoreCase)).ToList(); bool IsBatchComplete() { 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(); if (files.Length != count) @@ -24,7 +25,8 @@ public class DirectoryObserver } ConcurrentBag fileAccessResults = []; - Parallel.ForEach(files, (file) => + + Parallel.ForEach(files, file => { try { diff --git a/Toolkit.UI.Controls.Avalonia/ImageCropper/ImageCropper.axaml b/Toolkit.UI.Controls.Avalonia/ImageCropper/ImageCropper.axaml new file mode 100644 index 0000000..fb06376 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/ImageCropper/ImageCropper.axaml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Toolkit.UI.Controls.Avalonia/ImageCropper/ImageCropper.cs b/Toolkit.UI.Controls.Avalonia/ImageCropper/ImageCropper.cs new file mode 100644 index 0000000..e85a724 --- /dev/null +++ b/Toolkit.UI.Controls.Avalonia/ImageCropper/ImageCropper.cs @@ -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 CurrentAreaBitmapProperty = + AvaloniaProperty.Register(nameof(CurrentAreaBitmap)); + + public static readonly StyledProperty CurrentRectProperty = + AvaloniaProperty.Register(nameof(CurrentRect)); + + public static readonly StyledProperty IsRatioScaleProperty = + AvaloniaProperty.Register(nameof(IsRatioScale)); + + public static readonly StyledProperty RectScaleProperty = + AvaloniaProperty.Register(nameof(RectScale), 0.5); + + public static readonly StyledProperty ScaleSizeProperty = + AvaloniaProperty.Register(nameof(ScaleSize), new Size(2, 1)); + + public static readonly StyledProperty SourceProperty = + AvaloniaProperty.Register(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(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"); + rectangleLeft = args.NameScope.Find("RectangleLeft"); + rectangleTop = args.NameScope.Find("RectangleTop"); + rectangleRight = args.NameScope.Find("RectangleRight"); + rectangleBottom = args.NameScope.Find("RectangleBottom"); + border = args.NameScope.Find("Border"); + + topLeftButton = args.NameScope.Find("TopLeftButton"); + if (topLeftButton is not null) + { + topLeftButton.DragDelta += OnThumbDragDelta; + } + + topRightButton = args.NameScope.Find("TopRightButton"); + if (topRightButton is not null) + { + topRightButton.DragDelta += OnThumbDragDelta; + } + + bottomLeftButton = args.NameScope.Find("BottomLeftButton"); + if (bottomLeftButton is not null) + { + bottomLeftButton.DragDelta += OnThumbDragDelta; + } + + bottomRightButton = args.NameScope.Find("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; + } +} + diff --git a/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/ResponsiveGrid.cs b/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/ResponsiveGrid.cs index b447a2f..29b6b4c 100644 --- a/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/ResponsiveGrid.cs +++ b/Toolkit.UI.Controls.Avalonia/ResponsiveGrid/ResponsiveGrid.cs @@ -1,336 +1,335 @@ using Avalonia; 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 ActualColumnProperty = + AvaloniaProperty.RegisterAttached("ActualColumn", 0); + + public static readonly AvaloniaProperty ActualRowProperty = + AvaloniaProperty.RegisterAttached("ActualRow", 0); + + public static readonly StyledProperty BreakPointsProperty = + AvaloniaProperty.Register(nameof(Thresholds)); + + public static readonly AvaloniaProperty LargeOffsetProperty = + AvaloniaProperty.RegisterAttached("LargeOffset", 0); + + public static readonly AvaloniaProperty LargePullProperty = + AvaloniaProperty.RegisterAttached("LargePull", 0); + + public static readonly AvaloniaProperty LargePushProperty = + AvaloniaProperty.RegisterAttached("LargePush", 0); + + public static readonly AvaloniaProperty LargeProperty = + AvaloniaProperty.RegisterAttached("Large", 0); + + public static readonly StyledProperty MaxDivisionProperty = + AvaloniaProperty.Register(nameof(MaxDivision), 12); + + public static readonly AvaloniaProperty MediumOffsetProperty = + AvaloniaProperty.RegisterAttached("MediumOffset", 0); + + public static readonly AvaloniaProperty MediumPullProperty = + AvaloniaProperty.RegisterAttached("MediumPull", 0); + + public static readonly AvaloniaProperty MediumPushProperty = + AvaloniaProperty.RegisterAttached("MediumPush", 0); + + public static readonly AvaloniaProperty MediumProperty = + AvaloniaProperty.RegisterAttached("Medium", 0); + + public static readonly AvaloniaProperty SmallOffsetProperty = + AvaloniaProperty.RegisterAttached("SmallOffset", 0); + + public static readonly AvaloniaProperty SmallPullProperty = + AvaloniaProperty.RegisterAttached("SmallPull", 0); + + public static readonly AvaloniaProperty SmallPushProperty = + AvaloniaProperty.RegisterAttached("SmallPush", 0); + + public static readonly AvaloniaProperty SmallProperty = + AvaloniaProperty.RegisterAttached("Small", 0); + + public static readonly AvaloniaProperty ExtraSmallOffsetProperty = + AvaloniaProperty.RegisterAttached("ExtraSmallOffset", 0); + + public static readonly AvaloniaProperty ExtraSmallPullProperty = + AvaloniaProperty.RegisterAttached("ExtraSmallPull", 0); + + public static readonly AvaloniaProperty ExtraSmallPushProperty = + AvaloniaProperty.RegisterAttached("ExtraSmallPush", 0); + + public static readonly AvaloniaProperty ExtraSmallProperty = + AvaloniaProperty.RegisterAttached("ExtraSmall", 0); + + static ResponsiveGrid() { - public static readonly AvaloniaProperty ActualColumnProperty = - AvaloniaProperty.RegisterAttached("ActualColumn", 0); + AffectsMeasure( + MaxDivisionProperty, + BreakPointsProperty, + LargeProperty, + MediumProperty, + SmallProperty, + ExtraSmallProperty, + LargeOffsetProperty, + LargePullProperty, + LargePushProperty, + MediumOffsetProperty, + MediumPullProperty, + MediumPushProperty, + SmallOffsetProperty, + SmallPullProperty, + SmallPushProperty, + ExtraSmallOffsetProperty, + ExtraSmallPullProperty, + ExtraSmallPushProperty + ); + } - public static readonly AvaloniaProperty ActualRowProperty = - AvaloniaProperty.RegisterAttached("ActualRow", 0); + public ResponsiveGrid() + { + MaxDivision = 12; + Thresholds = new SizeThresholds(); + } - public static readonly StyledProperty BreakPointsProperty = - AvaloniaProperty.Register(nameof(Thresholds)); + public int MaxDivision + { + get => GetValue(MaxDivisionProperty); + set => SetValue(MaxDivisionProperty, value); + } - public static readonly AvaloniaProperty LargeOffsetProperty = - AvaloniaProperty.RegisterAttached("LargeOffset", 0); + public SizeThresholds Thresholds + { + get => GetValue(BreakPointsProperty); + set => SetValue(BreakPointsProperty, value); + } - public static readonly AvaloniaProperty LargePullProperty = - AvaloniaProperty.RegisterAttached("LargePull", 0); + public static int GetActualColumn(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(ActualColumnProperty); - public static readonly AvaloniaProperty LargePushProperty = - AvaloniaProperty.RegisterAttached("LargePush", 0); + public static int GetActualRow(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(ActualRowProperty); - public static readonly AvaloniaProperty LargeProperty = - AvaloniaProperty.RegisterAttached("Large", 0); + public static int GetLarge(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(LargeProperty); - public static readonly StyledProperty MaxDivisionProperty = - AvaloniaProperty.Register(nameof(MaxDivision), 12); + public static int GetLargeOffset(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(LargeOffsetProperty); - public static readonly AvaloniaProperty MediumOffsetProperty = - AvaloniaProperty.RegisterAttached("MediumOffset", 0); + public static int GetLargePull(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(LargePullProperty); - public static readonly AvaloniaProperty MediumPullProperty = - AvaloniaProperty.RegisterAttached("MediumPull", 0); + public static int GetLargePush(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(LargePushProperty); - public static readonly AvaloniaProperty MediumPushProperty = - AvaloniaProperty.RegisterAttached("MediumPush", 0); + public static int GetMedium(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(MediumProperty); - public static readonly AvaloniaProperty MediumProperty = - AvaloniaProperty.RegisterAttached("Medium", 0); + public static int GetMediumOffset(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(MediumOffsetProperty); - public static readonly AvaloniaProperty SmallOffsetProperty = - AvaloniaProperty.RegisterAttached("SmallOffset", 0); + public static int GetMediumPull(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(MediumPullProperty); - public static readonly AvaloniaProperty SmallPullProperty = - AvaloniaProperty.RegisterAttached("SmallPull", 0); + public static int GetMediumPush(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(MediumPushProperty); - public static readonly AvaloniaProperty SmallPushProperty = - AvaloniaProperty.RegisterAttached("SmallPush", 0); + public static int GetSmall(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(SmallProperty); - public static readonly AvaloniaProperty SmallProperty = - AvaloniaProperty.RegisterAttached("Small", 0); + public static int GetSmallOffset(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(SmallOffsetProperty); - public static readonly AvaloniaProperty ExtraSmallOffsetProperty = - AvaloniaProperty.RegisterAttached("ExtraSmallOffset", 0); + public static int GetSmallPull(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(SmallPullProperty); - public static readonly AvaloniaProperty ExtraSmallPullProperty = - AvaloniaProperty.RegisterAttached("ExtraSmallPull", 0); + public static int GetSmallPush(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(SmallPushProperty); - public static readonly AvaloniaProperty ExtraSmallPushProperty = - AvaloniaProperty.RegisterAttached("ExtraSmallPush", 0); + public static int GetExtraSmall(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(ExtraSmallProperty); - public static readonly AvaloniaProperty ExtraSmallProperty = - AvaloniaProperty.RegisterAttached("ExtraSmall", 0); + public static int GetExtraSmallOffset(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(ExtraSmallOffsetProperty); - static ResponsiveGrid() + public static int GetExtraSmallPull(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(ExtraSmallPullProperty); + + public static int GetExtraSmallPush(AvaloniaObject avaloniaObject) => + avaloniaObject.GetValue(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> groupedRows = Children.OfType().GroupBy(GetActualRow); + + double yOffset = 0; + foreach (IGrouping row in groupedRows) { - AffectsMeasure( - MaxDivisionProperty, - BreakPointsProperty, - LargeProperty, - MediumProperty, - SmallProperty, - ExtraSmallProperty, - LargeOffsetProperty, - LargePullProperty, - LargePushProperty, - MediumOffsetProperty, - MediumPullProperty, - MediumPushProperty, - SmallOffsetProperty, - SmallPullProperty, - SmallPushProperty, - ExtraSmallOffsetProperty, - ExtraSmallPullProperty, - ExtraSmallPushProperty - ); - } + double maxRowHeight = row.Max(control => control.DesiredSize.Height); - public ResponsiveGrid() - { - 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(ActualColumnProperty); - - public static int GetActualRow(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(ActualRowProperty); - - public static int GetLarge(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(LargeProperty); - - public static int GetLargeOffset(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(LargeOffsetProperty); - - public static int GetLargePull(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(LargePullProperty); - - public static int GetLargePush(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(LargePushProperty); - - public static int GetMedium(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(MediumProperty); - - public static int GetMediumOffset(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(MediumOffsetProperty); - - public static int GetMediumPull(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(MediumPullProperty); - - public static int GetMediumPush(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(MediumPushProperty); - - public static int GetSmall(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(SmallProperty); - - public static int GetSmallOffset(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(SmallOffsetProperty); - - public static int GetSmallPull(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(SmallPullProperty); - - public static int GetSmallPush(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(SmallPushProperty); - - public static int GetExtraSmall(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(ExtraSmallProperty); - - public static int GetExtraSmallOffset(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(ExtraSmallOffsetProperty); - - public static int GetExtraSmallPull(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(ExtraSmallPullProperty); - - public static int GetExtraSmallPush(AvaloniaObject avaloniaObject) => - avaloniaObject.GetValue(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> groupedRows = Children.OfType().GroupBy(GetActualRow); - - double yOffset = 0; - foreach (IGrouping row in groupedRows) + foreach (Control element in row) { - double maxRowHeight = row.Max(control => control.DesiredSize.Height); + int column = GetActualColumn(element); + int span = GetSpan(element, finalSize.Width); - foreach (Control element in row) - { - int column = GetActualColumn(element); - int span = GetSpan(element, finalSize.Width); - - Rect rect = new(column * columnWidth, yOffset, span * columnWidth, maxRowHeight); - element.Arrange(rect); - } - - yOffset += maxRowHeight; + Rect rect = new(column * columnWidth, yOffset, span * columnWidth, maxRowHeight); + element.Arrange(rect); } - 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()) { - 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()) + if (control.IsVisible) { - 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); - int offset = GetOffset(control, availableSize.Width); - 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); + 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); } - - IEnumerable> groupedRows = Children.OfType().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> groupedRows = Children.OfType().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; } } \ No newline at end of file diff --git a/Toolkit.UI.Controls.Avalonia/Themes/ControlResources.axaml b/Toolkit.UI.Controls.Avalonia/Themes/ControlResources.axaml index dd64fc3..d337845 100644 --- a/Toolkit.UI.Controls.Avalonia/Themes/ControlResources.axaml +++ b/Toolkit.UI.Controls.Avalonia/Themes/ControlResources.axaml @@ -69,6 +69,7 @@ + diff --git a/Toolkit.UI.Controls.Avalonia/Toolkit.UI.Controls.Avalonia.csproj b/Toolkit.UI.Controls.Avalonia/Toolkit.UI.Controls.Avalonia.csproj index 0897790..64e67c3 100644 --- a/Toolkit.UI.Controls.Avalonia/Toolkit.UI.Controls.Avalonia.csproj +++ b/Toolkit.UI.Controls.Avalonia/Toolkit.UI.Controls.Avalonia.csproj @@ -13,6 +13,7 @@ +