From 1035ff904546b911e5d2189e298408f5c392c84e Mon Sep 17 00:00:00 2001 From: michael-hawker <24302614+michael-hawker@users.noreply.github.com> Date: Fri, 14 Apr 2023 17:44:31 -0700 Subject: [PATCH] Fixes #388 SizerBase Orientation Property Removes Mouse Extension from XAML Template and does in code-behind as to remove the need to set the Cursor property directly via binding By setting the Cursor DependencyProperty we were losing our detection mechanism, now if it's set it's an explicit override, otherwise we use our logic based on Orientation property Also, switches to new CommunityToolkit.*.Extensions Dependency vs. old copies of Tree helpers Fixes some doc typos Bumps version, tested on UWP, WASDK, and Uno.UI/WASM --- ...mmunityToolkit.Labs.WinUI.SizerBase.csproj | 2 +- .../src/ContentSizer/ContentSizer.Events.cs | 2 +- components/SizerBase/src/Dependencies.props | 26 +- components/SizerBase/src/SizerBase.Events.cs | 2 +- .../SizerBase/src/SizerBase.Properties.cs | 37 +-- components/SizerBase/src/SizerBase.cs | 12 +- components/SizerBase/src/SizerBase.xaml | 3 +- .../src/Toolkit/DependencyObjectExtensions.cs | 225 ------------------ .../FrameworkElementExtensions.Mouse.cs | 113 --------- .../Toolkit/OrientationToObjectConverter.cs | 2 + .../src/Toolkit/TypeToObjectConverter.cs | 91 ------- nuget.config | 10 + 12 files changed, 51 insertions(+), 474 deletions(-) delete mode 100644 components/SizerBase/src/Toolkit/DependencyObjectExtensions.cs delete mode 100644 components/SizerBase/src/Toolkit/FrameworkElementExtensions.Mouse.cs delete mode 100644 components/SizerBase/src/Toolkit/TypeToObjectConverter.cs create mode 100644 nuget.config diff --git a/components/SizerBase/src/CommunityToolkit.Labs.WinUI.SizerBase.csproj b/components/SizerBase/src/CommunityToolkit.Labs.WinUI.SizerBase.csproj index 2c050a007..d80f15f1f 100644 --- a/components/SizerBase/src/CommunityToolkit.Labs.WinUI.SizerBase.csproj +++ b/components/SizerBase/src/CommunityToolkit.Labs.WinUI.SizerBase.csproj @@ -2,7 +2,7 @@ SizerBase This package contains SizerBase. - 0.0.4 + 0.0.5 CommunityToolkit.Labs.WinUI.SizerBaseRns diff --git a/components/SizerBase/src/ContentSizer/ContentSizer.Events.cs b/components/SizerBase/src/ContentSizer/ContentSizer.Events.cs index 3e9b8f567..bf5439f7b 100644 --- a/components/SizerBase/src/ContentSizer/ContentSizer.Events.cs +++ b/components/SizerBase/src/ContentSizer/ContentSizer.Events.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using CommunityToolkit.Labs.WinUI.SizerBaseLocal; +using CommunityToolkit.WinUI; namespace CommunityToolkit.Labs.WinUI; diff --git a/components/SizerBase/src/Dependencies.props b/components/SizerBase/src/Dependencies.props index e622e1df4..3470b688b 100644 --- a/components/SizerBase/src/Dependencies.props +++ b/components/SizerBase/src/Dependencies.props @@ -9,23 +9,13 @@ For UWP / WinAppSDK / Uno packages, place the package references here. --> - - - - + + + + - - - - - - - - - - - - - - + + + + diff --git a/components/SizerBase/src/SizerBase.Events.cs b/components/SizerBase/src/SizerBase.Events.cs index c3b2c52c0..1161075e8 100644 --- a/components/SizerBase/src/SizerBase.Events.cs +++ b/components/SizerBase/src/SizerBase.Events.cs @@ -72,7 +72,7 @@ protected override void OnManipulationStarting(ManipulationStartingRoutedEventAr /// protected override void OnManipulationDelta(ManipulationDeltaRoutedEventArgs e) { - // We use Trancate here to provide 'snapping' points with the DragIncrement property + // We use Truncate here to provide 'snapping' points with the DragIncrement property // It works for both our negative and positive values, as otherwise we'd need to use // Ceiling when negative and Floor when positive to maintain the correct behavior. var horizontalChange = diff --git a/components/SizerBase/src/SizerBase.Properties.cs b/components/SizerBase/src/SizerBase.Properties.cs index 650e6477a..39b34cb23 100644 --- a/components/SizerBase/src/SizerBase.Properties.cs +++ b/components/SizerBase/src/SizerBase.Properties.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using CommunityToolkit.WinUI; + #if !WINAPPSDK using CursorEnum = Windows.UI.Core.CoreCursorType; #else @@ -17,7 +19,7 @@ namespace CommunityToolkit.Labs.WinUI; public partial class SizerBase : Control { /// - /// Gets or sets the cursor to use when hovering over the gripper bar. If left as null, the control will manage the cursor automatically based on the property value. + /// Gets or sets the cursor to use when hovering over the gripper bar. If left as null, the control will manage the cursor automatically based on the property value (default). /// public CursorEnum Cursor { @@ -32,13 +34,13 @@ public CursorEnum Cursor DependencyProperty.Register(nameof(Cursor), typeof(CursorEnum), typeof(SizerBase), new PropertyMetadata(null, OnOrientationPropertyChanged)); /// - /// Gets or sets the incremental amount of change for draging with the mouse or touch of a sizer control. Effectively a snapping increment for changes. The default is 1. + /// Gets or sets the incremental amount of change for dragging with the mouse or touch of a sizer control. Effectively a snapping increment for changes. The default is 1. /// /// /// For instance, if the DragIncrement is set to 16. Then when a component is resized with the sizer, it will only increase or decrease in size in that increment. I.e. -16, 0, 16, 32, 48, etc... /// /// - /// This value is indepedent of the property. If you need to provide consistent snapping when moving regardless of input device, set these properties to the same value. + /// This value is independent of the property. If you need to provide consistent snapping when moving regardless of input device, set these properties to the same value. /// public double DragIncrement { @@ -92,35 +94,38 @@ private static void OnOrientationPropertyChanged(DependencyObject d, DependencyP { if (d is SizerBase gripper) { - CursorEnum cursorToUse = gripper.Orientation == Orientation.Vertical ? CursorEnum.SizeWestEast : CursorEnum.SizeNorthSouth; + CursorEnum cursorByOrientation = gripper.Orientation == Orientation.Vertical ? CursorEnum.SizeWestEast : CursorEnum.SizeNorthSouth; // See if there's been a cursor override, otherwise we'll pick var cursor = gripper.ReadLocalValue(CursorProperty); if (cursor == DependencyProperty.UnsetValue || cursor == null) { - cursor = cursorToUse; - - // On UWP, we use the extension in XAML to control this behavior, - // so we'll update it here (and maintain binding). - // We'll keep it in-sync to maintain behavior for WinUI 3 as well. - gripper.SetValue(CursorProperty, cursor); + cursor = cursorByOrientation; + } - // We return here, as the Cursor will trigger this function again anyway to set for WinUI 3 - return; +#if !WINAPPSDK + // On UWP, we use our XAML extension to control this behavior, + // so we'll update it here (and maintain any cursor override). + if (cursor is CursorEnum cursorValue) + { + FrameworkElementExtensions.SetCursor(gripper, cursorValue); } + return; +#endif + // TODO: [UNO] Only supported on certain platforms // See ProtectedCursor here: https://github.com/unoplatform/uno/blob/3fe3862b270b99dbec4d830b547942af61b1a1d9/src/Uno.UI/UI/Xaml/UIElement.cs#L1015-L1023 #if WINAPPSDK && !HAS_UNO // Need to wait until we're at least applying template step of loading before setting Cursor // See https://github.com/microsoft/microsoft-ui-xaml/issues/7062 - if (gripper._applyingTemplate && - cursor is CursorEnum cursorToSet && + if (gripper._appliedTemplate && + cursor is CursorEnum cursorValue && (gripper.ProtectedCursor == null || (gripper.ProtectedCursor is InputSystemCursor current && - current.CursorShape != cursorToSet))) + current.CursorShape != cursorValue))) { - gripper.ProtectedCursor = InputSystemCursor.Create(cursorToSet); + gripper.ProtectedCursor = InputSystemCursor.Create(cursorValue); } #endif } diff --git a/components/SizerBase/src/SizerBase.cs b/components/SizerBase/src/SizerBase.cs index 10a5ea6b8..a138dcfc9 100644 --- a/components/SizerBase/src/SizerBase.cs +++ b/components/SizerBase/src/SizerBase.cs @@ -21,7 +21,7 @@ protected virtual void OnLoaded(RoutedEventArgs e) /// /// Called when the control starts to be dragged by the user. - /// Implementor should record current state of manipulated target at this point in time. + /// Implementer should record current state of manipulated target at this point in time. /// They will receive the cumulative change in or /// based on the property. /// @@ -42,7 +42,7 @@ protected virtual void OnLoaded(RoutedEventArgs e) /// manipulation. This method will be used regardless of input device. It will already /// be adjusted for RightToLeft of the containing /// layout/settings. It will also already account for any settings such as - /// or . The implementor + /// or . The implementer /// just needs to use the provided value to manipulate their baseline stored /// in to provide the desired change. /// @@ -57,7 +57,7 @@ protected virtual void OnLoaded(RoutedEventArgs e) /// The value provided here is the cumulative change from the beginning of the /// manipulation. This method will be used regardless of input device. It will also /// already account for any settings such as or - /// . The implementor just needs + /// . The implementer just needs /// to use the provided value to manipulate their baseline stored /// in to provide the desired change. /// @@ -83,7 +83,7 @@ protected override AutomationPeer OnCreateAutomationPeer() // On Uno the ProtectedCursor isn't supported yet, so we don't need this value. #if WINAPPSDK && !HAS_UNO // Used to track when we're in the OnApplyTemplateStep to change ProtectedCursor value. - private bool _applyingTemplate = false; + private bool _appliedTemplate = false; #endif /// @@ -115,9 +115,9 @@ protected override void OnApplyTemplate() SizerBase_IsEnabledChanged(this, null!); #if WINAPPSDK && !HAS_UNO // On WinAppSDK, we'll trigger this to setup the initial ProtectedCursor value. - _applyingTemplate = true; + _appliedTemplate = true; #endif - // On UWP, we'll check the current Orientation and set the Cursor property to use here still. + // Ensure we have the proper cursor value setup, as we can only set now for WinUI 3 OnOrientationPropertyChanged(this, null!); } diff --git a/components/SizerBase/src/SizerBase.xaml b/components/SizerBase/src/SizerBase.xaml index 58f991fc3..59a468eb0 100644 --- a/components/SizerBase/src/SizerBase.xaml +++ b/components/SizerBase/src/SizerBase.xaml @@ -1,4 +1,4 @@ - + diff --git a/components/SizerBase/src/Toolkit/DependencyObjectExtensions.cs b/components/SizerBase/src/Toolkit/DependencyObjectExtensions.cs deleted file mode 100644 index c28bd5d96..000000000 --- a/components/SizerBase/src/Toolkit/DependencyObjectExtensions.cs +++ /dev/null @@ -1,225 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// We want this to be private/local to our component. -namespace CommunityToolkit.Labs.WinUI.SizerBaseLocal; - -//// IMPORTANT NOTE: This is the old 6.1.1 version of the extensions as I had issues with TPredicate with the new ones here for some reason and just wanted to get this working for now. - -/// -/// Defines a collection of extensions methods for UI. -/// -public static class VisualTree -{ - /// - /// Find descendant control using its name. - /// - /// Parent element. - /// Name of the control to find - /// Descendant control or null if not found. - public static FrameworkElement? FindDescendantByName(this DependencyObject element, string name) - { - if (element == null || string.IsNullOrWhiteSpace(name)) - { - return null; - } - - if (name.Equals((element as FrameworkElement)?.Name, StringComparison.OrdinalIgnoreCase)) - { - return element as FrameworkElement; - } - - var childCount = VisualTreeHelper.GetChildrenCount(element); - for (int i = 0; i < childCount; i++) - { - var result = VisualTreeHelper.GetChild(element, i).FindDescendantByName(name); - if (result != null) - { - return result; - } - } - - return null; - } - - /// - /// Find first descendant control of a specified type. - /// - /// Type to search for. - /// Parent element. - /// Descendant control or null if not found. - public static T? FindDescendant(this DependencyObject element) - where T : DependencyObject - { - T? retValue = default(T); - var childrenCount = VisualTreeHelper.GetChildrenCount(element); - - for (var i = 0; i < childrenCount; i++) - { - var child = VisualTreeHelper.GetChild(element, i); - if (child is T type) - { - retValue = type; - break; - } - - retValue = FindDescendant(child); - - if (retValue != null) - { - break; - } - } - - return retValue; - } - - /// - /// Find first descendant control of a specified type. - /// - /// Parent element. - /// Type of descendant. - /// Descendant control or null if not found. - public static object? FindDescendant(this DependencyObject element, Type type) - { - object? retValue = null; - var childrenCount = VisualTreeHelper.GetChildrenCount(element); - - for (var i = 0; i < childrenCount; i++) - { - var child = VisualTreeHelper.GetChild(element, i); - if (child.GetType() == type) - { - retValue = child; - break; - } - - retValue = FindDescendant(child, type); - - if (retValue != null) - { - break; - } - } - - return retValue; - } - - /// - /// Find all descendant controls of the specified type. - /// - /// Type to search for. - /// Parent element. - /// Descendant controls or empty if not found. - public static IEnumerable FindDescendants(this DependencyObject element) - where T : DependencyObject - { - var childrenCount = VisualTreeHelper.GetChildrenCount(element); - - for (var i = 0; i < childrenCount; i++) - { - var child = VisualTreeHelper.GetChild(element, i); - if (child is T type) - { - yield return type; - } - - foreach (T childofChild in child.FindDescendants()) - { - yield return childofChild; - } - } - } - - /// - /// Find visual ascendant control using its name. - /// - /// Parent element. - /// Name of the control to find - /// Descendant control or null if not found. - public static FrameworkElement? FindAscendantByName(this DependencyObject element, string name) - { - if (element == null || string.IsNullOrWhiteSpace(name)) - { - return null; - } - - var parent = VisualTreeHelper.GetParent(element); - - if (parent == null) - { - return null; - } - - if (name.Equals((parent as FrameworkElement)?.Name, StringComparison.OrdinalIgnoreCase)) - { - return parent as FrameworkElement; - } - - return parent.FindAscendantByName(name); - } - - /// - /// Find first visual ascendant control of a specified type. - /// - /// Type to search for. - /// Child element. - /// Ascendant control or null if not found. - public static T? FindAscendant(this DependencyObject element) - where T : DependencyObject - { - var parent = VisualTreeHelper.GetParent(element); - - if (parent == null) - { - return default(T); - } - - if (parent is T rtn) - { - return rtn; - } - - return parent.FindAscendant(); - } - - /// - /// Find first visual ascendant control of a specified type. - /// - /// Child element. - /// Type of ascendant to look for. - /// Ascendant control or null if not found. - public static object? FindAscendant(this DependencyObject element, Type type) - { - var parent = VisualTreeHelper.GetParent(element); - - if (parent == null) - { - return null; - } - - if (parent.GetType() == type) - { - return parent; - } - - return parent.FindAscendant(type); - } - - /// - /// Find all visual ascendants for the element. - /// - /// Child element. - /// A collection of parent elements or null if none found. - public static IEnumerable FindAscendants(this DependencyObject element) - { - var parent = VisualTreeHelper.GetParent(element); - - while (parent != null) - { - yield return parent; - parent = VisualTreeHelper.GetParent(parent); - } - } -} diff --git a/components/SizerBase/src/Toolkit/FrameworkElementExtensions.Mouse.cs b/components/SizerBase/src/Toolkit/FrameworkElementExtensions.Mouse.cs deleted file mode 100644 index ab66bdc43..000000000 --- a/components/SizerBase/src/Toolkit/FrameworkElementExtensions.Mouse.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Windows.UI.Core; - -namespace CommunityToolkit.Labs.WinUI.SizerBaseLocal; - -/// -public static partial class FrameworkElementExtensions -{ - private static readonly object _cursorLock = new object(); - private static readonly CoreCursor _defaultCursor = new CoreCursor(CoreCursorType.Arrow, 1); - private static readonly Dictionary _cursors = - new Dictionary { { CoreCursorType.Arrow, _defaultCursor } }; - - /// - /// Dependency property for specifying the target to be shown - /// over the target . - /// - public static readonly DependencyProperty CursorProperty = - DependencyProperty.RegisterAttached("Cursor", typeof(CoreCursorType), typeof(FrameworkElementExtensions), new PropertyMetadata(CoreCursorType.Arrow, CursorChanged)); - - /// - /// Set the target . - /// - /// Object where the selector cursor type should be shown. - /// Target cursor type value. - public static void SetCursor(FrameworkElement element, CoreCursorType value) - { - element.SetValue(CursorProperty, value); - } - - /// - /// Get the current . - /// - /// Object where the selector cursor type should be shown. - /// Cursor type set on target element. - public static CoreCursorType GetCursor(FrameworkElement element) - { - return (CoreCursorType)element.GetValue(CursorProperty); - } - - private static void CursorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - // TODO: How do we want to indicate this isn't supported on the WinAppSDK? -#if !WINAPPSDK - var element = d as FrameworkElement; - if (element == null) - { - throw new NullReferenceException(nameof(element)); - } - - var value = (CoreCursorType)e.NewValue; - - // lock ensures CoreCursor creation and event handlers attachment/detachment is atomic - lock (_cursorLock) - { - if (!_cursors.ContainsKey(value)) - { - _cursors[value] = new CoreCursor(value, 1); - } - - // make sure event handlers are not attached twice to element - element.PointerEntered -= Element_PointerEntered; - element.PointerEntered += Element_PointerEntered; - element.PointerExited -= Element_PointerExited; - element.PointerExited += Element_PointerExited; - element.Unloaded -= ElementOnUnloaded; - element.Unloaded += ElementOnUnloaded; - } -#endif - } - -#if !WINAPPSDK - private static void Element_PointerEntered(object sender, PointerRoutedEventArgs e) - { - // TODO: [UNO] Only supported on certain platforms - // See PointerCursor here: https://github.com/unoplatform/uno/blob/3fe3862b270b99dbec4d830b547942af61b1a1d9/src/Uno.UWP/UI/Core/CoreWindow.cs#L71-L77 -#if NETFX_CORE || WASM || __MACOS__ || __SKIA__ - CoreCursorType cursor = GetCursor((FrameworkElement)sender); - Window.Current.CoreWindow.PointerCursor = _cursors[cursor]; -#endif - } - - private static void Element_PointerExited(object sender, PointerRoutedEventArgs e) - { -#if NETFX_CORE || WASM || __MACOS__ || __SKIA__ - // when exiting change the cursor to the target Mouse.Cursor value of the new element - CoreCursor cursor; - if (sender != e.OriginalSource && e.OriginalSource is FrameworkElement newElement) - { - cursor = _cursors[GetCursor(newElement)]; - } - else - { - cursor = _defaultCursor; - } - - Window.Current.CoreWindow.PointerCursor = cursor; -#endif - } - - private static void ElementOnUnloaded(object sender, RoutedEventArgs routedEventArgs) - { -#if NETFX_CORE || __WASM__ || __MACOS__ || __SKIA__ - // when the element is programatically unloaded, reset the cursor back to default - // this is necessary when click triggers immediate change in layout and PointerExited is not called - Window.Current.CoreWindow.PointerCursor = _defaultCursor; -#endif - } -#endif -} diff --git a/components/SizerBase/src/Toolkit/OrientationToObjectConverter.cs b/components/SizerBase/src/Toolkit/OrientationToObjectConverter.cs index 358d7c21a..085e974ec 100644 --- a/components/SizerBase/src/Toolkit/OrientationToObjectConverter.cs +++ b/components/SizerBase/src/Toolkit/OrientationToObjectConverter.cs @@ -4,6 +4,8 @@ namespace CommunityToolkit.Labs.WinUI.SizerBaseLocal; +//// TODO: Make this part of the WCT converters package? + /// /// This class returns a value depending on the of the value provided to the converter. In case of default will return the . /// diff --git a/components/SizerBase/src/Toolkit/TypeToObjectConverter.cs b/components/SizerBase/src/Toolkit/TypeToObjectConverter.cs deleted file mode 100644 index 7f1322076..000000000 --- a/components/SizerBase/src/Toolkit/TypeToObjectConverter.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace CommunityToolkit.Labs.WinUI.SizerBaseLocal; - -/// -/// This class returns an object or another, depending on whether the type of the provided value matches another provided Type. -/// -[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1001:Types that own disposable fields should be disposable", Justification = "Internal Uno Generator Issue: https://github.com/unoplatform/uno/pull/8743")] -public partial class TypeToObjectConverter : DependencyObject, IValueConverter -{ - /// - /// Identifies the property. - /// - public static readonly DependencyProperty TrueValueProperty = - DependencyProperty.Register(nameof(TrueValue), typeof(object), typeof(TypeToObjectConverter), new PropertyMetadata(null)); - - /// - /// Identifies the property. - /// - public static readonly DependencyProperty FalseValueProperty = - DependencyProperty.Register(nameof(FalseValue), typeof(object), typeof(TypeToObjectConverter), new PropertyMetadata(null)); - - /// - /// Identifies the property. - /// - public static readonly DependencyProperty TypeProperty = - DependencyProperty.Register(nameof(Type), typeof(Type), typeof(TypeToObjectConverter), new PropertyMetadata(typeof(object))); - - /// - /// Gets or sets the value to be returned when the type of the provided value matches . - /// - public object TrueValue - { - get { return GetValue(TrueValueProperty); } - set { SetValue(TrueValueProperty, value); } - } - - /// - /// Gets or sets the value to be returned when the type of the provided value does not match . - /// - public object FalseValue - { - get { return GetValue(FalseValueProperty); } - set { SetValue(FalseValueProperty, value); } - } - - /// - /// Gets or sets the Type used to compare the type of the provided value. - /// - public Type Type - { - get { return (Type)GetValue(TypeProperty); } - set { SetValue(TypeProperty, value); } - } - - /// - /// Convert the 's Type to an other object. - /// - /// The source data being passed to the target. - /// The type of the target property, as a type reference. - /// An optional parameter to be used to invert the converter logic. - /// The language of the conversion. - /// The value to be passed to the target dependency property. - public object Convert(object value, Type targetType, object parameter, string language) - { - var typeMatches = value != null && Type.Equals(value.GetType()); - - // Negate if needed - if (ConverterTools.TryParseBool(parameter)) - { - typeMatches = !typeMatches; - } - - return ConverterTools.Convert(typeMatches ? TrueValue : FalseValue, targetType); - } - - /// - /// Not implemented. - /// - /// The source data being passed to the target. - /// The type of the target property, as a type reference. - /// Optional parameter. Not used. - /// The language of the conversion. Not used. - /// The value to be passed to the target dependency property. - public object ConvertBack(object value, Type targetType, object parameter, string language) - { - throw new NotImplementedException(); - } -} diff --git a/nuget.config b/nuget.config new file mode 100644 index 000000000..c3f797ff6 --- /dev/null +++ b/nuget.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file