Browse Source

Optimize selection handling

pull/3297/head
tom-englert 8 months ago
parent
commit
5149e4e77f
  1. 8
      Directory.Packages.props
  2. 3
      ILSpy/Analyzers/AnalyzerTreeView.xaml
  3. 24
      ILSpy/Analyzers/AnalyzerTreeViewModel.cs
  4. 3
      ILSpy/AssemblyTree/AssemblyListPane.xaml
  5. 93
      ILSpy/AssemblyTree/AssemblyTreeModel.cs
  6. 27
      ILSpy/Commands/DecompileInNewViewCommand.cs
  7. 2
      ILSpy/Controls/TreeView/SharpTreeView.cs
  8. 12
      ILSpy/Docking/DockWorkspace.cs
  9. 5
      ILSpy/MainWindow.xaml.cs

8
Directory.Packages.props

@ -45,12 +45,12 @@ @@ -45,12 +45,12 @@
<PackageVersion Include="System.Reflection.Metadata" Version="8.0.0" />
<PackageVersion Include="System.Resources.Extensions" Version="8.0.0" />
<PackageVersion Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageVersion Include="TomsToolbox.Wpf.Composition" Version="2.18.1" />
<PackageVersion Include="TomsToolbox.Wpf.Composition.Mef" Version="2.18.1" />
<PackageVersion Include="TomsToolbox.Wpf.Styles" Version="2.18.1" />
<PackageVersion Include="TomsToolbox.Wpf.Composition" Version="2.20.0" />
<PackageVersion Include="TomsToolbox.Wpf.Composition.Mef" Version="2.20.0" />
<PackageVersion Include="TomsToolbox.Wpf.Styles" Version="2.20.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
</ItemGroup>
<ItemGroup>
<GlobalPackageReference Include="TomsToolbox.Composition.Analyzer" Version="2.18.1" />
<GlobalPackageReference Include="TomsToolbox.Composition.Analyzer" Version="2.20.0" />
</ItemGroup>
</Project>

3
ILSpy/Analyzers/AnalyzerTreeView.xaml

@ -12,8 +12,7 @@ @@ -12,8 +12,7 @@
ShowRoot="False"
BorderThickness="0"
Root="{Binding Root}"
toms:MultiSelectorExtensions.SelectionBinding="{Binding SelectedItems}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
toms:MultiSelectorExtensions.SelectionBinding="{Binding SelectedItems, Mode=TwoWay}"
SelectionChanged="AnalyzerTreeView_OnSelectionChanged">
<UIElement.InputBindings>

24
ILSpy/Analyzers/AnalyzerTreeViewModel.cs

@ -17,8 +17,6 @@ @@ -17,8 +17,6 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows;
@ -38,8 +36,6 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -38,8 +36,6 @@ namespace ICSharpCode.ILSpy.Analyzers
[Export]
public class AnalyzerTreeViewModel : ToolPaneModel
{
private AnalyzerTreeNode selectedItem;
public const string PaneContentId = "analyzerPane";
public AnalyzerTreeViewModel()
@ -52,14 +48,20 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -52,14 +48,20 @@ namespace ICSharpCode.ILSpy.Analyzers
public AnalyzerRootNode Root { get; } = new();
public AnalyzerTreeNode SelectedItem {
get => selectedItem;
set => SetProperty(ref selectedItem, value);
}
public ICommand AnalyzeCommand => new DelegateCommand(AnalyzeSelected);
public ObservableCollection<AnalyzerTreeNode> SelectedItems { get; } = [];
private AnalyzerTreeNode[] selectedItems = [];
public AnalyzerTreeNode[] SelectedItems {
get => selectedItems ?? [];
set {
if (SelectedItems.SequenceEqual(value))
return;
selectedItems = value;
OnPropertyChanged();
}
}
private void AnalyzeSelected()
{
@ -87,7 +89,7 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -87,7 +89,7 @@ namespace ICSharpCode.ILSpy.Analyzers
}
target.IsExpanded = true;
this.SelectedItem = target;
this.SelectedItems = [target];
}
public void Analyze(IEntity entity)

3
ILSpy/AssemblyTree/AssemblyListPane.xaml

