|
// 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.
#nullable disable
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.ChangeSignature;
using Microsoft.VisualStudio.PlatformUI;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ChangeSignature;
/// <summary>
/// Interaction logic for ChangeSignatureDialog.xaml
/// </summary>
internal partial class ChangeSignatureDialog : DialogWindow
{
private readonly ChangeSignatureDialogViewModel _viewModel;
// Expose localized strings for binding
public static string ChangeSignatureDialogTitle { get { return EditorFeaturesResources.Change_Signature; } }
public static string CurrentParameter { get { return ServicesVSResources.Current_parameter; } }
public static string Parameters { get { return ServicesVSResources.Parameters_colon2; } }
public static string PreviewMethodSignature { get { return ServicesVSResources.Preview_method_signature_colon; } }
public static string PreviewReferenceChanges { get { return ServicesVSResources.Preview_reference_changes; } }
public static string Remove { get { return ServicesVSResources.Re_move; } }
public static string Restore { get { return ServicesVSResources.Restore; } }
public static string Add { get { return ServicesVSResources.Add; } }
public static string OK { get { return ServicesVSResources.OK; } }
public static string Cancel { get { return EditorFeaturesResources.Cancel; } }
public static string WarningTypeDoesNotBind { get { return ServicesVSResources.Warning_colon_type_does_not_bind; } }
public static string WarningDuplicateParameterName { get { return ServicesVSResources.Warning_colon_duplicate_parameter_name; } }
public Brush ParameterText { get; }
public Brush RemovedParameterText { get; }
public Brush DisabledParameterForeground { get; }
public Brush DisabledParameterBackground { get; }
public Brush StrikethroughBrush { get; }
// Use C# Reorder Parameters helpTopic for C# and VB.
internal ChangeSignatureDialog(ChangeSignatureDialogViewModel viewModel)
: base(helpTopic: "vs.csharp.refactoring.reorder")
{
_viewModel = viewModel;
InitializeComponent();
// Set these headers explicitly because binding to DataGridTextColumn.Header is not
// supported.
modifierHeader.Header = ServicesVSResources.Modifier;
defaultHeader.Header = ServicesVSResources.Default_;
typeHeader.Header = ServicesVSResources.Type;
parameterHeader.Header = ServicesVSResources.Parameter;
callsiteHeader.Header = ServicesVSResources.Callsite;
indexHeader.Header = ServicesVSResources.Index;
ParameterText = SystemParameters.HighContrast ? SystemColors.WindowTextBrush : new SolidColorBrush(Color.FromArgb(0xFF, 0x1E, 0x1E, 0x1E));
RemovedParameterText = SystemParameters.HighContrast ? SystemColors.WindowTextBrush : new SolidColorBrush(Colors.Gray);
DisabledParameterBackground = SystemParameters.HighContrast ? SystemColors.WindowBrush : new SolidColorBrush(Color.FromArgb(0xFF, 0xDF, 0xE7, 0xF3));
DisabledParameterForeground = SystemParameters.HighContrast ? SystemColors.GrayTextBrush : new SolidColorBrush(Color.FromArgb(0xFF, 0xA2, 0xA4, 0xA5));
Members.Background = SystemParameters.HighContrast ? SystemColors.WindowBrush : new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
StrikethroughBrush = SystemParameters.HighContrast ? SystemColors.WindowTextBrush : new SolidColorBrush(Colors.Red);
DataContext = viewModel;
Loaded += ChangeSignatureDialog_Loaded;
}
private void ChangeSignatureDialog_Loaded(object sender, RoutedEventArgs e)
=> Members.Focus();
private void OK_Click(object sender, RoutedEventArgs e)
{
if (_viewModel.TrySubmit())
{
DialogResult = true;
}
}
private void Cancel_Click(object sender, RoutedEventArgs e)
=> DialogResult = false;
private void MoveUp_Click(object sender, EventArgs e)
{
MoveUp_UpdateSelectedIndex();
SetFocusToSelectedRow(false);
}
private void MoveUp_Click_FocusRow(object sender, EventArgs e)
{
MoveUp_UpdateSelectedIndex();
SetFocusToSelectedRow(true);
}
private void MoveUp_UpdateSelectedIndex()
{
var oldSelectedIndex = Members.SelectedIndex;
if (_viewModel.CanMoveUp && oldSelectedIndex >= 0)
{
_viewModel.MoveUp();
Members.Items.Refresh();
Members.SelectedIndex = oldSelectedIndex - 1;
}
}
private void MoveDown_Click(object sender, EventArgs e)
{
MoveDown_UpdateSelectedIndex();
SetFocusToSelectedRow(false);
}
private void MoveDown_Click_FocusRow(object sender, EventArgs e)
{
MoveDown_UpdateSelectedIndex();
SetFocusToSelectedRow(true);
}
private void MoveDown_UpdateSelectedIndex()
{
var oldSelectedIndex = Members.SelectedIndex;
if (_viewModel.CanMoveDown && oldSelectedIndex >= 0)
{
_viewModel.MoveDown();
Members.Items.Refresh();
Members.SelectedIndex = oldSelectedIndex + 1;
}
}
private void Remove_Click(object sender, RoutedEventArgs e)
{
if (_viewModel.CanRemove)
{
_viewModel.Remove();
Members.Items.Refresh();
}
SetFocusToSelectedRow(true);
}
private void Restore_Click(object sender, RoutedEventArgs e)
{
if (_viewModel.CanRestore)
{
_viewModel.Restore();
Members.Items.Refresh();
}
SetFocusToSelectedRow(true);
}
private void Add_Click(object sender, RoutedEventArgs e)
{
var addParameterViewModel = _viewModel.CreateAddParameterDialogViewModel();
var dialog = new AddParameterDialog(addParameterViewModel);
var result = dialog.ShowModal();
ChangeSignatureLogger.LogAddParameterDialogLaunched();
if (result.HasValue && result.Value)
{
ChangeSignatureLogger.LogAddParameterDialogCommitted();
var addedParameter = new AddedParameter(
addParameterViewModel.TypeSymbol,
addParameterViewModel.TypeName,
addParameterViewModel.ParameterName,
GetCallSiteKind(addParameterViewModel),
addParameterViewModel.IsCallsiteRegularValue ? addParameterViewModel.CallSiteValue : string.Empty,
addParameterViewModel.IsRequired,
addParameterViewModel.IsRequired ? string.Empty : addParameterViewModel.DefaultValue,
addParameterViewModel.TypeBinds);
_viewModel.AddParameter(addedParameter);
}
SetFocusToSelectedRow(false);
}
private static CallSiteKind GetCallSiteKind(AddParameterDialogViewModel addParameterViewModel)
{
if (addParameterViewModel.IsCallsiteInferred)
return CallSiteKind.Inferred;
if (addParameterViewModel.IsCallsiteOmitted)
return CallSiteKind.Omitted;
if (addParameterViewModel.IsCallsiteTodo)
return CallSiteKind.Todo;
Debug.Assert(addParameterViewModel.IsCallsiteRegularValue);
return addParameterViewModel.UseNamedArguments
? CallSiteKind.ValueWithName
: CallSiteKind.Value;
}
private void SetFocusToSelectedRow(bool focusRow)
{
if (Members.SelectedIndex >= 0)
{
if (Members.ItemContainerGenerator.ContainerFromIndex(Members.SelectedIndex) is not DataGridRow row)
{
Members.ScrollIntoView(Members.SelectedItem);
row = Members.ItemContainerGenerator.ContainerFromIndex(Members.SelectedIndex) as DataGridRow;
}
if (row != null && focusRow)
{
// This line is required primarily for accessibility purposes to ensure the screenreader always
// focuses on individual rows rather than the parent DataGrid.
Members.UpdateLayout();
FocusRow(row);
}
}
}
private static void FocusRow(DataGridRow row)
{
var cell = row.FindDescendant<DataGridCell>();
cell?.Focus();
}
private void MoveSelectionUp_Click(object sender, EventArgs e)
{
var oldSelectedIndex = Members.SelectedIndex;
if (oldSelectedIndex > 0)
{
var potentialNewSelectedParameter = Members.Items[oldSelectedIndex - 1] as ChangeSignatureDialogViewModel.ParameterViewModel;
if (!potentialNewSelectedParameter.IsDisabled)
{
Members.SelectedIndex = oldSelectedIndex - 1;
}
}
SetFocusToSelectedRow(true);
}
private void MoveSelectionDown_Click(object sender, EventArgs e)
{
var oldSelectedIndex = Members.SelectedIndex;
if (oldSelectedIndex >= 0 && oldSelectedIndex < Members.Items.Count - 1)
{
Members.SelectedIndex = oldSelectedIndex + 1;
}
SetFocusToSelectedRow(true);
}
private void Members_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (Members.CurrentItem != null)
{
// When it has a valid value, CurrentItem is generally more up-to-date than SelectedIndex.
// For example, if the user clicks on an out of view item in the parameter list (i.e. the
// parameter list is long and the user scrolls to click another parameter farther down/up
// in the list), CurrentItem will update immediately while SelectedIndex will not.
Members.SelectedIndex = Members.Items.IndexOf(Members.CurrentItem);
}
if (Members.SelectedIndex == -1)
{
Members.SelectedIndex = _viewModel.GetStartingSelectionIndex();
}
SetFocusToSelectedRow(true);
}
private void ToggleRemovedState(object sender, ExecutedRoutedEventArgs e)
{
if (_viewModel.CanRemove)
{
_viewModel.Remove();
}
else if (_viewModel.CanRestore)
{
_viewModel.Restore();
}
Members.Items.Refresh();
SetFocusToSelectedRow(true);
}
internal TestAccessor GetTestAccessor()
=> new(this);
internal readonly struct TestAccessor
{
private readonly ChangeSignatureDialog _dialog;
public TestAccessor(ChangeSignatureDialog dialog)
=> _dialog = dialog;
public ChangeSignatureDialogViewModel ViewModel => _dialog._viewModel;
public DataGrid Members => _dialog.Members;
public DialogButton OKButton => _dialog.OKButton;
public DialogButton CancelButton => _dialog.CancelButton;
public DialogButton DownButton => _dialog.DownButton;
public DialogButton UpButton => _dialog.UpButton;
public DialogButton AddButton => _dialog.AddButton;
public DialogButton RemoveButton => _dialog.RemoveButton;
public DialogButton RestoreButton => _dialog.RestoreButton;
}
}
|