Skip to content

Commit 4883e4c

Browse files
authored
Merge pull request #59779 from dotnet/features/rename_ui_rework
2 parents 5138447 + f198b0d commit 4883e4c

35 files changed

+696
-45
lines changed

src/EditorFeatures/Core.Cocoa/Microsoft.CodeAnalysis.EditorFeatures.Cocoa.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
<ItemGroup>
1414
<Compile Include="..\Core.Wpf\ExternalAccess\VSTypeScript\**\*.cs" LinkBase="ExternalAccess\VSTypeScript" />
15-
<Compile Include="..\Core.Wpf\InlineRename\**\*.cs" Exclude="..\Core.Wpf\InlineRename\Dashboard\**\*.cs" LinkBase="InlineRename" />
15+
<Compile Include="..\Core.Wpf\InlineRename\**\*.cs" Exclude="..\Core.Wpf\InlineRename\UI\**\*.cs" LinkBase="InlineRename" />
1616
<Compile Include="..\Core.Wpf\Classification\**\*.cs" LinkBase="Classification" />
1717
<Compile Include="..\Core.Wpf\Diagnostics\**\*.cs" LinkBase="Diagnostics" />
1818
<Compile Include="..\Core.Wpf\NavigableSymbols\**\*.cs" LinkBase="NavigableSymbols" />