@ -15,8 +15,7 @@ @@ -15,8 +15,7 @@
AllowDrop="True"
BorderThickness="0" Visibility="Visible"
Root="{Binding Root}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
toms:MultiSelectorExtensions.SelectionBinding="{Binding SelectedItems}">
toms:MultiSelectorExtensions.SelectionBinding="{Binding SelectedItems, Mode=TwoWay}">
<treeView:SharpTreeView.ItemContainerStyle>
<Style TargetType="treeView:SharpTreeViewItem">
<Setter Property="Template">

93
ILSpy/AssemblyTree/AssemblyTreeModel.cs

@ -18,7 +18,6 @@ @@ -18,7 +18,6 @@
using System.Collections.Generic;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.IO;
using System.Threading.Tasks;
@ -82,9 +81,6 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -82,9 +81,6 @@ namespace ICSharpCode.ILSpy.AssemblyTree
MessageBus<NavigateToReferenceEventArgs>.Subscribers += JumpToReference;
MessageBus<SettingsChangedEventArgs>.Subscribers += (sender, e) => Settings_PropertyChanged(sender, e);
var selectionChangeThrottle = new DispatcherThrottle(DispatcherPriority.Input, TreeView_SelectionChanged);
SelectedItems.CollectionChanged += (_, _) => selectionChangeThrottle.Tick();
refreshThrottle = new DispatcherThrottle(DispatcherPriority.Background, RefreshInternal);
AssemblyList = SettingsService.Instance.CreateEmptyAssemblyList();
@ -131,13 +127,23 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -131,13 +127,23 @@ namespace ICSharpCode.ILSpy.AssemblyTree
set => SetProperty(ref root, value);
}
private SharpTreeNode? selectedItem;
public SharpTreeNode? SelectedItem {
get => selectedItem;
set => SetProperty(ref selectedItem, value);
get => SelectedItems.FirstOrDefault();
set => SelectedItems = value is null ? [] : [value];
}
public ObservableCollection<SharpTreeNode> SelectedItems { get; } = [];
private SharpTreeNode[] selectedItems = [];
public SharpTreeNode[] SelectedItems {
get => selectedItems;
set {
if (selectedItems.SequenceEqual(value))
return;
selectedItems = value;
OnPropertyChanged();
TreeView_SelectionChanged();
}
}
public string[]? SelectedPath => GetPathForNode(SelectedItem);
@ -230,9 +236,11 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -230,9 +236,11 @@ namespace ICSharpCode.ILSpy.AssemblyTree
else
{
IEntity? mr = await Task.Run(() => FindEntityInRelevantAssemblies(navigateTo, relevantAssemblies));
// Make sure we wait for assemblies being loaded...
// BeginInvoke in LoadedAssembly.LookupReferencedAssemblyInternal
await Dispatcher.InvokeAsync(delegate { }, DispatcherPriority.Normal);
if (mr is { ParentModule.MetadataFile: not null })
{
found = true;
@ -511,6 +519,10 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -511,6 +519,10 @@ namespace ICSharpCode.ILSpy.AssemblyTree
{
activeView?.ScrollIntoView(node);
SelectedItem = node;
Dispatcher.BeginInvoke(DispatcherPriority.Background, () => {
activeView?.ScrollIntoView(node);
});
}
}
@ -526,27 +538,12 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -526,27 +538,12 @@ namespace ICSharpCode.ILSpy.AssemblyTree
return;
}
if (this.isNavigatingHistory)
foreach (var node in nodesList)
{
SelectedItems.Clear();
foreach (var node in nodesList)
{
activeView?.ScrollIntoView(node);
SelectedItems.Add(node);
}
}
else
{
// defer selection change, so it does not interfere with the focus of the tab page.
Dispatcher.BeginInvoke(() => {
SelectedItems.Clear();
foreach (var node in nodesList)
{
activeView?.ScrollIntoView(node);
SelectedItems.Add(node);
}
});
activeView?.ScrollIntoView(node);
}
SelectedItems = nodesList.ToArray();
}
/// <summary>
@ -704,7 +701,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -704,7 +701,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
{
lastNode = node;
activeView?.ScrollIntoView(node);
SelectedItems.Add(node);
SelectedItems = [.. SelectedItems, node];
}
}
}
@ -719,7 +716,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -719,7 +716,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
private void TreeView_SelectionChanged()
{
if (SelectedItems.Count > 0)
if (SelectedItems.Length > 0)
{
var activeTabPage = DockWorkspace.Instance.ActiveTabPage;
@ -767,13 +764,13 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -767,13 +764,13 @@ namespace ICSharpCode.ILSpy.AssemblyTree
}
}
private void DecompileSelectedNodes(DecompilerTextViewState? newState = null)
public void DecompileSelectedNodes(DecompilerTextViewState? newState = null)
{
var activeTabPage = DockWorkspace.Instance.ActiveTabPage;
activeTabPage.SupportsLanguageSwitching = true;
if (SelectedItems.Count == 1)
if (SelectedItems.Length == 1)
{
if (SelectedItem is ILSpyTreeNode node && node.View(activeTabPage))
return;
@ -808,23 +805,29 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -808,23 +805,29 @@ namespace ICSharpCode.ILSpy.AssemblyTree
public void NavigateHistory(bool forward)
{
isNavigatingHistory = true;
this.Dispatcher.BeginInvoke(DispatcherPriority.Background, () => isNavigatingHistory = false);
try
{
isNavigatingHistory = true;
TabPageModel tabPage = DockWorkspace.Instance.ActiveTabPage;
var state = tabPage.GetState();
if (state != null)
history.UpdateCurrent(new NavigationState(tabPage, state));
var newState = forward ? history.GoForward() : history.GoBack();
TabPageModel tabPage = DockWorkspace.Instance.ActiveTabPage;
var state = tabPage.GetState();
if (state != null)
history.UpdateCurrent(new NavigationState(tabPage, state));
var newState = forward ? history.GoForward() : history.GoBack();
TabPageModel activeTabPage = newState.TabPage;
TabPageModel activeTabPage = newState.TabPage;
if (!DockWorkspace.Instance.TabPages.Contains(activeTabPage))
DockWorkspace.Instance.AddTabPage(activeTabPage);
else
DockWorkspace.Instance.ActiveTabPage = activeTabPage;
if (!DockWorkspace.Instance.TabPages.Contains(activeTabPage))
DockWorkspace.Instance.AddTabPage(activeTabPage);
else
DockWorkspace.Instance.ActiveTabPage = activeTabPage;
SelectNodes(newState.TreeNodes);
SelectNodes(newState.TreeNodes);
}
finally
{
isNavigatingHistory = false;
}
}
public bool CanNavigateBack => history.CanNavigateBack;
@ -906,7 +909,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -906,7 +909,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
private void UnselectAll()
{
SelectedItems.Clear();
SelectedItems = [];
}
private IEnumerable<SharpTreeNode> GetTopLevelSelection()

27
ILSpy/Commands/DecompileInNewViewCommand.cs

@ -23,9 +23,11 @@ using System.Linq; @@ -23,9 +23,11 @@ using System.Linq;
using System.Windows.Threading;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.ViewModels;
using TomsToolbox.Essentials;
@ -35,6 +37,14 @@ namespace ICSharpCode.ILSpy.Commands @@ -35,6 +37,14 @@ namespace ICSharpCode.ILSpy.Commands
[PartCreationPolicy(CreationPolicy.Shared)]
internal sealed class DecompileInNewViewCommand : IContextMenuEntry
{
private readonly AssemblyTreeModel assemblyTreeModel;
[ImportingConstructor]
public DecompileInNewViewCommand(AssemblyTreeModel assemblyTreeModel)
{
this.assemblyTreeModel = assemblyTreeModel;
}
public bool IsVisible(TextViewContext context)
{
return context.SelectedTreeNodes != null || context.Reference?.Reference is IEntity;
@ -47,14 +57,18 @@ namespace ICSharpCode.ILSpy.Commands @@ -47,14 +57,18 @@ namespace ICSharpCode.ILSpy.Commands
public void Execute(TextViewContext context)
{
var activePane = DockWorkspace.Instance.ActivePane;
DecompileNodes(GetNodes(context).ToArray());
DockWorkspace.Instance.ActivePane = activePane;
}
IEnumerable<ILSpyTreeNode> GetNodes(TextViewContext context)
{
if (context.SelectedTreeNodes != null)
{
if (context.TreeView.DataContext != MainWindow.Instance.AssemblyTreeModel)
if (context.TreeView.DataContext != assemblyTreeModel)
{
return context.SelectedTreeNodes.OfType<IMemberTreeNode>().Select(FindTreeNode).ExceptNullItems();
}
@ -65,7 +79,7 @@ namespace ICSharpCode.ILSpy.Commands @@ -65,7 +79,7 @@ namespace ICSharpCode.ILSpy.Commands
}
else if (context.Reference?.Reference is IEntity entity)
{
if (MainWindow.Instance.AssemblyTreeModel.FindTreeNode(entity) is { } node)
if (assemblyTreeModel.FindTreeNode(entity) is { } node)
{
return new[] { node };
}
@ -76,18 +90,21 @@ namespace ICSharpCode.ILSpy.Commands @@ -76,18 +90,21 @@ namespace ICSharpCode.ILSpy.Commands
{
if (node is ILSpyTreeNode ilspyNode)
return ilspyNode;
return MainWindow.Instance.AssemblyTreeModel.FindTreeNode(node.Member);
return assemblyTreeModel.FindTreeNode(node.Member);
}
}
static void DecompileNodes(ILSpyTreeNode[] nodes)
void DecompileNodes(ILSpyTreeNode[] nodes)
{
if (nodes.Length == 0)
return;
DockWorkspace.Instance.AddTabPage();
MainWindow.Instance.AssemblyTreeModel.SelectNodes(nodes);
if (assemblyTreeModel.SelectedItems.SequenceEqual(nodes))
assemblyTreeModel.DecompileSelectedNodes();
else
assemblyTreeModel.SelectNodes(nodes);
}
}
}

2
ILSpy/Controls/TreeView/SharpTreeView.cs

@ -416,7 +416,9 @@ namespace ICSharpCode.ILSpy.Controls.TreeView @@ -416,7 +416,9 @@ namespace ICSharpCode.ILSpy.Controls.TreeView
throw new ArgumentNullException("node");
doNotScrollOnExpanding = true;
foreach (SharpTreeNode ancestor in node.Ancestors())
{
ancestor.IsExpanded = true;
}
doNotScrollOnExpanding = false;
base.ScrollIntoView(node);
}

12
ILSpy/Docking/DockWorkspace.cs

@ -35,7 +35,6 @@ using ICSharpCode.ILSpy.Analyzers; @@ -35,7 +35,6 @@ using ICSharpCode.ILSpy.Analyzers;
using ICSharpCode.ILSpy.Search;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.ILSpyX.Extensions;
using TomsToolbox.Composition;
using TomsToolbox.Essentials;
@ -53,6 +52,8 @@ namespace ICSharpCode.ILSpy.Docking @@ -53,6 +52,8 @@ namespace ICSharpCode.ILSpy.Docking
private readonly ObservableCollection<TabPageModel> tabPages = [];
private DockingManager dockingManager;
private DockWorkspace()
{
this.tabPages.CollectionChanged += TabPages_CollectionChanged;
@ -179,8 +180,17 @@ namespace ICSharpCode.ILSpy.Docking @@ -179,8 +180,17 @@ namespace ICSharpCode.ILSpy.Docking
}
}
public PaneModel ActivePane {
get => dockingManager?.ActiveContent as PaneModel;
set {
if (dockingManager is not null)
dockingManager.ActiveContent = value;
}
}
public void InitializeLayout(DockingManager manager)
{
this.dockingManager = manager;
manager.LayoutUpdateStrategy = this;
XmlLayoutSerializer serializer = new XmlLayoutSerializer(manager);
serializer.LayoutSerializationCallback += LayoutSerializationCallback;

5
ILSpy/MainWindow.xaml.cs

@ -77,7 +77,10 @@ namespace ICSharpCode.ILSpy @@ -77,7 +77,10 @@ namespace ICSharpCode.ILSpy
mainWindowViewModel.Workspace.InitializeLayout(dockManager);
MenuService.Instance.Init(mainMenu, toolBar, InputBindings);
Dispatcher.BeginInvoke(DispatcherPriority.Background, AssemblyTreeModel.Initialize);
Dispatcher.BeginInvoke(DispatcherPriority.Background, () => {
AssemblyTreeModel.Initialize();
AssemblyTreeModel.Show();
});
});
}

Loading…
Cancel
Save