Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize conversion of MouseGesture from/to string, reduce allocations #10265

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// 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 System.Runtime.CompilerServices;
using System.ComponentModel;
using System.Globalization;

namespace System.Windows.Input
{
/// <summary>
/// Converter class for converting between a <see langword="string"/> and <see cref="MouseAction"/>.
/// Converter class for converting between a <see cref="string"/> and <see cref="MouseAction"/>.
/// </summary>
public class MouseActionConverter : TypeConverter
{
///<summary>
/// Used to check whether we can convert a <see langword="string"/> into a <see cref="MouseAction"/>.
/// Used to check whether we can convert a <see cref="string"/> into a <see cref="MouseAction"/>.
///</summary>
///<param name="context">ITypeDescriptorContext</param>
///<param name="sourceType">type to convert from</param>
Expand All @@ -25,11 +25,11 @@ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceT
}

/// <summary>
/// Used to check whether we can convert specified value to <see langword="string"/>.
/// Used to check whether we can convert specified value to <see cref="string"/>.
/// </summary>
/// <param name="context">ITypeDescriptorContext</param>
/// <param name="destinationType">Type to convert to</param>
/// <returns><see langword="true"/> if conversion to <see langword="string"/> is possible, <see langword="false"/> otherwise.</returns>
/// <returns><see langword="true"/> if conversion to <see cref="string"/> is possible, <see langword="false"/> otherwise.</returns>
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
// We can convert to an InstanceDescriptor or to a string
Expand All @@ -45,41 +45,48 @@ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinati
}

