diff --git a/CSharpCodeAnalyst/App.xaml.cs b/CSharpCodeAnalyst/App.xaml.cs index eccbc8a..6574055 100644 --- a/CSharpCodeAnalyst/App.xaml.cs +++ b/CSharpCodeAnalyst/App.xaml.cs @@ -56,7 +56,7 @@ protected override void OnStartup(StartupEventArgs e) var treeViewModel = new TreeViewModel(messaging); var searchViewModel = new SearchViewModel(messaging); var cycleViewModel = new CycleSummaryViewModel(); - var infoPanelViewModel = new InfoPanelViewModel(settings); + var infoPanelViewModel = new InfoPanelViewModel(); viewModel.InfoPanelViewModel = infoPanelViewModel; viewModel.GraphViewModel = graphViewModel; diff --git a/CSharpCodeAnalyst/Configuration/ApplicationSettings.cs b/CSharpCodeAnalyst/Configuration/ApplicationSettings.cs index bacbc49..92eed9a 100644 --- a/CSharpCodeAnalyst/Configuration/ApplicationSettings.cs +++ b/CSharpCodeAnalyst/Configuration/ApplicationSettings.cs @@ -7,7 +7,6 @@ public class ApplicationSettings { public int WarningCodeElementLimit { get; set; } = 50; public string DefaultProjectExcludeFilter { get; set; } = string.Empty; - public bool DefaultShowQuickHelp { get; set; } public int MaxDegreeOfParallelism { get; set; } = 8; public bool AutomaticallyAddContainingType { get; set; } = true; diff --git a/CSharpCodeAnalyst/Configuration/SettingsDialog.xaml b/CSharpCodeAnalyst/Configuration/SettingsDialog.xaml index 96ecb76..7f6ba36 100644 --- a/CSharpCodeAnalyst/Configuration/SettingsDialog.xaml +++ b/CSharpCodeAnalyst/Configuration/SettingsDialog.xaml @@ -17,10 +17,6 @@ - - 0) @@ -54,7 +52,6 @@ private static ApplicationSettings CloneSettings(ApplicationSettings original) { WarningCodeElementLimit = original.WarningCodeElementLimit, DefaultProjectExcludeFilter = original.DefaultProjectExcludeFilter, - DefaultShowQuickHelp = original.DefaultShowQuickHelp, MaxDegreeOfParallelism = original.MaxDegreeOfParallelism, AutomaticallyAddContainingType = original.AutomaticallyAddContainingType }; diff --git a/CSharpCodeAnalyst/GraphArea/ClickController.cs b/CSharpCodeAnalyst/GraphArea/ClickController.cs new file mode 100644 index 0000000..a220993 --- /dev/null +++ b/CSharpCodeAnalyst/GraphArea/ClickController.cs @@ -0,0 +1,155 @@ +using System.Diagnostics; +using System.Windows.Input; +using System.Windows.Threading; +using Microsoft.Msagl.Drawing; + +namespace CSharpCodeAnalyst.GraphArea +{ + internal enum States + { + Init, + + /// + /// Detected a left mouse down. + /// + AwaitSingleClick, + + /// + /// First mouse up was received. + /// + DetectedSingleClick, + + /// + /// Second left mouse down was received within the time limit. + /// + AwaitDoubleClick + } + + /// + /// Emulates single and double click events since the graph control does not provide them. + /// I want to detect a double click without executing the single click action first. + /// Also, it is important to let the MSAGL LayoutEditor handles the mouse events first. + /// If the layout editor is not finished with its internal cleanup on mouse up, + /// replacing the graph crashes the application. + /// + internal class ClickController + { + private readonly Microsoft.Msagl.WpfGraphControl.GraphViewer _viewer; + + private States _state = States.Init; + + /// + /// The object that was under the mouse cursor when the first click was detected. + /// + private IViewerObject? _targetObject; + + DispatcherTimer _timer = new DispatcherTimer(); + private Stopwatch _watch = Stopwatch.StartNew(); + + public ClickController(Microsoft.Msagl.WpfGraphControl.GraphViewer viewer, int doubleClickDelay = 300) + { + _viewer = viewer; + _timer.Interval = TimeSpan.FromMilliseconds(doubleClickDelay); + _timer.Stop(); + _timer.Tick += OnTimerTick; + _watch.Restart(); + Register(viewer); + } + + bool IsShiftPressed() + { + return Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift); + } + + bool IsCtrlPressed() + { + return Keyboard.IsKeyDown(Key.LeftCtrl); + } + + + private void OnTimerTick(object? sender, EventArgs e) + { + _timer.Stop(); + + if (_state == States.DetectedSingleClick) + { + // No second click within the time limit. + // Interpret as single click. + LeftSingleClick?.Invoke(_targetObject); + } + + _state = States.Init; + } + + + // High-level events + public event Action? LeftSingleClick; + public event Action? LeftDoubleClick; + public event Action? OpenContextMenu; + + + public void Register(Microsoft.Msagl.WpfGraphControl.GraphViewer msaglViewer) + { + // Important: The LayoutEditor has to handle the events first + // and finish all cleanup work. + + msaglViewer.MouseDown += OnViewerMouseDown; + msaglViewer.MouseUp += OnViewerMouseUp; + msaglViewer.MouseMove += OnViewerMouseMove; + } + + private void OnViewerMouseMove(object? sender, MsaglMouseEventArgs e) + { + // _timer.Stop(); + // _state = States.Init; + } + + private void OnViewerMouseUp(object? sender, MsaglMouseEventArgs e) + { + if (_state == States.AwaitSingleClick) + { + _state = States.DetectedSingleClick; + } + else if (_state == States.AwaitDoubleClick) + { + // Detected a second left mouse click within the time limit. + _timer.Stop(); + LeftDoubleClick?.Invoke(_targetObject); + _state = States.Init; + } + } + + private void OnViewerMouseDown(object? sender, MsaglMouseEventArgs e) + { + if (e.LeftButtonIsPressed) + { + if (_state == States.Init) + { + _timer.Stop(); + _targetObject = _viewer.ObjectUnderMouseCursor; + _state = States.AwaitSingleClick; + + _timer.Start(); + } + else if (_state == States.DetectedSingleClick) + { + // Second left click detected within time limit + _state = States.AwaitDoubleClick; + } + else + { + // Unexpected state, reset + _state = States.Init; + _timer.Stop(); + } + + return; + } + + if (e.RightButtonIsPressed) + { + OpenContextMenu?.Invoke(_targetObject); + } + } + } +} \ No newline at end of file diff --git a/CSharpCodeAnalyst/GraphArea/GraphViewModel.cs b/CSharpCodeAnalyst/GraphArea/GraphViewModel.cs index e472b81..cbc037e 100644 --- a/CSharpCodeAnalyst/GraphArea/GraphViewModel.cs +++ b/CSharpCodeAnalyst/GraphArea/GraphViewModel.cs @@ -77,13 +77,13 @@ internal GraphViewModel(IGraphViewer viewer, ICodeGraphExplorer explorer, IPubli // Static commands _viewer.AddContextMenuCommand(new CodeElementContextCommand(Strings.Expand, Expand, CanExpand) { - //IsDoubleClickable = true, - //IsVisible = false + IsDoubleClickable = true, + IsVisible = false }); _viewer.AddContextMenuCommand(new CodeElementContextCommand(Strings.Collapse, Collapse, CanCollapse) { - //IsDoubleClickable = true, - //IsVisible = false + IsDoubleClickable = true, + IsVisible = false }); _viewer.AddContextMenuCommand(new CodeElementContextCommand(Strings.ToggleFlag, ToggleFlag, icon: LoadIcon("Resources/flag.png"))); diff --git a/CSharpCodeAnalyst/GraphArea/GraphViewer.cs b/CSharpCodeAnalyst/GraphArea/GraphViewer.cs index 4ee4490..4e7bb08 100644 --- a/CSharpCodeAnalyst/GraphArea/GraphViewer.cs +++ b/CSharpCodeAnalyst/GraphArea/GraphViewer.cs @@ -3,7 +3,6 @@ using System.IO; using System.Runtime.CompilerServices; using System.Windows.Controls; -using System.Windows.Input; using Contracts.Graph; using CSharpCodeAnalyst.Common; using CSharpCodeAnalyst.GraphArea.Highlighting; @@ -32,6 +31,8 @@ internal class GraphViewer : IGraphViewer, IGraphBinding, INotifyPropertyChanged private IHighlighting _activeHighlighting = new EdgeHoveredHighlighting(); + ClickController? _clickController; + /// /// Held to read the help /// @@ -64,9 +65,15 @@ public void Bind(Panel graphPanel) _msaglViewer.BindToPanel(graphPanel); _msaglViewer.ObjectUnderMouseCursorChanged += ObjectUnderMouseCursorChanged; - _msaglViewer.MouseDown += Viewer_MouseDown!; + + _clickController = new ClickController(_msaglViewer); + + _clickController.LeftDoubleClick += OnLeftDoubleClick; + _clickController.LeftSingleClick += OnLeftSingleClick; + _clickController.OpenContextMenu += OnOpenContextMenu; } + public void ShowFlatGraph(bool value) { _showFlatGraph = value; @@ -336,6 +343,76 @@ public void LoadSession(CodeGraph newGraph, PresentationState? presentationState public event PropertyChangedEventHandler? PropertyChanged; + private void OnOpenContextMenu(IViewerObject? obj) + { + if (_msaglViewer?.ObjectUnderMouseCursor is IViewerNode clickedObject) + { + // Click on specific node + var node = clickedObject.Node; + var contextMenu = new ContextMenu(); + var element = GetCodeElementFromUserData(node); + AddToContextMenuEntries(element, contextMenu); + if (contextMenu.Items.Count > 0) + { + contextMenu.IsOpen = true; + } + } + else if (_msaglViewer?.ObjectUnderMouseCursor is IViewerEdge viewerEdge) + { + // Click on specific edge + var edge = viewerEdge.Edge; + var contextMenu = new ContextMenu(); + var relationships = GetRelationshipsFromUserData(edge); + AddContextMenuEntries(relationships, contextMenu); + if (contextMenu.Items.Count > 0) + { + contextMenu.IsOpen = true; + } + } + else + { + // Click on free space + ShowGlobalContextMenu(); + } + } + + private void OnLeftSingleClick(IViewerObject? obj) + { + if (obj == null) + { + // Release the fixed info if we click on empty space + _clickedObject = null; + } + else + { + // User wants to "hold" the current quick info. + _clickedObject = obj; + } + + UpdateQuickInfoPanel(obj); + } + + private void OnLeftDoubleClick(IViewerObject? obj) + { + // Execute double click action if any registered + if (obj is IViewerNode clickedObject) + { + var node = clickedObject.Node; + var element = GetCodeElementFromUserData(node); + if (element is not null) + { + foreach (var cmd in _nodeCommands) + { + if (cmd.IsDoubleClickable && cmd.CanHandle(element)) + { + cmd.Invoke(element); + return; + } + } + } + } + } + private void RefreshFlagsWithoutLayout(List ids, bool isFlagged) { foreach (var id in ids) @@ -425,8 +502,6 @@ private void RefreshGraph() } } - - private void ObjectUnderMouseCursorChanged(object? sender, ObjectUnderMouseCursorChangedEventArgs e) { if (_clickedObject is null) @@ -454,121 +529,6 @@ private void UpdateQuickInfoPanel(IViewerObject? obj) _publisher.Publish(new QuickInfoUpdate(quickInfo)); } - private void Viewer_MouseDown(object sender, MsaglMouseEventArgs e) - { - bool IsShiftPressed() - { - return Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift); - } - - bool IsCtrlPressed() - { - return Keyboard.IsKeyDown(Key.LeftCtrl); - } - - if (TryEmulateDoubleClick(e)) - { - return; - } - - if (e.LeftButtonIsPressed) - { - var obj = _msaglViewer?.ObjectUnderMouseCursor; - if (obj == null || !IsShiftPressed() && !IsCtrlPressed()) - { - // Release the fixed info if we click on empty space or click - // anywhere without holding shift or ctrl. - _clickedObject = null; - UpdateQuickInfoPanel(obj); - return; - } - - if (IsCtrlPressed()) - { - // User wants to "hold" the current quick info. - _clickedObject = obj; - UpdateQuickInfoPanel(obj); - return; - } - } - - if (e.RightButtonIsPressed) - { - if (_msaglViewer?.ObjectUnderMouseCursor is IViewerNode clickedObject) - { - // Click on specific node - var node = clickedObject.Node; - var contextMenu = new ContextMenu(); - var element = GetCodeElementFromUserData(node); - AddToContextMenuEntries(element, contextMenu); - if (contextMenu.Items.Count > 0) - { - contextMenu.IsOpen = true; - } - } - else if (_msaglViewer?.ObjectUnderMouseCursor is IViewerEdge viewerEdge) - { - // Click on specific edge - var edge = viewerEdge.Edge; - var contextMenu = new ContextMenu(); - var relationships = GetRelationshipsFromUserData(edge); - AddContextMenuEntries(relationships, contextMenu); - if (contextMenu.Items.Count > 0) - { - contextMenu.IsOpen = true; - } - } - else - { - // Click on free space - ShowGlobalContextMenu(); - } - } - else - { - e.Handled = false; - } - } - - private bool TryEmulateDoubleClick(MsaglMouseEventArgs e) - { - if (e.LeftButtonIsPressed) - { - var elapsed = _clickStopwatch.ElapsedMilliseconds; - if (elapsed > 0 && elapsed < Constants.DoubleClickMilliseconds) - { - OnDoubleClick(e); - _clickStopwatch.Restart(); - return true; - } - - _clickStopwatch.Restart(); - } - - return false; - } - - private void OnDoubleClick(MsaglMouseEventArgs args) - { - // Execute double click action if any registered - if (_msaglViewer?.ObjectUnderMouseCursor is IViewerNode clickedObject) - { - var node = clickedObject.Node; - var element = GetCodeElementFromUserData(node); - if (element is not null) - { - foreach (var cmd in _nodeCommands) - { - if (cmd.IsDoubleClickable && cmd.CanHandle(element)) - { - cmd.Invoke(element); - return; - } - } - } - } - } - private void AddContextMenuEntries(List relationships, ContextMenu contextMenu) { if (relationships.Count == 0) diff --git a/CSharpCodeAnalyst/InfoPanel/InfoPanel.xaml b/CSharpCodeAnalyst/InfoPanel/InfoPanel.xaml index e9e0413..0d7f9d4 100644 --- a/CSharpCodeAnalyst/InfoPanel/InfoPanel.xaml +++ b/CSharpCodeAnalyst/InfoPanel/InfoPanel.xaml @@ -10,15 +10,33 @@ d:DesignHeight="450" d:DesignWidth="300"> + + + + + + + + - + - - - - - - - + + - + - - + + - - - - - - - + + @@ -88,11 +93,12 @@ Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DataContext.OpenSourceLocationCommand}" CommandParameter="{Binding}" /> - + - - + + diff --git a/CSharpCodeAnalyst/InfoPanel/InfoPanel.xaml.cs b/CSharpCodeAnalyst/InfoPanel/InfoPanel.xaml.cs index 1f0c08e..25258df 100644 --- a/CSharpCodeAnalyst/InfoPanel/InfoPanel.xaml.cs +++ b/CSharpCodeAnalyst/InfoPanel/InfoPanel.xaml.cs @@ -8,16 +8,6 @@ namespace CSharpCodeAnalyst.InfoPanel /// public partial class InfoPanel : UserControl { - public static readonly DependencyProperty IsVisibleProperty = - DependencyProperty.Register("IsVisible", typeof(bool), typeof(InfoPanel), - new PropertyMetadata(true, OnIsVisibleChanged)); - - public bool IsVisible - { - get => (bool)GetValue(IsVisibleProperty); - set => SetValue(IsVisibleProperty, value); - } - public InfoPanel() { InitializeComponent(); diff --git a/CSharpCodeAnalyst/InfoPanel/InfoPanelViewModel.cs b/CSharpCodeAnalyst/InfoPanel/InfoPanelViewModel.cs index fb444dc..91a4b5e 100644 --- a/CSharpCodeAnalyst/InfoPanel/InfoPanelViewModel.cs +++ b/CSharpCodeAnalyst/InfoPanel/InfoPanelViewModel.cs @@ -5,7 +5,6 @@ using System.Windows.Input; using Contracts.Graph; using CSharpCodeAnalyst.Common; -using CSharpCodeAnalyst.Configuration; using CSharpCodeAnalyst.Help; using CSharpCodeAnalyst.Resources; using Prism.Commands; @@ -15,38 +14,17 @@ namespace CSharpCodeAnalyst.InfoPanel; internal class InfoPanelViewModel : INotifyPropertyChanged { private bool _hide; - private bool _isInfoPanelVisible; private List _quickInfo = QuickInfoFactory.NoInfoProviderRegistered; - public InfoPanelViewModel(ApplicationSettings settings) + public InfoPanelViewModel() { - _isInfoPanelVisible = settings.DefaultShowQuickHelp; OpenSourceLocationCommand = new DelegateCommand(OpenSourceLocation); + Hide(true); } public ICommand OpenSourceLocationCommand { get; } - public bool IsInfoPanelVisible - { - get => _isInfoPanelVisible; - set - { - if (_isInfoPanelVisible == value) - { - return; - } - - _isInfoPanelVisible = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(IsInfoPanelVisibleEffective)); - } - } - - public bool IsInfoPanelVisibleEffective - { - get => IsInfoPanelVisible && !_hide; - } public List QuickInfo @@ -100,7 +78,7 @@ private void OpenSourceLocation(SourceLocation? location) public void HandleUpdateQuickInfo(QuickInfoUpdate quickInfoUpdate) { // May come from any view - if (IsInfoPanelVisible is false) + if (_hide) { // This can be very slow if updated even the help is not visible. return; @@ -117,15 +95,14 @@ protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName public void Clear() { _quickInfo = QuickInfoFactory.NoInfoProviderRegistered; - OnPropertyChanged(nameof(IsInfoPanelVisibleEffective)); } /// - /// Hide the info panel temporarily for example when the wong page is shown.. + /// Hide the info panel temporarily when not visible. + /// This does not waste computation if the info panel is hidden. /// public void Hide(bool hide) { _hide = hide; - OnPropertyChanged(nameof(IsInfoPanelVisibleEffective)); } } \ No newline at end of file diff --git a/CSharpCodeAnalyst/MainViewModel.cs b/CSharpCodeAnalyst/MainViewModel.cs index fd38d80..bd1d008 100644 --- a/CSharpCodeAnalyst/MainViewModel.cs +++ b/CSharpCodeAnalyst/MainViewModel.cs @@ -38,6 +38,8 @@ namespace CSharpCodeAnalyst; internal class MainViewModel : INotifyPropertyChanged { + + private const int INFO_PANEL_TAB_INDEX = 2; private readonly int _maxDegreeOfParallelism; private readonly MessageBus _messaging; @@ -64,9 +66,10 @@ internal class MainViewModel : INotifyPropertyChanged private LegendDialog? _openedLegendDialog; private SearchViewModel? _searchViewModel; + private int _selectedLeftTabIndex; - private int _selectedTabIndex; + private int _selectedRightTabIndex; private TreeViewModel? _treeViewModel; @@ -99,7 +102,7 @@ internal MainViewModel(MessageBus messaging, ApplicationSettings settings) OpenFilterDialogCommand = new DelegateCommand(OpenFilterDialog); OpenSettingsDialogCommand = new DelegateCommand(OpenSettingsDialog); ExportToPngCommand = new DelegateCommand(ExportToPng); - + CopyToExplorerGraphCommand = new DelegateCommand(CopyToExplorerGraph); _loadMessage = string.Empty; @@ -207,19 +210,18 @@ public SearchViewModel? SearchViewModel } - public int SelectedTabIndex + public int SelectedRightTabIndex { - get => _selectedTabIndex; + get => _selectedRightTabIndex; set { - if (value == _selectedTabIndex) + if (value == _selectedRightTabIndex) { return; } - _selectedTabIndex = value; - OnPropertyChanged(nameof(SelectedTabIndex)); - InfoPanelViewModel.Hide(_selectedTabIndex != 0); + _selectedRightTabIndex = value; + OnPropertyChanged(nameof(SelectedRightTabIndex)); } } @@ -253,6 +255,18 @@ public ObservableCollection Metrics public InfoPanelViewModel InfoPanelViewModel { get; set; } + public int SelectedLeftTabIndex + { + get => _selectedLeftTabIndex; + set + { + if (value == _selectedLeftTabIndex) return; + _selectedLeftTabIndex = value; + InfoPanelViewModel.Hide(value != INFO_PANEL_TAB_INDEX); + OnPropertyChanged(nameof(SelectedLeftTabIndex)); + } + } + public event PropertyChangedEventHandler? PropertyChanged; @@ -470,7 +484,7 @@ private async void FindCycles() if (cycleGroups != null) { _messaging.Publish(new CycleCalculationComplete(cycleGroups)); - SelectedTabIndex = 1; + SelectedRightTabIndex = 1; } } @@ -718,11 +732,6 @@ private void LoadProject() // Load settings - if (projectData.Settings.TryGetValue(nameof(InfoPanelViewModel.IsInfoPanelVisible), out var isInfoPanelVisibleString)) - { - InfoPanelViewModel.IsInfoPanelVisible = bool.Parse(isInfoPanelVisibleString); - } - if (GraphViewModel != null) { @@ -779,7 +788,6 @@ private void SaveProject() var projectData = new ProjectData(); projectData.SetCodeGraph(_codeGraph); projectData.SetGallery(_gallery ?? new Gallery.Gallery()); - projectData.Settings[nameof(InfoPanelViewModel.IsInfoPanelVisible)] = InfoPanelViewModel.IsInfoPanelVisible.ToString(); projectData.Settings[nameof(GraphViewModel.ShowFlatGraph)] = _graphViewModel.ShowFlatGraph.ToString(); projectData.Settings[nameof(GraphViewModel.ShowDataFlow)] = _graphViewModel.ShowDataFlow.ToString(); projectData.Settings[nameof(ProjectExclusionRegExCollection)] = _projectExclusionFilters.ToString(); @@ -802,7 +810,7 @@ private void CopyToExplorerGraph(CycleGroupViewModel vm) GraphViewModel?.ImportCycleGroup(graph.Clone()); - SelectedTabIndex = 0; + SelectedRightTabIndex = 0; } public void HandleDeleteFromModel(DeleteFromModelRequest request) diff --git a/CSharpCodeAnalyst/MainWindow.xaml b/CSharpCodeAnalyst/MainWindow.xaml index 6b6f951..dbe6829 100644 --- a/CSharpCodeAnalyst/MainWindow.xaml +++ b/CSharpCodeAnalyst/MainWindow.xaml @@ -161,8 +161,6 @@ - - @@ -178,17 +176,18 @@ - + - + - @@ -197,31 +196,23 @@ - - - - - @@ -280,6 +271,7 @@ @@ -465,6 +457,15 @@ + + + + + + + @@ -479,7 +480,7 @@ - + Code Explorer @@ -487,7 +488,6 @@ Visibility="{Binding IsCanvasHintsVisible, Converter={StaticResource BooleanToVisibilityConverter}}" HorizontalAlignment="Center" VerticalAlignment="Center" Panel.ZIndex="10"> - @@ -587,10 +587,7 @@ - - + diff --git a/CSharpCodeAnalyst/Resources/Strings.Designer.cs b/CSharpCodeAnalyst/Resources/Strings.Designer.cs index 03aa0b8..0dae8cb 100644 --- a/CSharpCodeAnalyst/Resources/Strings.Designer.cs +++ b/CSharpCodeAnalyst/Resources/Strings.Designer.cs @@ -475,7 +475,7 @@ public static string FlatGraph_Tooltip { } /// - /// Looks up a localized string similar to Show Information Flow. + /// Looks up a localized string similar to Show Flow. /// public static string Flow_Label { get { @@ -600,6 +600,15 @@ public static string ImportSolution_Label { } } + /// + /// Looks up a localized string similar to Info. + /// + public static string InfoPanel_TabHeader { + get { + return ResourceManager.GetString("InfoPanel_TabHeader", resourceCulture); + } + } + /// /// Looks up a localized string similar to Filters cannot contain semicolons (;).. /// diff --git a/CSharpCodeAnalyst/Resources/Strings.resx b/CSharpCodeAnalyst/Resources/Strings.resx index c74b39d..c86b5bb 100644 --- a/CSharpCodeAnalyst/Resources/Strings.resx +++ b/CSharpCodeAnalyst/Resources/Strings.resx @@ -429,7 +429,7 @@ - Show Information Flow + Show Flow Shows the information flow instead of dependencies between code elements @@ -574,5 +574,8 @@ Use ' ' for AND, '|' for OR condition. If you want to search for type colum use type:xxx - + + + Info + \ No newline at end of file diff --git a/CSharpCodeAnalyst/appsettings.json b/CSharpCodeAnalyst/appsettings.json index b76d8a4..3fd9600 100644 --- a/CSharpCodeAnalyst/appsettings.json +++ b/CSharpCodeAnalyst/appsettings.json @@ -2,7 +2,6 @@ "ApplicationSettings": { "WarningCodeElementLimit": 200, "DefaultProjectExcludeFilter": ".*Tests", - "DefaultShowQuickHelp": false, "MaxDegreeOfParallelism": 8, "AutomaticallyAddContainingType": false } diff --git a/Images/code-explorer.png b/Images/code-explorer.png index d1fe17c..5f89bda 100644 Binary files a/Images/code-explorer.png and b/Images/code-explorer.png differ diff --git a/Images/cycle-graph.png b/Images/cycle-graph.png index a0cf85e..8a59e83 100644 Binary files a/Images/cycle-graph.png and b/Images/cycle-graph.png differ diff --git a/README.md b/README.md index ffbe77f..2bad856 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,9 @@ Note: MSBuild must be installed on your computer for the application to work. ![image-20240731123233438](Images/code-explorer.png) -- Use the tree view to add code elements to the canvas. +- Use the tree or search view to add code elements to the canvas. - You can explore relationships between code elements using the context menu on a code element. -- Use the context menu to automatically connect all code elements in the space around the graph. -- Pressing **Control + Left Mouse Click** will keep the quick help for the clicked element active. -- To pan, press **Shift + Left Mouse Button** and move the mouse in empty canvas space. - +- Use the context menu in the space around the graph to automatically connect all code elements. - You can export graphs to DGML for further analysis in Visual Studio. ## Find and visualize cycles in your codebase