/
githubmirror
/
PowerToys
Обзор
Документация
Войти
/
githubmirror
/
PowerToys
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/WrapPanelCustom/WrapPanel.cs
416 строк
14 KB
Michael Jolley
CmdPal: Removing Core projects (#45693)
23 фев 2026, 15:05
Не верифицирован
23 фев 2026, 15:05
138c66c
Код
Авторство
О чём код?
// Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using CommunityToolkit.WinUI.Controls; using Microsoft.CmdPal.UI.ViewModels; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Windows.Foundation; using ToolkitStretchChild = CommunityToolkit.WinUI.Controls.StretchChild; namespace Microsoft.CmdPal.UI.Controls; /// <summary> /// Arranges elements by wrapping them to fit the available space. /// When <see cref="Orientation"/> is set to Orientation.Horizontal, element are arranged in rows until the available width is reached and then to a new row. /// When <see cref="Orientation"/> is set to Orientation.Vertical, element are arranged in columns until the available height is reached. /// </summary> public sealed partial class WrapPanel : Panel { private struct UvRect { public UvMeasure Position { get; set; } public UvMeasure Size { get; set; } public Rect ToRect(Orientation orientation) { return orientation switch { Orientation.Vertical => new Rect(Position.V, Position.U, Size.V, Size.U), Orientation.Horizontal => new Rect(Position.U, Position.V, Size.U, Size.V), _ => ThrowArgumentException(), }; } private static Rect ThrowArgumentException() { throw new ArgumentException("The input orientation is not valid."); } } private struct Row { public List<UvRect> ChildrenRects { get; } public UvMeasure Size { get; set; } public UvRect Rect { get { UvRect result; if (ChildrenRects.Count <= 0) { result = default(UvRect); result.Position = UvMeasure.Zero; result.Size = Size; return result; } result = default(UvRect); result.Position = ChildrenRects.First().Position; result.Size = Size; return result; } } public Row(List<UvRect> childrenRects, UvMeasure size) { ChildrenRects = childrenRects; Size = size; } public void Add(UvMeasure position, UvMeasure size) { ChildrenRects.Add(new UvRect { Position = position, Size = size, }); Size = new UvMeasure { U = position.U + size.U, V = Math.Max(Size.V, size.V), }; } } /// <summary> /// Gets or sets a uniform Horizontal distance (in pixels) between items when <see cref="Orientation"/> is set to Horizontal, /// or between columns of items when <see cref="Orientation"/> is set to Vertical. /// </summary> public double HorizontalSpacing { get { return (double)GetValue(HorizontalSpacingProperty); } set { SetValue(HorizontalSpacingProperty, value); } } /// <summary> /// Identifies the <see cref="HorizontalSpacing"/> dependency property. /// </summary> public static readonly DependencyProperty HorizontalSpacingProperty = DependencyProperty.Register( nameof(HorizontalSpacing), typeof(double), typeof(WrapPanel), new PropertyMetadata(0d, LayoutPropertyChanged)); /// <summary> /// Gets or sets a uniform Vertical distance (in pixels) between items when <see cref="Orientation"/> is set to Vertical, /// or between rows of items when <see cref="Orientation"/> is set to Horizontal. /// </summary> public double VerticalSpacing { get { return (double)GetValue(VerticalSpacingProperty); } set { SetValue(VerticalSpacingProperty, value); } } /// <summary> /// Identifies the <see cref="VerticalSpacing"/> dependency property. /// </summary> public static readonly DependencyProperty VerticalSpacingProperty = DependencyProperty.Register( nameof(VerticalSpacing), typeof(double), typeof(WrapPanel), new PropertyMetadata(0d, LayoutPropertyChanged)); /// <summary> /// Gets or sets the orientation of the WrapPanel. /// Horizontal means that child controls will be added horizontally until the width of the panel is reached, then a new row is added to add new child controls. /// Vertical means that children will be added vertically until the height of the panel is reached, then a new column is added. /// </summary> public Orientation Orientation { get { return (Orientation)GetValue(OrientationProperty); } set { SetValue(OrientationProperty, value); } } /// <summary> /// Identifies the <see cref="Orientation"/> dependency property. /// </summary> public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register( nameof(Orientation), typeof(Orientation), typeof(WrapPanel), new PropertyMetadata(Orientation.Horizontal, LayoutPropertyChanged)); /// <summary> /// Gets or sets the distance between the border and its child object. /// </summary> /// <returns> /// The dimensions of the space between the border and its child as a Thickness value. /// Thickness is a structure that stores dimension values using pixel measures. /// </returns> public Thickness Padding { get { return (Thickness)GetValue(PaddingProperty); } set { SetValue(PaddingProperty, value); } } /// <summary> /// Identifies the Padding dependency property. /// </summary> /// <returns>The identifier for the <see cref="Padding"/> dependency property.</returns> public static readonly DependencyProperty PaddingProperty = DependencyProperty.Register( nameof(Padding), typeof(Thickness), typeof(WrapPanel), new PropertyMetadata(default(Thickness), LayoutPropertyChanged)); /// <summary> /// Gets or sets a value indicating how to arrange child items /// </summary> public ToolkitStretchChild StretchChild { get { return (ToolkitStretchChild)GetValue(StretchChildProperty); } set { SetValue(StretchChildProperty, value); } } /// <summary> /// Identifies the <see cref="StretchChild"/> dependency property. /// </summary> /// <returns>The identifier for the <see cref="StretchChild"/> dependency property.</returns> public static readonly DependencyProperty StretchChildProperty = DependencyProperty.Register( nameof(StretchChild), typeof(ToolkitStretchChild), typeof(WrapPanel), new PropertyMetadata(ToolkitStretchChild.None, LayoutPropertyChanged)); /// <summary> /// Identifies the IsFullLine attached dependency property. /// If true, the child element will occupy the entire width of the panel and force a line break before and after itself. /// </summary> public static readonly DependencyProperty IsFullLineProperty = DependencyProperty.RegisterAttached( "IsFullLine", typeof(bool), typeof(WrapPanel), new PropertyMetadata(false, OnIsFullLineChanged)); public static bool GetIsFullLine(DependencyObject obj) { return (bool)obj.GetValue(IsFullLineProperty); } public static void SetIsFullLine(DependencyObject obj, bool value) { obj.SetValue(IsFullLineProperty, value); } private static void OnIsFullLineChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (FindVisualParentWrapPanel(d) is WrapPanel wp) { wp.InvalidateMeasure(); } } private static WrapPanel? FindVisualParentWrapPanel(DependencyObject child) { var parent = Microsoft.UI.Xaml.Media.VisualTreeHelper.GetParent(child); while (parent != null) { if (parent is WrapPanel wrapPanel) { return wrapPanel; } parent = Microsoft.UI.Xaml.Media.VisualTreeHelper.GetParent(parent); } return null; } private static void LayoutPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is WrapPanel wp) { wp.InvalidateMeasure(); wp.InvalidateArrange(); } } private readonly List<Row> _rows = new List<Row>(); /// <inheritdoc /> protected override Size MeasureOverride(Size availableSize) { var childAvailableSize = new Size( availableSize.Width - Padding.Left - Padding.Right, availableSize.Height - Padding.Top - Padding.Bottom); foreach (var child in Children) { child.Measure(childAvailableSize); } var requiredSize = UpdateRows(availableSize); return requiredSize; } /// <inheritdoc /> protected override Size ArrangeOverride(Size finalSize) { if ((Orientation == Orientation.Horizontal && finalSize.Width < DesiredSize.Width) || (Orientation == Orientation.Vertical && finalSize.Height < DesiredSize.Height)) { // We haven't received our desired size. We need to refresh the rows. UpdateRows(finalSize); } if (_rows.Count > 0) { // Now that we have all the data, we do the actual arrange pass var childIndex = 0; foreach (var row in _rows) { foreach (var rect in row.ChildrenRects) { var child = Children[childIndex++]; while (child.Visibility == Visibility.Collapsed) { // Collapsed children are not added into the rows, // we skip them. child = Children[childIndex++]; } var arrangeRect = new UvRect { Position = rect.Position, Size = new UvMeasure { U = rect.Size.U, V = row.Size.V }, }; var finalRect = arrangeRect.ToRect(Orientation); child.Arrange(finalRect); } } } return finalSize; } private Size UpdateRows(Size availableSize) { _rows.Clear(); var paddingStart = new UvMeasure(Orientation, Padding.Left, Padding.Top); var paddingEnd = new UvMeasure(Orientation, Padding.Right, Padding.Bottom); if (Children.Count == 0) { return paddingStart.Add(paddingEnd).ToSize(Orientation); } var parentMeasure = new UvMeasure(Orientation, availableSize.Width, availableSize.Height); var spacingMeasure = new UvMeasure(Orientation, HorizontalSpacing, VerticalSpacing); var position = new UvMeasure(Orientation, Padding.Left, Padding.Top); var currentRow = new Row(new List<UvRect>(), default); var finalMeasure = new UvMeasure(Orientation, width: 0.0, height: 0.0); void CommitRow() { // Only adds if the row has a content if (currentRow.ChildrenRects.Count > 0) { _rows.Add(currentRow); position.V += currentRow.Size.V + spacingMeasure.V; } position.U = paddingStart.U; currentRow = new Row(new List<UvRect>(), default); } void Arrange(UIElement child, bool isLast = false) { if (child.Visibility == Visibility.Collapsed) { return; } var isFullLine = GetIsFullLine(child); var desiredMeasure = new UvMeasure(Orientation, child.DesiredSize); if (isFullLine) { if (currentRow.ChildrenRects.Count > 0) { CommitRow(); } // Forces the width to fill all the available space // (Total width - Padding Left - Padding Right) desiredMeasure.U = parentMeasure.U - paddingStart.U - paddingEnd.U; // Adds the Section Header to the row currentRow.Add(position, desiredMeasure); // Updates the global measures position.U += desiredMeasure.U + spacingMeasure.U; finalMeasure.U = Math.Max(finalMeasure.U, position.U); CommitRow(); } else { // Checks if the item can fit in the row if ((desiredMeasure.U + position.U + paddingEnd.U) > parentMeasure.U) { CommitRow(); } if (isLast) { desiredMeasure.U = parentMeasure.U - position.U; } currentRow.Add(position, desiredMeasure); position.U += desiredMeasure.U + spacingMeasure.U; finalMeasure.U = Math.Max(finalMeasure.U, position.U); } } var lastIndex = Children.Count - 1; for (var i = 0; i < lastIndex; i++) { Arrange(Children[i]); } Arrange(Children[lastIndex], StretchChild == ToolkitStretchChild.Last); if (currentRow.ChildrenRects.Count > 0) { _rows.Add(currentRow); } if (_rows.Count == 0) { return paddingStart.Add(paddingEnd).ToSize(Orientation); } var lastRowRect = _rows.Last().Rect; finalMeasure.V = lastRowRect.Position.V + lastRowRect.Size.V; return finalMeasure.Add(paddingEnd).ToSize(Orientation); } }