/// <summary>
/// Converts <paramref name="source"/> of <see langword="string"/> type to its <see cref="MouseAction"/> represensation.
/// Converts <paramref name="source"/> of <see cref="string"/> type to its <see cref="MouseAction"/> represensation.
/// </summary>
/// <param name="context">Parser Context</param>
/// <param name="culture">Culture Info</param>
/// <param name="source">MouseAction String</param>
/// <returns>A <see cref="MouseAction"/> representing the <see langword="string"/> specified by <paramref name="source"/>.</returns>
/// <returns>A <see cref="MouseAction"/> representing the <see cref="string"/> specified by <paramref name="source"/>.</returns>
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object source)
{
if (source is not string mouseAction)
throw GetConvertFromException(source);

ReadOnlySpan<char> mouseActionToken = mouseAction.AsSpan().Trim();
return mouseActionToken switch
{
_ when mouseActionToken.IsEmpty => MouseAction.None, // Special casing as produced by "ConvertTo"
_ when mouseActionToken.Equals("None", StringComparison.OrdinalIgnoreCase) => MouseAction.None,
_ when mouseActionToken.Equals("LeftClick", StringComparison.OrdinalIgnoreCase) => MouseAction.LeftClick,
_ when mouseActionToken.Equals("RightClick", StringComparison.OrdinalIgnoreCase) => MouseAction.RightClick,
_ when mouseActionToken.Equals("MiddleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.MiddleClick,
_ when mouseActionToken.Equals("WheelClick", StringComparison.OrdinalIgnoreCase) => MouseAction.WheelClick,
_ when mouseActionToken.Equals("LeftDoubleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.LeftDoubleClick,
_ when mouseActionToken.Equals("RightDoubleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.RightDoubleClick,
_ when mouseActionToken.Equals("MiddleDoubleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.MiddleDoubleClick,
_ => throw new NotSupportedException(SR.Format(SR.Unsupported_MouseAction, mouseActionToken.ToString()))
};

return ConvertFromImpl(mouseActionToken);
}

/// <summary>
/// Converts a <paramref name="value"/> of <see cref="MouseAction"/> to its <see langword="string"/> represensation.
/// Converts <paramref name="mouseActionToken"/> to its <see cref="MouseAction"/> represensation.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static MouseAction ConvertFromImpl(ReadOnlySpan<char> mouseActionToken) => mouseActionToken switch
{
_ when mouseActionToken.IsEmpty => MouseAction.None, // Special casing as produced by "ConvertTo"
_ when mouseActionToken.Equals("None", StringComparison.OrdinalIgnoreCase) => MouseAction.None,
_ when mouseActionToken.Equals("LeftClick", StringComparison.OrdinalIgnoreCase) => MouseAction.LeftClick,
_ when mouseActionToken.Equals("RightClick", StringComparison.OrdinalIgnoreCase) => MouseAction.RightClick,
_ when mouseActionToken.Equals("MiddleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.MiddleClick,
_ when mouseActionToken.Equals("WheelClick", StringComparison.OrdinalIgnoreCase) => MouseAction.WheelClick,
_ when mouseActionToken.Equals("LeftDoubleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.LeftDoubleClick,
_ when mouseActionToken.Equals("RightDoubleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.RightDoubleClick,
_ when mouseActionToken.Equals("MiddleDoubleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.MiddleDoubleClick,
_ => throw new NotSupportedException(SR.Format(SR.Unsupported_MouseAction, mouseActionToken.ToString()))
};

/// <summary>
/// Converts a <paramref name="value"/> of <see cref="MouseAction"/> to its <see cref="string"/> represensation.
/// </summary>
/// <param name="context">Serialization Context</param>
/// <param name="culture">Culture Info</param>
/// <param name="value">MouseAction value </param>
/// <param name="destinationType">Type to Convert</param>
/// <returns>A <see langword="string"/> representing the <see cref="MouseAction"/> specified by <paramref name="value"/>.</returns>
/// <returns>A <see cref="string"/> representing the <see cref="MouseAction"/> specified by <paramref name="value"/>.</returns>
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
ArgumentNullException.ThrowIfNull(destinationType);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// 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.

Expand Down Expand Up @@ -53,13 +53,13 @@ public MouseGesture(MouseAction mouseAction): this(mouseAction, ModifierKeys.Non
/// </summary>
/// <param name="mouseAction">Mouse Action</param>
/// <param name="modifiers">Modifiers</param>
public MouseGesture( MouseAction mouseAction,ModifierKeys modifiers) // acclerator action
public MouseGesture(MouseAction mouseAction, ModifierKeys modifiers) // acclerator action
{
if (!MouseGesture.IsDefinedMouseAction(mouseAction))
throw new InvalidEnumArgumentException("mouseAction", (int)mouseAction, typeof(MouseAction));
if (!MouseActionConverter.IsDefinedMouseAction(mouseAction))
throw new InvalidEnumArgumentException(nameof(mouseAction), (int)mouseAction, typeof(MouseAction));

if (!ModifierKeysConverter.IsDefinedModifierKeys(modifiers))
throw new InvalidEnumArgumentException("modifiers", (int)modifiers, typeof(ModifierKeys));
throw new InvalidEnumArgumentException(nameof(modifiers), (int)modifiers, typeof(ModifierKeys));

_modifiers = modifiers;
_mouseAction = mouseAction;
Expand All @@ -86,12 +86,13 @@ public MouseAction MouseAction
}
set
{
if (!MouseGesture.IsDefinedMouseAction((MouseAction)value))
throw new InvalidEnumArgumentException("value", (int)value, typeof(MouseAction));
if (!MouseActionConverter.IsDefinedMouseAction(value))
throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(MouseAction));

if (_mouseAction != value)
{
_mouseAction = (MouseAction)value;
OnPropertyChanged("MouseAction");
_mouseAction = value;
OnPropertyChanged(nameof(MouseAction));
}
}
}
Expand All @@ -107,13 +108,13 @@ public ModifierKeys Modifiers
}
set
{
if (!ModifierKeysConverter.IsDefinedModifierKeys((ModifierKeys)value))
throw new InvalidEnumArgumentException("value", (int)value, typeof(ModifierKeys));
if (!ModifierKeysConverter.IsDefinedModifierKeys(value))
throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(ModifierKeys));

if (_modifiers != value)
{
_modifiers = (ModifierKeys)value;
OnPropertyChanged("Modifiers");
_modifiers = value;
OnPropertyChanged(nameof(Modifiers));
}
}
}
Expand All @@ -135,12 +136,6 @@ public override bool Matches(object targetElement, InputEventArgs inputEventArgs
return false;
}


// Helper like Enum.IsDefined, for MouseAction.
internal static bool IsDefinedMouseAction(MouseAction mouseAction)
{
return (mouseAction >= MouseAction.None && mouseAction <= MouseAction.MiddleDoubleClick);
}
#endregion Public Methods

#region Internal NotifyProperty changed
Expand Down
Loading