diff --git a/src/dev/impl/DevToys/DevToys.csproj b/src/dev/impl/DevToys/DevToys.csproj index 2418f39824..a9a5499d15 100644 --- a/src/dev/impl/DevToys/DevToys.csproj +++ b/src/dev/impl/DevToys/DevToys.csproj @@ -24,6 +24,8 @@ + + @@ -31,6 +33,9 @@ + + GuidGeneratorToolPage.xaml + HtmlEncoderDecoderToolPage.xaml @@ -198,6 +203,7 @@ + @@ -224,6 +230,8 @@ + + @@ -294,8 +302,8 @@ Base64EncoderDecoderToolPage.xaml - - GuidGeneratorToolPage.xaml + + NumberBaseConverterToolPage.xaml HashGeneratorToolPage.xaml @@ -354,7 +362,7 @@ Designer - + MSBuild:Compile Designer @@ -395,6 +403,10 @@ Designer MSBuild:Compile + + MSBuild:Compile + Designer + MSBuild:Compile Designer @@ -447,7 +459,6 @@ Designer MSBuild:Compile - @@ -481,6 +492,9 @@ Windows Desktop Extensions for the UWP + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Number Base Converter tool + + + Binary + + + Configuration + + + Decimal + + + Number Base Converter + + + Format number + + + Hexadecimal + + + Input + + + Binary + + + Decimal + + + Select which Input type you want to use + + + Hexadecimal + + + Octal + + + Input type + + + Octal + + \ No newline at end of file diff --git a/src/dev/impl/DevToys/Strings/en-US/NumberBaseConverter.resw b/src/dev/impl/DevToys/Strings/en-US/NumberBaseConverter.resw new file mode 100644 index 0000000000..0a162a510e --- /dev/null +++ b/src/dev/impl/DevToys/Strings/en-US/NumberBaseConverter.resw @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Number Base Converter tool + + + Binary + + + Configuration + + + Decimal + + + Number Base Converter + + + Format number + + + Hexadecimal + + + Input + + + Binary + + + Decimal + + + Select which Input type you want to use + + + Hexadecimal + + + Octal + + + Input type + + + Octal + + \ No newline at end of file diff --git a/src/dev/impl/DevToys/Strings/fr-FR/NumberBaseConverter.resw b/src/dev/impl/DevToys/Strings/fr-FR/NumberBaseConverter.resw new file mode 100644 index 0000000000..3c39690b06 --- /dev/null +++ b/src/dev/impl/DevToys/Strings/fr-FR/NumberBaseConverter.resw @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Outil de conversion de base de nombres + + + Binaire + + + Configuration + + + Décimal + + + Convertisseur de base de nombres + + + Format du numéro + + + Hexadécimal + + + Entrée + + + Binaire + + + Décimal + + + Sélectionnez le type d'entrée que vous souhaitez utiliser + + + Hexadécimal + + + Octal + + + Type d'entrée + + + Octal + + \ No newline at end of file diff --git a/src/dev/impl/DevToys/Strings/pl-PL/NumberBaseConverter.resw b/src/dev/impl/DevToys/Strings/pl-PL/NumberBaseConverter.resw new file mode 100644 index 0000000000..0a162a510e --- /dev/null +++ b/src/dev/impl/DevToys/Strings/pl-PL/NumberBaseConverter.resw @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Number Base Converter tool + + + Binary + + + Configuration + + + Decimal + + + Number Base Converter + + + Format number + + + Hexadecimal + + + Input + + + Binary + + + Decimal + + + Select which Input type you want to use + + + Hexadecimal + + + Octal + + + Input type + + + Octal + + \ No newline at end of file diff --git a/src/dev/impl/DevToys/Strings/ru-RU/NumberBaseConverter.resw b/src/dev/impl/DevToys/Strings/ru-RU/NumberBaseConverter.resw new file mode 100644 index 0000000000..0a162a510e --- /dev/null +++ b/src/dev/impl/DevToys/Strings/ru-RU/NumberBaseConverter.resw @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Number Base Converter tool + + + Binary + + + Configuration + + + Decimal + + + Number Base Converter + + + Format number + + + Hexadecimal + + + Input + + + Binary + + + Decimal + + + Select which Input type you want to use + + + Hexadecimal + + + Octal + + + Input type + + + Octal + + \ No newline at end of file diff --git a/src/dev/impl/DevToys/Strings/zh-CN/NumberBaseConverter.resw b/src/dev/impl/DevToys/Strings/zh-CN/NumberBaseConverter.resw new file mode 100644 index 0000000000..0a162a510e --- /dev/null +++ b/src/dev/impl/DevToys/Strings/zh-CN/NumberBaseConverter.resw @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Number Base Converter tool + + + Binary + + + Configuration + + + Decimal + + + Number Base Converter + + + Format number + + + Hexadecimal + + + Input + + + Binary + + + Decimal + + + Select which Input type you want to use + + + Hexadecimal + + + Octal + + + Input type + + + Octal + + \ No newline at end of file diff --git a/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseConverterToolProvider.cs b/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseConverterToolProvider.cs new file mode 100644 index 0000000000..77bed7568f --- /dev/null +++ b/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseConverterToolProvider.cs @@ -0,0 +1,53 @@ +#nullable enable + +using System.Composition; +using System.Threading.Tasks; +using DevToys.Api.Tools; +using DevToys.Core.Threading; +using DevToys.Shared.Api.Core; +using Windows.UI.Xaml.Controls; + +namespace DevToys.ViewModels.Tools.NumberBaseConverter +{ + [Export(typeof(IToolProvider))] + [Name("Number Base Converter")] + [ProtocolName("baseconverter")] + [Order(0)] + [CompactOverlaySize(width: 400, height: 500)] + internal sealed class NumberBaseConverterToolProvider : ToolProviderBase, IToolProvider + { + private readonly IMefProvider _mefProvider; + + public string DisplayName => LanguageManager.Instance.NumberBaseConverter.DisplayName; + + public string AccessibleName => LanguageManager.Instance.NumberBaseConverter.AccessibleName; + + + public object IconSource + => new TaskCompletionNotifier( + ThreadHelper.RunOnUIThreadAsync(() => + { + return Task.FromResult( + new FontIcon + { + Glyph = "\uFE2C" + }); + })); + + [ImportingConstructor] + public NumberBaseConverterToolProvider(IMefProvider mefProvider) + { + _mefProvider = mefProvider; + } + + public bool CanBeTreatedByTool(string data) + { + return false; + } + + public IToolViewModel CreateTool() + { + return _mefProvider.Import(); + } + } +} diff --git a/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseConverterToolViewModel.cs b/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseConverterToolViewModel.cs new file mode 100644 index 0000000000..bc95fdf1f6 --- /dev/null +++ b/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseConverterToolViewModel.cs @@ -0,0 +1,243 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Composition; +using System.Linq; +using System.Threading.Tasks; +using DevToys.Api.Core; +using DevToys.Api.Core.Settings; +using DevToys.Api.Tools; +using DevToys.Core; +using DevToys.Core.Formatter; +using DevToys.Core.Threading; +using DevToys.Models; +using DevToys.Shared.Core.Threading; +using DevToys.Views.Tools.NumberBaseConverter; +using Microsoft.Toolkit.Mvvm.ComponentModel; + +namespace DevToys.ViewModels.Tools.NumberBaseConverter +{ + [Export(typeof(NumberBaseConverterToolViewModel))] + public sealed class NumberBaseConverterToolViewModel : ObservableRecipient, IToolViewModel + { + /// + /// Which format is the input number. + /// + private static readonly SettingDefinition BaseNumber + = new( + name: $"{nameof(NumberBaseConverterToolViewModel)}.{nameof(BaseNumber)}", + isRoaming: true, + defaultValue: Radix.Decimal); + + /// + /// Whether the value should be formatted or not. + /// + private static readonly SettingDefinition Formatted + = new( + name: $"{nameof(NumberBaseConverterToolViewModel)}.{nameof(Formatted)}", + isRoaming: true, + defaultValue: true); + + private readonly Queue _convertQueue = new(); + private readonly ISettingsProvider _settingsProvider; + private readonly IMarketingService _marketingService; + + private string? _inputValue; + private string? _octalValue; + private string? _binaryValue; + private string? _decimalValue; + private string? _hexadecimalValue; + private string? _infoBarMessage; + private bool _isInfoBarOpen; + private bool _conversionInProgress; + private bool _toolSuccessfullyWorked; + + internal NumberBaseConverterStrings Strings => LanguageManager.Instance.NumberBaseConverter; + + public Type View { get; } = typeof(NumberBaseConverterToolPage); + + /// + /// Gets or sets the input text. + /// + internal string? InputValue + { + get => _inputValue; + set + { + SetProperty(ref _inputValue, value); + QueueFormatting(); + } + } + + internal string? OctalValue + { + get => _octalValue; + private set => SetProperty(ref _octalValue, value); + } + + internal string? BinaryValue + { + get => _binaryValue; + private set => SetProperty(ref _binaryValue, value); + } + + internal string? DecimalValue + { + get => _decimalValue; + private set => SetProperty(ref _decimalValue, value); + } + + internal string? HexaDecimalValue + { + get => _hexadecimalValue; + private set => SetProperty(ref _hexadecimalValue, value); + } + + internal bool IsInfoBarOpen + { + get => _isInfoBarOpen; + private set => SetProperty(ref _isInfoBarOpen, value); + } + + internal string? InfoBarMessage + { + get => _infoBarMessage; + private set => SetProperty(ref _infoBarMessage, value); + } + + internal NumberBaseFormat InputBaseNumber + { + get + { + Radix settingsValue = _settingsProvider.GetSetting(BaseNumber); + NumberBaseFormat? baseNumberFormat = BaseNumbers.FirstOrDefault(x => x.Value == settingsValue); + return baseNumberFormat ?? NumberBaseFormat.Decimal; + } + set + { + if (InputBaseNumber != value) + { + _settingsProvider.SetSetting(BaseNumber, value.Value); + + if (InputBaseNumber == NumberBaseFormat.Octal) + { + InputValue = NumberBaseFormatter.RemoveFormatting(OctalValue).ToString(); + } + else if (InputBaseNumber == NumberBaseFormat.Binary) + { + InputValue = NumberBaseFormatter.RemoveFormatting(BinaryValue).ToString(); + } + else if (InputBaseNumber == NumberBaseFormat.Hexadecimal) + { + InputValue = NumberBaseFormatter.RemoveFormatting(HexaDecimalValue).ToString(); + } + else + { + InputValue = NumberBaseFormatter.RemoveFormatting(DecimalValue).ToString(); + } + OnPropertyChanged(); + QueueFormatting(); + } + } + } + + /// + /// Get a list of supported BaseNumbers + /// + internal IReadOnlyList BaseNumbers = new ObservableCollection { + NumberBaseFormat.Octal, + NumberBaseFormat.Binary, + NumberBaseFormat.Decimal, + NumberBaseFormat.Hexadecimal, + }; + + internal bool IsFormatted + { + get => _settingsProvider.GetSetting(Formatted); + set + { + if (_settingsProvider.GetSetting(Formatted) != value) + { + _settingsProvider.SetSetting(Formatted, value); + OnPropertyChanged(); + QueueFormatting(); + } + } + } + + [ImportingConstructor] + public NumberBaseConverterToolViewModel(ISettingsProvider settingsProvider, IMarketingService marketingService) + { + _settingsProvider = settingsProvider; + _marketingService = marketingService; + } + + private void QueueFormatting() + { + _convertQueue.Enqueue(InputValue ?? string.Empty); + TreatQueueAsync().Forget(); + } + + private async Task TreatQueueAsync() + { + if (_conversionInProgress) + { + return; + } + + _conversionInProgress = true; + + await TaskScheduler.Default; + + while (_convertQueue.TryDequeue(out string value)) + { + string octalValue = string.Empty; + string binaryValue = string.Empty; + string decimalValue = string.Empty; + string hexaDecimalValue = string.Empty; + string infoBarMessage = string.Empty; + bool isInfoBarOpen = false; + try + { + long? baseValue = NumberBaseFormatter.StringToBase(value, InputBaseNumber); + if (baseValue != null) + { + octalValue = NumberBaseFormatter.LongToBase(baseValue.Value, NumberBaseFormat.Octal, IsFormatted); + binaryValue = NumberBaseFormatter.LongToBase(baseValue.Value, NumberBaseFormat.Binary, IsFormatted); + decimalValue = NumberBaseFormatter.LongToBase(baseValue.Value, NumberBaseFormat.Decimal, IsFormatted); + hexaDecimalValue = NumberBaseFormatter.LongToBase(baseValue.Value, NumberBaseFormat.Hexadecimal, IsFormatted); + } + } + catch (OverflowException exception) + { + isInfoBarOpen = true; + infoBarMessage = exception.Message; + } + catch (Exception ex) + { + Logger.LogFault("NumberBaseConverter", ex, $"Input base number: {InputBaseNumber}"); + } + + ThreadHelper.RunOnUIThreadAsync(ThreadPriority.Low, () => + { + OctalValue = octalValue; + BinaryValue = binaryValue; + DecimalValue = decimalValue; + HexaDecimalValue = hexaDecimalValue; + InfoBarMessage = infoBarMessage; + IsInfoBarOpen = isInfoBarOpen; + + if (!_toolSuccessfullyWorked) + { + _toolSuccessfullyWorked = true; + _marketingService.NotifyToolSuccessfullyWorked(); + } + }).ForgetSafely(); + } + + _conversionInProgress = false; + } + } +} diff --git a/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseFormatter.cs b/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseFormatter.cs new file mode 100644 index 0000000000..a67ee65f23 --- /dev/null +++ b/src/dev/impl/DevToys/ViewModels/Tools/NumberBaseConverter/NumberBaseFormatter.cs @@ -0,0 +1,248 @@ +using System; +using System.Globalization; +using System.Text; +using DevToys.Models; + +namespace DevToys.Core.Formatter +{ + internal static class NumberBaseFormatter + { + /// + /// Based on + /// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/ParseNumbers.cs + /// + /// Current value + /// Current base number + /// Value converted to + public static long? StringToBase(string value, NumberBaseFormat baseNumber) + { + if (value.Length <= 0) + { + return null; + } + + int length = value.Length; + int index = 0; + + Span unformattedValue = RemoveFormatting(value); + + // Check for a sign + int sign = 1; + if (unformattedValue[index] == '-') + { + if (baseNumber != NumberBaseFormat.Decimal) + { + throw new ArgumentException($"Base {baseNumber} can't have a negative number"); + } + + sign = -1; + index++; + } + else if (unformattedValue[index] == '+') + { + index++; + } + + long result = 0; + long maxVal; + + // Allow all non-decimal numbers to set the sign bit. + if (baseNumber == NumberBaseFormat.Decimal) + { + maxVal = 0x7FFFFFFFFFFFFFFF / 10; + + // Read all of the digits and convert to a number + while (index < length && IsDigit(unformattedValue[index], baseNumber.BaseNumber, out int current)) + { + // Check for overflows - this is sufficient & correct. + if (result > maxVal || result < 0) + { + throw new OverflowException($"Unable to parse the current value exceded max value ({long.MaxValue})"); + } + + result = result * baseNumber.BaseNumber + current; + index++; + } + + if (result is < 0 and not 0x800000000000000) + { + throw new OverflowException($"Unable to parse the current value exceded max value ({long.MaxValue})"); + } + } + else + { + maxVal = + baseNumber.BaseNumber == 10 ? 0xfffffffffffffff / 10 : + baseNumber.BaseNumber == 16 ? 0xfffffffffffffff / 16 : + baseNumber.BaseNumber == 8 ? 0xfffffffffffffff / 8 : + 0xfffffffffffffff / 2; + + // Read all of the digits and convert to a number + while (index < unformattedValue.Length && IsDigit(unformattedValue[index], baseNumber.BaseNumber, out int current)) + { + // Check for overflows - this is sufficient & correct. + if (result > maxVal) + { + throw new OverflowException($"Unable to parse the current value exceded max value ({long.MaxValue})"); + } + + long temp = result * baseNumber.BaseNumber + current; + + if (temp < result) // this means overflow as well + { + throw new OverflowException($"Unable to parse the current value exceded max value ({long.MaxValue})"); + } + + result = temp; + index++; + } + } + + if (baseNumber == NumberBaseFormat.Decimal) + { + result *= sign; + } + + return result; + } + + /// + /// Based on + /// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/ParseNumbers.cs + /// + /// Current number to convert + /// + /// Define if the number need to base formatted + /// + public static string LongToBase(long number, NumberBaseFormat baseNumber, bool isFormatted) + { + Span buffer = stackalloc char[67]; // Longest possible string length for an integer in binary notation with prefix + + // If the number is negative, make it positive and remember the sign. + long ul; + bool isNegative = false; + if (number < 0) + { + isNegative = true; + + // For base 10, write out -num, but other bases write out the + // 2's complement bit pattern + ul = (10 == baseNumber.BaseNumber) ? -number : number; + } + else + { + ul = number; + } + + // Special case the 0. + int index; + if (0 == ul) + { + buffer[0] = '0'; + index = 1; + } + else + { + index = 0; + for (int i = 0; i < buffer.Length; i++) // for loop instead of do{...}while(l!=0) to help JIT eliminate span bounds checks + { + long div = ul / baseNumber.BaseNumber; // TODO https://github.com/dotnet/runtime/issues/5213 + int charVal = (int)(ul - (div * baseNumber.BaseNumber)); + ul = div; + + buffer[i] = (charVal < 10) ? + (char)(charVal + '0') : + (char)(charVal + 'a' - 10); + + if (ul == 0) + { + index = i + 1; + break; + } + } + } + + if (baseNumber == NumberBaseFormat.Decimal) + { + // If it was negative, append the sign. + if (isNegative) + { + buffer[index++] = '-'; + } + } + + var builder = new StringBuilder(); + + for (int builderIndex = --index; builderIndex >= 0; builderIndex--) + { + builder.Append(buffer[builderIndex]); + if (isFormatted && builderIndex != 0 && builderIndex % baseNumber.GroupSize == 0) + { + builder.Append(baseNumber.GroupSeparator); + } + } + + // Add padding left for Binary Format + if (baseNumber == NumberBaseFormat.Binary) + { + int reminder = ++index % baseNumber.GroupSize; + for (int padIndex = 0; reminder != 0 && padIndex < baseNumber.GroupSize - reminder; padIndex++) + { + builder.Insert(0, '0'); + } + } + + return builder.ToString().ToUpperInvariant(); + } + + /// + /// Remove formatting (whitespace and Culture separator) + /// + /// + /// + public static Span RemoveFormatting(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + return Span.Empty; + } + + string currentCulture = CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator; + Span values = new char[value.Length]; + int valueIndex = 0; + for (int i = 0; i < values.Length; i++) + { + if (!char.IsWhiteSpace(value[i]) && value[i] != Convert.ToChar(currentCulture)) + { + values[valueIndex] = value[i]; + valueIndex++; + } + } + return values; + } + + private static bool IsDigit(char c, int radix, out int result) + { + int tmp; + if ((uint)(c - '0') <= 9) + { + result = tmp = c - '0'; + } + else if ((uint)(c - 'A') <= 'Z' - 'A') + { + result = tmp = c - 'A' + 10; + } + else if ((uint)(c - 'a') <= 'z' - 'a') + { + result = tmp = c - 'a' + 10; + } + else + { + result = -1; + return false; + } + + return tmp < radix; + } + } +} diff --git a/src/dev/impl/DevToys/Views/Tools/GuidGenerator/GuidGeneratorToolPage.xaml - Copy.cs b/src/dev/impl/DevToys/Views/Tools/GuidGenerator/GuidGeneratorToolPage.xaml - Copy.cs new file mode 100644 index 0000000000..68dacc596b --- /dev/null +++ b/src/dev/impl/DevToys/Views/Tools/GuidGenerator/GuidGeneratorToolPage.xaml - Copy.cs @@ -0,0 +1,52 @@ +#nullable enable + +using DevToys.Api.Core.Navigation; +using DevToys.Shared.Core; +using DevToys.ViewModels.Tools.GuidGenerator; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Navigation; + +namespace DevToys.Views.Tools.GuidGenerator +{ + public sealed partial class GuidGeneratorToolPage : Page + { + public static readonly DependencyProperty ViewModelProperty + = DependencyProperty.Register( + nameof(ViewModel), + typeof(GuidGeneratorToolViewModel), + typeof(GuidGeneratorToolPage), + new PropertyMetadata(null)); + + /// + /// Gets the page's view model. + /// + public GuidGeneratorToolViewModel ViewModel + { + get => (GuidGeneratorToolViewModel)GetValue(ViewModelProperty); + set => SetValue(ViewModelProperty, value); + } + + public GuidGeneratorToolPage() + { + InitializeComponent(); + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + if (ViewModel is null) + { + var parameters = (NavigationParameter)e.Parameter; + + // Set the view model + Assumes.NotNull(parameters.ViewModel, nameof(parameters.ViewModel)); + ViewModel = (GuidGeneratorToolViewModel)parameters.ViewModel!; + DataContext = ViewModel; + + ViewModel.OutputTextBox = OutputTextBox; + } + + base.OnNavigatedTo(e); + } + } +} diff --git a/src/dev/impl/DevToys/Views/Tools/NumberBaseConverter/NumberBaseConverterToolPage.xaml b/src/dev/impl/DevToys/Views/Tools/NumberBaseConverter/NumberBaseConverterToolPage.xaml new file mode 100644 index 0000000000..46054efa35 --- /dev/null +++ b/src/dev/impl/DevToys/Views/Tools/NumberBaseConverter/NumberBaseConverterToolPage.xaml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/dev/impl/DevToys/Views/Tools/NumberBaseConverter/NumberBaseConverterToolPage.xaml.cs b/src/dev/impl/DevToys/Views/Tools/NumberBaseConverter/NumberBaseConverterToolPage.xaml.cs new file mode 100644 index 0000000000..f5ac4ed19e --- /dev/null +++ b/src/dev/impl/DevToys/Views/Tools/NumberBaseConverter/NumberBaseConverterToolPage.xaml.cs @@ -0,0 +1,50 @@ +#nullable enable + +using DevToys.Api.Core.Navigation; +using DevToys.Shared.Core; +using DevToys.ViewModels.Tools.NumberBaseConverter; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Navigation; + +namespace DevToys.Views.Tools.NumberBaseConverter +{ + public sealed partial class NumberBaseConverterToolPage : Page + { + public static readonly DependencyProperty ViewModelProperty + = DependencyProperty.Register( + nameof(ViewModel), + typeof(NumberBaseConverterToolViewModel), + typeof(NumberBaseConverterToolPage), + new PropertyMetadata(null)); + + /// + /// Gets the page's view model. + /// + public NumberBaseConverterToolViewModel ViewModel + { + get => (NumberBaseConverterToolViewModel)GetValue(ViewModelProperty); + set => SetValue(ViewModelProperty, value); + } + + public NumberBaseConverterToolPage() + { + InitializeComponent(); + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + if (ViewModel is null) + { + var parameters = (NavigationParameter)e.Parameter; + + // Set the view model + Assumes.NotNull(parameters.ViewModel, nameof(parameters.ViewModel)); + ViewModel = (NumberBaseConverterToolViewModel)parameters.ViewModel!; + DataContext = ViewModel; + } + + base.OnNavigatedTo(e); + } + } +} diff --git a/src/tests/DevToys.Tests/Core/Formatter/BaseNumberFormatterTests.cs b/src/tests/DevToys.Tests/Core/Formatter/BaseNumberFormatterTests.cs new file mode 100644 index 0000000000..e4ec486e6f --- /dev/null +++ b/src/tests/DevToys.Tests/Core/Formatter/BaseNumberFormatterTests.cs @@ -0,0 +1,62 @@ +using DevToys.Core.Formatter; +using DevToys.Models; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DevToys.Tests.Core.Formatter +{ + [TestClass] + public class BaseNumberFormatterTests + { + [DataTestMethod] + [DataRow("C6AEA155", 3333333333, false)] + [DataRow("C6AE A155", 3333333333, true)] + public void HexadecimalToDecimal(string input, long expectedResult, bool isFormatted) + { + Assert.AreEqual(expectedResult, NumberBaseFormatter.StringToBase(input, NumberBaseFormat.Hexadecimal)); + } + + [DataTestMethod] + [DataRow("30653520525", 3333333333, false)] + [DataRow("30 653 520 525", 3333333333, true)] + public void OctalToDecimal(string input, long expectedResult, bool isFormatted) + { + Assert.AreEqual(expectedResult, NumberBaseFormatter.StringToBase(input, NumberBaseFormat.Octal)); + } + + [DataTestMethod] + [DataRow("000101001101", 333, false)] + [DataRow("0001 0100 1101", 333, true)] + [DataRow("11000110101011101010000101010101", 3333333333, false)] + [DataRow("1100 0110 1010 1110 1010 0001 0101 0101", 3333333333, true)] + public void BinaryToDecimal(string input, long expectedResult, bool isFormatted) + { + Assert.AreEqual(expectedResult, NumberBaseFormatter.StringToBase(input, NumberBaseFormat.Binary)); + } + + [DataTestMethod] + [DataRow(3333333333, "C6AEA155", false)] + [DataRow(3333333333, "C6AE A155", true)] + public void DecimalToHexadecimal(long input, string expectedResult, bool isFormatted) + { + Assert.AreEqual(expectedResult, NumberBaseFormatter.LongToBase(input, NumberBaseFormat.Hexadecimal, isFormatted)); + } + + [DataTestMethod] + [DataRow(3333333333, "30653520525", false)] + [DataRow(3333333333, "30 653 520 525", true)] + public void DecimalToOctal(long input, string expectedResult, bool isFormatted) + { + Assert.AreEqual(expectedResult, NumberBaseFormatter.LongToBase(input, NumberBaseFormat.Octal, isFormatted)); + } + + [DataTestMethod] + [DataRow(333, "000101001101", false)] + [DataRow(333, "0001 0100 1101", true)] + [DataRow(3333333333, "11000110101011101010000101010101", false)] + [DataRow(3333333333, "1100 0110 1010 1110 1010 0001 0101 0101", true)] + public void DecimalToBinary(long input, string expectedResult, bool isFormatted) + { + Assert.AreEqual(expectedResult, NumberBaseFormatter.LongToBase(input, NumberBaseFormat.Binary, isFormatted)); + } + } +} diff --git a/src/tests/DevToys.Tests/DevToys.Tests.csproj b/src/tests/DevToys.Tests/DevToys.Tests.csproj index 8b6221b0ae..9d4d9d9e41 100644 --- a/src/tests/DevToys.Tests/DevToys.Tests.csproj +++ b/src/tests/DevToys.Tests/DevToys.Tests.csproj @@ -44,6 +44,7 @@ + @@ -88,7 +89,7 @@ - {E3E4E200-B380-4207-9A7E-4C9421904502} + {e3e4e200-b380-4207-9a7e-4c9421904502} DevToys