diff --git a/WinUIGallery/Assets/ControlImages/WrapPanel.png b/WinUIGallery/Assets/ControlImages/WrapPanel.png new file mode 100644 index 000000000..24f9ea56e Binary files /dev/null and b/WinUIGallery/Assets/ControlImages/WrapPanel.png differ diff --git a/WinUIGallery/ContentIncludes.props b/WinUIGallery/ContentIncludes.props index 75241219b..35611aa1a 100644 --- a/WinUIGallery/ContentIncludes.props +++ b/WinUIGallery/ContentIncludes.props @@ -238,6 +238,9 @@ + + + diff --git a/WinUIGallery/Layouts/WrapPanel.cs b/WinUIGallery/Layouts/WrapPanel.cs deleted file mode 100644 index 1b54f491a..000000000 --- a/WinUIGallery/Layouts/WrapPanel.cs +++ /dev/null @@ -1,692 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using System; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Runtime.InteropServices; -using Windows.Foundation; - -namespace WinUIGallery.Layouts; - -/// -/// Positions child elements sequentially from left to right or top to -/// bottom. When elements extend beyond the panel edge, elements are -/// positioned in the next row or column. -/// -/// Mature -public partial class WrapPanel : Panel -{ - /// - /// A value indicating whether a dependency property change handler - /// should ignore the next change notification. This is used to reset - /// the value of properties without performing any of the actions in - /// their change handlers. - /// - private bool _ignorePropertyChange; - - #region public double ItemHeight - /// - /// Gets or sets the height of the layout area for each item that is - /// contained in a . - /// - /// - /// The height applied to the layout area of each item that is contained - /// within a . The - /// default value is . - /// - public double ItemHeight - { - get { return (double)GetValue(ItemHeightProperty); } - set { SetValue(ItemHeightProperty, value); } - } - - /// - /// Identifies the - /// - /// dependency property. - /// - /// - /// The identifier for the - /// - /// dependency property - /// - public static readonly DependencyProperty ItemHeightProperty = - DependencyProperty.Register( - "ItemHeight", - typeof(double), - typeof(WrapPanel), - new PropertyMetadata(double.NaN, OnItemHeightOrWidthPropertyChanged)); - #endregion public double ItemHeight - - #region public double ItemWidth - /// - /// Gets or sets the width of the layout area for each item that is - /// contained in a . - /// - /// - /// The width that applies to the layout area of each item that is - /// contained in a . - /// The default value is . - /// - public double ItemWidth - { - get { return (double)GetValue(ItemWidthProperty); } - set { SetValue(ItemWidthProperty, value); } - } - - /// - /// Identifies the - /// - /// dependency property. - /// - /// - /// The identifier for the - /// - /// dependency property. - /// - public static readonly DependencyProperty ItemWidthProperty = - DependencyProperty.Register( - "ItemWidth", - typeof(double), - typeof(WrapPanel), - new PropertyMetadata(double.NaN, OnItemHeightOrWidthPropertyChanged)); - #endregion public double ItemWidth - - #region public Orientation Orientation - /// - /// Gets or sets the direction in which child elements are arranged. - /// - /// - /// One of the - /// values. The default is - /// . - /// - public Orientation Orientation - { - get { return (Orientation)GetValue(OrientationProperty); } - set { SetValue(OrientationProperty, value); } - } - - /// - /// Identifies the - /// - /// dependency property. - /// - /// - /// The identifier for the - /// - /// dependency property. - /// - public static readonly DependencyProperty OrientationProperty = - DependencyProperty.Register( - "Orientation", - typeof(Orientation), - typeof(WrapPanel), - new PropertyMetadata(Orientation.Horizontal, OnOrientationPropertyChanged)); - - /// - /// OrientationProperty property changed handler. - /// - /// WrapPanel that changed its Orientation. - /// Event arguments. - [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "Almost always set from the CLR property.")] - private static void OnOrientationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - WrapPanel source = (WrapPanel)d; - Orientation value = (Orientation)e.NewValue; - - // Ignore the change if requested - if (source._ignorePropertyChange) - { - source._ignorePropertyChange = false; - return; - } - - // Validate the Orientation - if ((value != Orientation.Horizontal) && - (value != Orientation.Vertical)) - { - // Reset the property to its original state before throwing - source._ignorePropertyChange = true; - source.SetValue(OrientationProperty, (Orientation)e.OldValue); - - string message = string.Format( - CultureInfo.InvariantCulture, - "Properties.Resources.WrapPanel_OnOrientationPropertyChanged_InvalidValue", - value); - throw new ArgumentException(message, "value"); - } - - // Orientation affects measuring. - source.InvalidateMeasure(); - } - #endregion public Orientation Orientation - - /// - /// Initializes a new instance of the - /// class. - /// - public WrapPanel() - { - } - - /// - /// Property changed handler for ItemHeight and ItemWidth. - /// - /// - /// WrapPanel that changed its ItemHeight or ItemWidth. - /// - /// Event arguments. - [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "Almost always set from the CLR property.")] - private static void OnItemHeightOrWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - WrapPanel source = (WrapPanel)d; - double value = (double)e.NewValue; - - // Ignore the change if requested - if (source._ignorePropertyChange) - { - source._ignorePropertyChange = false; - return; - } - - // Validate the length (which must either be NaN or a positive, - // finite number) - if (!double.IsNaN(value) && ((value <= 0.0) || double.IsPositiveInfinity(value))) - { - // Reset the property to its original state before throwing - source._ignorePropertyChange = true; - source.SetValue(e.Property, (double)e.OldValue); - - string message = string.Format( - CultureInfo.InvariantCulture, - "Properties.Resources.WrapPanel_OnItemHeightOrWidthPropertyChanged_InvalidValue", - value); - throw new ArgumentException(message, "value"); - } - - // The length properties affect measuring. - source.InvalidateMeasure(); - } - - /// - /// Measures the child elements of a - /// in anticipation - /// of arranging them during the - /// - /// pass. - /// - /// - /// The size available to child elements of the wrap panel. - /// - /// - /// The size required by the - /// and its - /// elements. - /// - [SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Justification = "Compat with WPF.")] - protected override Size MeasureOverride(Size constraint) - { - // Variables tracking the size of the current line, the total size - // measured so far, and the maximum size available to fill. Note - // that the line might represent a row or a column depending on the - // orientation. - Orientation o = Orientation; - OrientedSize lineSize = new OrientedSize(o); - OrientedSize totalSize = new OrientedSize(o); - OrientedSize maximumSize = new OrientedSize(o, constraint.Width, constraint.Height); - - // Determine the constraints for individual items - double itemWidth = ItemWidth; - double itemHeight = ItemHeight; - bool hasFixedWidth = !double.IsNaN(itemWidth); - bool hasFixedHeight = !double.IsNaN(itemHeight); - Size itemSize = new Size( - hasFixedWidth ? itemWidth : constraint.Width, - hasFixedHeight ? itemHeight : constraint.Height); - - // Measure each of the Children - foreach (UIElement element in Children) - { - // Determine the size of the element - element.Measure(itemSize); - OrientedSize elementSize = new OrientedSize( - o, - hasFixedWidth ? itemWidth : element.DesiredSize.Width, - hasFixedHeight ? itemHeight : element.DesiredSize.Height); - - // If this element falls of the edge of the line - if (NumericExtensions.IsGreaterThan(lineSize.Direct + elementSize.Direct, maximumSize.Direct)) - { - // Update the total size with the direct and indirect growth - // for the current line - totalSize.Direct = Math.Max(lineSize.Direct, totalSize.Direct); - totalSize.Indirect += lineSize.Indirect; - - // Move the element to a new line - lineSize = elementSize; - - // If the current element is larger than the maximum size, - // place it on a line by itself - if (NumericExtensions.IsGreaterThan(elementSize.Direct, maximumSize.Direct)) - { - // Update the total size for the line occupied by this - // single element - totalSize.Direct = Math.Max(elementSize.Direct, totalSize.Direct); - totalSize.Indirect += elementSize.Indirect; - - // Move to a new line - lineSize = new OrientedSize(o); - } - } - else - { - // Otherwise just add the element to the end of the line - lineSize.Direct += elementSize.Direct; - lineSize.Indirect = Math.Max(lineSize.Indirect, elementSize.Indirect); - } - } - - // Update the total size with the elements on the last line - totalSize.Direct = Math.Max(lineSize.Direct, totalSize.Direct); - totalSize.Indirect += lineSize.Indirect; - - // Return the total size required as an un-oriented quantity - return new Size(totalSize.Width, totalSize.Height); - } - - /// - /// Arranges and sizes the - /// control and its - /// child elements. - /// - /// - /// The area within the parent that the - /// should use - /// arrange itself and its children. - /// - /// - /// The actual size used by the - /// . - /// - protected override Size ArrangeOverride(Size finalSize) - { - // Variables tracking the size of the current line, and the maximum - // size available to fill. Note that the line might represent a row - // or a column depending on the orientation. - Orientation o = Orientation; - OrientedSize lineSize = new OrientedSize(o); - OrientedSize maximumSize = new OrientedSize(o, finalSize.Width, finalSize.Height); - - // Determine the constraints for individual items - double itemWidth = ItemWidth; - double itemHeight = ItemHeight; - bool hasFixedWidth = !double.IsNaN(itemWidth); - bool hasFixedHeight = !double.IsNaN(itemHeight); - double indirectOffset = 0; - double? directDelta = (o == Orientation.Horizontal) ? - (hasFixedWidth ? (double?)itemWidth : null) : - (hasFixedHeight ? (double?)itemHeight : null); - - // Measure each of the Children. We will process the elements one - // line at a time, just like during measure, but we will wait until - // we've completed an entire line of elements before arranging them. - // The lineStart and lineEnd variables track the size of the - // currently arranged line. - UIElementCollection children = Children; - int count = children.Count; - int lineStart = 0; - for (int lineEnd = 0; lineEnd < count; lineEnd++) - { - UIElement element = children[lineEnd]; - - // Get the size of the element - OrientedSize elementSize = new OrientedSize( - o, - hasFixedWidth ? itemWidth : element.DesiredSize.Width, - hasFixedHeight ? itemHeight : element.DesiredSize.Height); - - // If this element falls of the edge of the line - if (NumericExtensions.IsGreaterThan(lineSize.Direct + elementSize.Direct, maximumSize.Direct)) - { - // Then we just completed a line and we should arrange it - ArrangeLine(lineStart, lineEnd, directDelta, indirectOffset, lineSize.Indirect); - - // Move the current element to a new line - indirectOffset += lineSize.Indirect; - lineSize = elementSize; - - // If the current element is larger than the maximum size - if (NumericExtensions.IsGreaterThan(elementSize.Direct, maximumSize.Direct)) - { - // Arrange the element as a single line - ArrangeLine(lineEnd, ++lineEnd, directDelta, indirectOffset, elementSize.Indirect); - - // Move to a new line - indirectOffset += lineSize.Indirect; - lineSize = new OrientedSize(o); - } - - // Advance the start index to a new line after arranging - lineStart = lineEnd; - } - else - { - // Otherwise just add the element to the end of the line - lineSize.Direct += elementSize.Direct; - lineSize.Indirect = Math.Max(lineSize.Indirect, elementSize.Indirect); - } - } - - // Arrange any elements on the last line - if (lineStart < count) - { - ArrangeLine(lineStart, count, directDelta, indirectOffset, lineSize.Indirect); - } - - return finalSize; - } - - /// - /// Arrange a sequence of elements in a single line. - /// - /// - /// Index of the first element in the sequence to arrange. - /// - /// - /// Index of the last element in the sequence to arrange. - /// - /// - /// Optional fixed growth in the primary direction. - /// - /// - /// Offset of the line in the indirect direction. - /// - /// - /// Shared indirect growth of the elements on this line. - /// - private void ArrangeLine(int lineStart, int lineEnd, double? directDelta, double indirectOffset, double indirectGrowth) - { - double directOffset = 0.0; - - Orientation o = Orientation; - bool isHorizontal = o == Orientation.Horizontal; - - UIElementCollection children = Children; - for (int index = lineStart; index < lineEnd; index++) - { - // Get the size of the element - UIElement element = children[index]; - OrientedSize elementSize = new OrientedSize(o, element.DesiredSize.Width, element.DesiredSize.Height); - - // Determine if we should use the element's desired size or the - // fixed item width or height - double directGrowth = directDelta != null ? - directDelta.Value : - elementSize.Direct; - - // Arrange the element - Rect bounds = isHorizontal ? - new Rect(directOffset, indirectOffset, directGrowth, indirectGrowth) : - new Rect(indirectOffset, directOffset, indirectGrowth, directGrowth); - element.Arrange(bounds); - - directOffset += directGrowth; - } - } -} - -/// -/// Numeric utility methods used by controls. These methods are similar in -/// scope to the WPF DoubleUtil class. -/// -internal static partial class NumericExtensions -{ - /// - /// NanUnion is a C++ style type union used for efficiently converting - /// a double into an unsigned long, whose bits can be easily - /// manipulated. - /// - [StructLayout(LayoutKind.Explicit)] - private struct NanUnion - { - /// - /// Floating point representation of the union. - /// - [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "It is accessed through the other member of the union")] - [FieldOffset(0)] - internal double FloatingValue; - - /// - /// Integer representation of the union. - /// - [FieldOffset(0)] - internal ulong IntegerValue; - } - -#if !WINDOWS_PHONE - /// - /// Check if a number is zero. - /// - /// The number to check. - /// True if the number is zero, false otherwise. - public static bool IsZero(this double value) - { - // We actually consider anything within an order of magnitude of - // epsilon to be zero - return Math.Abs(value) < 2.2204460492503131E-15; - } -#endif - - /// - /// Check if a number isn't really a number. - /// - /// The number to check. - /// - /// True if the number is not a number, false if it is a number. - /// - public static bool IsNaN(this double value) - { - // Get the double as an unsigned long - NanUnion union = new NanUnion { FloatingValue = value }; - - // An IEEE 754 double precision floating point number is NaN if its - // exponent equals 2047 and it has a non-zero mantissa. - ulong exponent = union.IntegerValue & 0xfff0000000000000L; - if ((exponent != 0x7ff0000000000000L) && (exponent != 0xfff0000000000000L)) - { - return false; - } - ulong mantissa = union.IntegerValue & 0x000fffffffffffffL; - return mantissa != 0L; - } - - /// - /// Determine if one number is greater than another. - /// - /// First number. - /// Second number. - /// - /// True if the first number is greater than the second, false - /// otherwise. - /// - public static bool IsGreaterThan(double left, double right) - { - return (left > right) && !AreClose(left, right); - } - - /// - /// Determine if two numbers are close in value. - /// - /// First number. - /// Second number. - /// - /// True if the first number is close in value to the second, false - /// otherwise. - /// - public static bool AreClose(double left, double right) - { - // ReSharper disable CompareOfFloatsByEqualityOperator - if (left == right) - // ReSharper restore CompareOfFloatsByEqualityOperator - { - return true; - } - - double a = (Math.Abs(left) + Math.Abs(right) + 10.0) * 2.2204460492503131E-16; - double b = left - right; - return (-a < b) && (a > b); - } - -#if !WINDOWS_PHONE - /// - /// Determine if one number is less than or close to another. - /// - /// First number. - /// Second number. - /// - /// True if the first number is less than or close to the second, false - /// otherwise. - /// - public static bool IsLessThanOrClose(double left, double right) - { - return (left < right) || AreClose(left, right); - } -#endif -} - -/// -/// The OrientedSize structure is used to abstract the growth direction from -/// the layout algorithms of WrapPanel. When the growth direction is -/// oriented horizontally (ex: the next element is arranged on the side of -/// the previous element), then the Width grows directly with the placement -/// of elements and Height grows indirectly with the size of the largest -/// element in the row. When the orientation is reversed, so is the -/// directional growth with respect to Width and Height. -/// -/// Mature -[StructLayout(LayoutKind.Sequential)] -internal struct OrientedSize -{ - /// - /// The orientation of the structure. - /// - private Orientation _orientation; - - /// - /// Gets the orientation of the structure. - /// - public Orientation Orientation - { - get { return _orientation; } - } - - /// - /// The size dimension that grows directly with layout placement. - /// - private double _direct; - - /// - /// Gets or sets the size dimension that grows directly with layout - /// placement. - /// - public double Direct - { - get { return _direct; } - set { _direct = value; } - } - - /// - /// The size dimension that grows indirectly with the maximum value of - /// the layout row or column. - /// - private double _indirect; - - /// - /// Gets or sets the size dimension that grows indirectly with the - /// maximum value of the layout row or column. - /// - public double Indirect - { - get { return _indirect; } - set { _indirect = value; } - } - - /// - /// Gets or sets the width of the size. - /// - public double Width - { - get - { - return (Orientation == Orientation.Horizontal) ? - Direct : - Indirect; - } - set - { - if (Orientation == Orientation.Horizontal) - { - Direct = value; - } - else - { - Indirect = value; - } - } - } - - /// - /// Gets or sets the height of the size. - /// - public double Height - { - get - { - return (Orientation != Orientation.Horizontal) ? - Direct : - Indirect; - } - set - { - if (Orientation != Orientation.Horizontal) - { - Direct = value; - } - else - { - Indirect = value; - } - } - } - - /// - /// Initializes a new OrientedSize structure. - /// - /// Orientation of the structure. - public OrientedSize(Orientation orientation) : - this(orientation, 0.0, 0.0) - { - } - - /// - /// Initializes a new OrientedSize structure. - /// - /// Orientation of the structure. - /// Un-oriented width of the structure. - /// Un-oriented height of the structure. - public OrientedSize(Orientation orientation, double width, double height) - { - _orientation = orientation; - - // All fields must be initialized before we access the this pointer - _direct = 0.0; - _indirect = 0.0; - - Width = width; - Height = height; - } -} diff --git a/WinUIGallery/Samples/ControlPages/ContentIslandPage.xaml b/WinUIGallery/Samples/ControlPages/ContentIslandPage.xaml index 56d0cc221..402d18334 100644 --- a/WinUIGallery/Samples/ControlPages/ContentIslandPage.xaml +++ b/WinUIGallery/Samples/ControlPages/ContentIslandPage.xaml @@ -38,7 +38,7 @@ Click="LoadModel_Click" Content="Load model" Style="{StaticResource AccentButtonStyle}" /> - @@ -54,7 +54,7 @@ Grid.Row="2" Grid.Column="1" Style="{StaticResource CardRectangleStyle}" /> - + diff --git a/WinUIGallery/Samples/ControlPages/Design/IconographyPage.xaml b/WinUIGallery/Samples/ControlPages/Design/IconographyPage.xaml index 5a3724de6..6da9ff462 100644 --- a/WinUIGallery/Samples/ControlPages/Design/IconographyPage.xaml +++ b/WinUIGallery/Samples/ControlPages/Design/IconographyPage.xaml @@ -19,7 +19,6 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:helpers="using:WinUIGallery.Helpers" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:toolkit="using:CommunityToolkit.WinUI.Controls" xmlns:models="using:WinUIGallery.Models" helpers:PageScrollBehaviorHelper.SuppressHostScrolling="True" mc:Ignorable="d"> @@ -239,7 +238,7 @@ ItemsSource="{x:Bind SelectedItem.Tags, Mode=OneWay}" SelectionMode="None"> - + diff --git a/WinUIGallery/Samples/ControlPages/Design/IconographyPage.xaml.cs b/WinUIGallery/Samples/ControlPages/Design/IconographyPage.xaml.cs index 83915d6e5..ac15b9f5f 100644 --- a/WinUIGallery/Samples/ControlPages/Design/IconographyPage.xaml.cs +++ b/WinUIGallery/Samples/ControlPages/Design/IconographyPage.xaml.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using CommunityToolkit.WinUI.Controls; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using System; @@ -160,7 +159,7 @@ private void IconsItemsView_SelectionChanged(ItemsView sender, ItemsViewSelectio if (TagsItemsView != null) { - TagsItemsView.Layout = new WrapLayout { VerticalSpacing = 4, HorizontalSpacing = 4 }; + TagsItemsView.Layout = new FlowLayout { LineSpacing = 4, MinItemSpacing = 4 }; } } diff --git a/WinUIGallery/Samples/ControlPages/MenuFlyoutPage.xaml b/WinUIGallery/Samples/ControlPages/MenuFlyoutPage.xaml index c4384dbbf..4d166c70d 100644 --- a/WinUIGallery/Samples/ControlPages/MenuFlyoutPage.xaml +++ b/WinUIGallery/Samples/ControlPages/MenuFlyoutPage.xaml @@ -128,6 +128,57 @@ </MenuFlyoutSubItem> </MenuFlyout> </Button.Flyout> +</Button> + + + + + + + + + + +<Button Content="File Options"> + <Button.Flyout> + <MenuFlyout> + <SplitMenuFlyoutItem Text="Save" Click="SplitMenuFlyoutItem_Click"> + <SplitMenuFlyoutItem.Icon> + <FontIcon Glyph="&#xE74E;"/> + </SplitMenuFlyoutItem.Icon> + <MenuFlyoutItem Text="Save as .docx" Click="SplitMenuFlyoutItem_Click"/> + <MenuFlyoutItem Text="Save as .pdf" Click="SplitMenuFlyoutItem_Click"/> + <MenuFlyoutItem Text="Save as .txt" Click="SplitMenuFlyoutItem_Click"/> + </SplitMenuFlyoutItem> + <SplitMenuFlyoutItem Text="Share" Icon="Share" Click="SplitMenuFlyoutItem_Click"> + <MenuFlyoutItem Text="Share via email" Click="SplitMenuFlyoutItem_Click"/> + <MenuFlyoutItem Text="Share via link" Click="SplitMenuFlyoutItem_Click"/> + </SplitMenuFlyoutItem> + </MenuFlyout> + </Button.Flyout> </Button> diff --git a/WinUIGallery/Samples/ControlPages/MenuFlyoutPage.xaml.cs b/WinUIGallery/Samples/ControlPages/MenuFlyoutPage.xaml.cs index 9e4c42a0a..569c61d81 100644 --- a/WinUIGallery/Samples/ControlPages/MenuFlyoutPage.xaml.cs +++ b/WinUIGallery/Samples/ControlPages/MenuFlyoutPage.xaml.cs @@ -38,4 +38,12 @@ private void Example5_Loaded(object sender, RoutedEventArgs e) { } + + private void SplitMenuFlyoutItem_Click(object sender, RoutedEventArgs e) + { + if (sender is MenuFlyoutItem selectedItem) + { + Control3bOutput.Text = "Clicked: " + selectedItem.Text; + } + } } diff --git a/WinUIGallery/Samples/ControlPages/SystemBackdropElementPage.xaml b/WinUIGallery/Samples/ControlPages/SystemBackdropElementPage.xaml new file mode 100644 index 000000000..eabdbf1b1 --- /dev/null +++ b/WinUIGallery/Samples/ControlPages/SystemBackdropElementPage.xaml @@ -0,0 +1,57 @@ + + + + + + + + + + + +