src/EditorFeatures/Core.Wpf/EditorFeaturesWpfResources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,7 @@
153153
<data name="Interactive_host_process_platform" xml:space="preserve">
154154
<value>Interactive host process platform</value>
155155
</data>
156+
<data name="Enter_to_rename_shift_enter_to_preview" xml:space="preserve">
157+
<value>Enter to rename, Shift+Enter to preview</value>
158+
</data>
156159
</root>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using Microsoft.CodeAnalysis.Options;
6+
7+
namespace Microsoft.CodeAnalysis.Editor.InlineRename
8+
{
9+
internal sealed class InlineRenameExperimentationOptions
10+
{
11+
public static readonly Option<bool> UseInlineAdornment = new(
12+
feature: "InlineRenameExperimentation",
13+
name: "UseInlineAdornment",
14+
defaultValue: false,
15+
storageLocation: new FeatureFlagStorageLocation("Roslyn.UseInlineAdornmentForRename"));
16+
}
17+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<UserControl x:Class="Microsoft.CodeAnalysis.Editor.InlineRename.Adornment.InlineRenameAdornment"
2+
x:ClassModifier="internal"
3+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
5+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
7+
xmlns:local="clr-namespace:Microsoft.CodeAnalysis.Editor.InlineRename.Adornment"
8+
xmlns:rename="clr-namespace:Microsoft.CodeAnalysis.Editor.Implementation.InlineRename"
9+
xmlns:imaging="clr-namespace:Microsoft.VisualStudio.Imaging;assembly=Microsoft.VisualStudio.Imaging"
10+
xmlns:imagecatalog="clr-namespace:Microsoft.VisualStudio.Imaging;assembly=Microsoft.VisualStudio.ImageCatalog"
11+
mc:Ignorable="d"
12+
d:DesignHeight="450" d:DesignWidth="800"
13+
MinWidth="200"
14+
x:Name="control"
15+
KeyDown="Adornment_KeyDown"
16+
MouseDown="Adornment_ConsumeMouseEvent"
17+
MouseUp="Adornment_ConsumeMouseEvent"
18+
GotKeyboardFocus="Adornment_GotKeyboardFocus"
19+
Focusable="False"
20+
UseLayoutRounding="True"
21+
Background="{DynamicResource {x:Static rename:InlineRenameColors.BackgroundBrushKey}}">
22+
23+
<UserControl.Resources>
24+
<ResourceDictionary>
25+
<ResourceDictionary.MergedDictionaries>
26+
<ResourceDictionary Source="../InlineRenameColors.xaml"/>
27+
</ResourceDictionary.MergedDictionaries>
28+
29+
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
30+
31+
<SolidColorBrush x:Key="ForegroundText" Color="{DynamicResource {x:Static rename:InlineRenameColors.SystemCaptionTextColorKey}}"/>
32+
33+
<Style TargetType="CheckBox">
34+
<Setter Property="Foreground" Value="{DynamicResource {x:Static rename:InlineRenameColors.CheckBoxTextBrushKey}}"/>
35+
</Style>
36+
37+
<Style TargetType="TextBlock">
38+
<Setter Property="Foreground" Value="{StaticResource ForegroundText}"/>
39+
</Style>
40+
</ResourceDictionary>
41+
</UserControl.Resources>
42+
43+
<Border
44+
Background="{DynamicResource {x:Static rename:InlineRenameColors.BackgroundBrushKey}}"
45+
BorderThickness="1"
46+
BorderBrush="{DynamicResource {x:Static rename:InlineRenameColors.ButtonBorderBrush}}" >
47+
<StackPanel Orientation="Vertical" Margin="5" >
48+
<Grid Margin="0 0 0 10">
49+
<Grid.ColumnDefinitions>
50+
<ColumnDefinition Width="*" />
51+
<ColumnDefinition Width="22" />
52+
</Grid.ColumnDefinitions>
53+
54+
55+
<TextBox
56+
Grid.Column="0"
57+
x:Name="IdentifierTextBox"
58+
Text="{Binding IdentifierText, UpdateSourceTrigger=PropertyChanged}"
59+
GotFocus="IdentifierTextBox_GotFocus"
60+
HorizontalAlignment="Stretch" />
61+
62+
<!-- Expand/Collapse button and glyph -->
63+
<Button x:Name="ToggleExpandButton" Grid.Column="1" Click="ToggleExpand" Background="Transparent">
64+
<Button.Style>
65+
<Style TargetType="Button">
66+
<Setter Property="Template">
67+
<Setter.Value>
68+
<ControlTemplate TargetType="Button">
69+
<Border Name="border"
70+
BorderThickness="1"
71+
BorderBrush="{DynamicResource {x:Static rename:InlineRenameColors.BackgroundBrushKey}}"
72+
Background="{TemplateBinding Background}">
73+
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
74+
</Border>
75+
<ControlTemplate.Triggers>
76+
<Trigger Property="IsMouseOver" Value="True">
77+
<Setter TargetName="border" Property="BorderBrush" Value="{DynamicResource {x:Static rename:InlineRenameColors.ButtonBorderBrush}}" />
78+
</Trigger>
79+
</ControlTemplate.Triggers>
80+
</ControlTemplate>
81+
</Setter.Value>
82+
</Setter>
83+
</Style>
84+
</Button.Style>
85+
<imaging:CrispImage Grid.Column="0">
86+
<imaging:CrispImage.Style>
87+
<Style TargetType="imaging:CrispImage">
88+
<Style.Triggers>
89+
<DataTrigger Binding="{Binding IsCollapsed}" Value="True">
90+
<Setter Property="Moniker" Value="{x:Static imagecatalog:KnownMonikers.ExpandDown}" />
91+
</DataTrigger>
92+
93+
<DataTrigger Binding="{Binding IsCollapsed}" Value="False">
94+
<Setter Property="Moniker" Value="{x:Static imagecatalog:KnownMonikers.CollapseUp}" />
95+
</DataTrigger>
96+
</Style.Triggers>
97+
</Style>
98+
</imaging:CrispImage.Style>
99+
</imaging:CrispImage>
100+
</Button>
101+
</Grid>
102+
103+
<StackPanel Orientation="Vertical" Visibility="{Binding IsExpanded, Converter={StaticResource BooleanToVisibilityConverter}}">
104+
<CheckBox Content="{Binding ElementName=control, Path=RenameOverloads}" Margin="0,5,0,0" IsChecked="{Binding Path=RenameOverloadFlag, Mode=TwoWay}"
105+
Name="OverloadsCheckbox" Visibility="{Binding RenameOverloadsVisibility}" IsEnabled="{Binding IsRenameOverloadsEditable}" />
106+
<CheckBox Name="CommentsCheckbox" Content="{Binding ElementName=control, Path=SearchInComments}" Margin="0,5,0,0" IsChecked="{Binding Path=RenameInCommentsFlag, Mode=TwoWay}" />
107+
<CheckBox Name="StringsCheckbox" Content="{Binding ElementName=control, Path=SearchInStrings}" Margin="0,5,0,0" IsChecked="{Binding Path=RenameInStringsFlag, Mode=TwoWay}" />
108+
<CheckBox Name="FileRenameCheckbox"
109+
Content="{Binding Path=FileRenameString}"
110+
Margin="0,5,0,0"
111+
IsChecked="{Binding Path=RenameFileFlag, Mode=TwoWay}"
112+
IsEnabled="{Binding Path=AllowFileRename, Mode=OneWay}"
113+
Visibility="{Binding Path=ShowFileRename, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}"/>
114+
</StackPanel>
115+
116+
<TextBlock Text="{Binding ElementName=control, Path=SubmitText}" Foreground="LightGray" Margin="5, 5, 0, 0" FontStyle="Italic" />
117+
</StackPanel>
118+
</Border>
119+
</UserControl>
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Windows;
7+
using System.Windows.Controls;
8+
using System.Windows.Input;
9+
using Microsoft.VisualStudio.Text.Editor;
10+
11+
namespace Microsoft.CodeAnalysis.Editor.InlineRename.Adornment
12+
{
13+
/// <summary>
14+
/// Interaction logic for InlineRenameAdornment.xaml
15+
/// </summary>
16+
internal partial class InlineRenameAdornment : UserControl, IDisposable
17+
{
18+
private readonly InlineRenameAdornmentViewModel _viewModel;
19+
private readonly ITextView _textView;
20+
21+
public InlineRenameAdornment(InlineRenameAdornmentViewModel viewModel, ITextView textView)
22+
{
23+
DataContext = _viewModel = viewModel;
24+
_textView = textView;
25+
26+
_textView.LayoutChanged += TextView_LayoutChanged;
27+
_textView.ViewportHeightChanged += TextView_ViewPortChanged;
28+
_textView.ViewportWidthChanged += TextView_ViewPortChanged;
29+
_textView.LostAggregateFocus += TextView_LostFocus;
30+
_textView.Caret.PositionChanged += TextView_CursorChanged;
31+
32+
// On initialization focus the first tab target
33+
Initialized += (s, e) => MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
34+
35+
InitializeComponent();
36+
PositionAdornment();
37+
}
38+
39+
#pragma warning disable CA1822 // Mark members as static - used in xaml
40+
public string RenameOverloads => EditorFeaturesResources.Include_overload_s;
41+
public string SearchInComments => EditorFeaturesResources.Include_comments;
42+
public string SearchInStrings => EditorFeaturesResources.Include_strings;
43+
public string ApplyRename => EditorFeaturesResources.Apply1;
44+
public string CancelRename => EditorFeaturesResources.Cancel;
45+
public string PreviewChanges => EditorFeaturesResources.Preview_changes1;
46+
public string SubmitText => EditorFeaturesWpfResources.Enter_to_rename_shift_enter_to_preview;
47+
#pragma warning restore CA1822 // Mark members as static
48+
49+
private void TextView_CursorChanged(object sender, CaretPositionChangedEventArgs e)
50+
=> _viewModel.Cancel();
51+
52+
private void TextView_LostFocus(object sender, EventArgs e)
53+
=> _viewModel.Cancel();
54+
55+
private void TextView_ViewPortChanged(object sender, EventArgs e)
56+
=> PositionAdornment();
57+
58+
private void TextView_LayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
59+
=> PositionAdornment();
60+
61+
private void PositionAdornment()
62+
{
63+
var top = _textView.Caret.Bottom + 5;
64+
var left = _textView.Caret.Left - 5;
65+
66+
Canvas.SetTop(this, top);
67+
Canvas.SetLeft(this, left);
68+
}
69+
70+
public void Dispose()
71+
{
72+
_viewModel.Dispose();
73+
74+
_textView.LayoutChanged -= TextView_LayoutChanged;
75+
_textView.ViewportHeightChanged -= TextView_ViewPortChanged;
76+
_textView.ViewportWidthChanged -= TextView_ViewPortChanged;
77+
_textView.LostAggregateFocus -= TextView_LostFocus;
78+
_textView.Caret.PositionChanged -= TextView_CursorChanged;
79+
}
80+
81+
private void Submit_Click(object sender, RoutedEventArgs e)
82+
{
83+
_viewModel.Submit();
84+
}
85+
86+
private void Adornment_KeyDown(object sender, KeyEventArgs e)
87+
{
88+
switch (e.Key)
89+
{
90+
case Key.Enter:
91+
e.Handled = true;
92+
_viewModel.Submit();
93+
break;
94+
95+
case Key.Escape:
96+
e.Handled = true;
97+
_viewModel.Cancel();
98+
break;
99+
100+
case Key.Tab:
101+
// We don't want tab to lose focus for the adornment, so manually
102+
// loop focus back to the first item that is focusable.
103+
FrameworkElement lastItem = _viewModel.IsExpanded
104+
? FileRenameCheckbox
105+
: IdentifierTextBox;
106+
107+
if (lastItem.IsFocused)
108+
{
109+
e.Handled = true;
110+
MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
111+
}
112+
113+
break;
114+
}
115+
}
116+
117+
private void IdentifierTextBox_GotFocus(object sender, RoutedEventArgs e)
118+
{
119+
IdentifierTextBox.SelectAll();
120+
}
121+
122+
private void Adornment_ConsumeMouseEvent(object sender, MouseButtonEventArgs e)
123+
{
124+
e.Handled = true;
125+
}
126+
127+
private void Adornment_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
128+
{
129+
if (e.OldFocus == this)
130+
{
131+
return;
132+
}
133+
134+
IdentifierTextBox.Focus();
135+
e.Handled = true;
136+
}
137+
138+
private void ToggleExpand(object sender, RoutedEventArgs e)
139+
{
140+
_viewModel.IsExpanded = !_viewModel.IsExpanded;
141+
}
142+
}
143+
}

0 commit comments

Comments
 (0)