diff --git a/TuneUp/Properties/Resources.Designer.cs b/TuneUp/Properties/Resources.Designer.cs index 703ee9e..5830686 100644 --- a/TuneUp/Properties/Resources.Designer.cs +++ b/TuneUp/Properties/Resources.Designer.cs @@ -349,6 +349,15 @@ public static string ToolTip_RunAll { } } + /// + /// Looks up a localized string similar to Custom Node mode does not support Run All.. + /// + public static string ToolTip_RunAllDisabled { + get { + return ResourceManager.GetString("ToolTip_RunAllDisabled", resourceCulture); + } + } + /// /// Looks up a localized string similar to Combined execution time of latest and previous run.. /// diff --git a/TuneUp/Properties/Resources.en-US.resx b/TuneUp/Properties/Resources.en-US.resx index 89cb216..43272a2 100644 --- a/TuneUp/Properties/Resources.en-US.resx +++ b/TuneUp/Properties/Resources.en-US.resx @@ -214,6 +214,9 @@ Execute entire graph, including nodes with no change since the latest run. + + Custom Node mode does not support Run All. + Combined execution time of latest and previous run. diff --git a/TuneUp/Properties/Resources.resx b/TuneUp/Properties/Resources.resx index 813b0d5..ec2a143 100644 --- a/TuneUp/Properties/Resources.resx +++ b/TuneUp/Properties/Resources.resx @@ -214,6 +214,9 @@ Execute entire graph, including nodes with no change since the latest run. + + Custom Node mode does not support Run All. + Combined execution time of latest and previous run. diff --git a/TuneUp/TuneUpWindow.xaml b/TuneUp/TuneUpWindow.xaml index 26b05f8..e840a62 100644 --- a/TuneUp/TuneUpWindow.xaml +++ b/TuneUp/TuneUpWindow.xaml @@ -345,6 +345,9 @@ + + + @@ -483,10 +486,11 @@ Content="{x:Static resx:Resources.Button_RunAll}" Cursor="Hand" IsEnabled="{Binding Path=IsRecomputeEnabled}" - Style="{StaticResource ButtonStyleTuneUp}" > + Style="{StaticResource ButtonStyleTuneUp}" + ToolTipService.ShowOnDisabled="True" > - + diff --git a/TuneUp/TuneUpWindowViewModel.cs b/TuneUp/TuneUpWindowViewModel.cs index 2c1ceba..27de2a2 100644 --- a/TuneUp/TuneUpWindowViewModel.cs +++ b/TuneUp/TuneUpWindowViewModel.cs @@ -79,6 +79,9 @@ public class TuneUpWindowViewModel : NotificationObject, IDisposable private HashSet tempProfiledNodesLatestRun = new HashSet(); private HashSet tempProfiledNodesPreviousRun = new HashSet(); private HashSet tempProfiledNodesNotExecuted = new HashSet(); + private bool suppressNodeReset = false; + private IWorkspaceModel previousWorkspace; + private readonly WorkspaceProfilingData cachedData = new WorkspaceProfilingData(); private HomeWorkspaceModel CurrentWorkspace { @@ -121,6 +124,7 @@ private set { isRecomputeEnabled = value; RaisePropertyChanged(nameof(IsRecomputeEnabled)); + RaisePropertyChanged(nameof(RunAllTooltipMessage)); } } } @@ -281,6 +285,7 @@ public string SortingOrder public const string SortByName = "name"; public const string SortByNumber = "number"; public const string SortByTime = "time"; + public string RunAllTooltipMessage => IsRecomputeEnabled ? Resources.ToolTip_RunAll : Resources.ToolTip_RunAllDisabled; #endregion @@ -315,6 +320,14 @@ internal void ResetProfiledNodes() Task.Run(() => { uiContext.Post(_ => { + if (suppressNodeReset) + { + // Skip resetting nodes and directly refresh the UI + isProfilingEnabled = true; + RefreshUIAfterReset(); + return; + } + // Initialize collections and dictionaries InitializeCollectionsAndDictionaries(); @@ -1013,8 +1026,36 @@ private void CurrentWorkspaceModel_GroupRemoved(AnnotationModel group) private void OnCurrentWorkspaceChanged(IWorkspaceModel workspace) { - // Profiling needs to be enabled per workspace so mark it false after switching + // Reset suppression flag + suppressNodeReset = false; + + // Handle transitions based on the types of the current and previous workspaces + if (workspace is CustomNodeWorkspaceModel) + { + if (previousWorkspace is HomeWorkspaceModel) + { + // Cache data when moving from HomeWorkspace to CustomNodeWorkspace + CacheWorkspaceData(); + } + } + else if (workspace is HomeWorkspaceModel) + { + if (previousWorkspace is CustomNodeWorkspaceModel) + { + // Restore data when moving from CustomNodeWorkspace to HomeWorkspace + suppressNodeReset = true; + RestoreWorkspaceData(); + } + } + + // Profiling needs to be enabled per workspace, so mark it false after switching isProfilingEnabled = false; + + // Disable IsRecomputeEnabled if the workspace is a CustomNodeWorkspaceModel + IsRecomputeEnabled = !(workspace is CustomNodeWorkspaceModel); + + // Update the previous and current workspace references + previousWorkspace = workspace; CurrentWorkspace = workspace as HomeWorkspaceModel; } @@ -1022,11 +1063,127 @@ private void OnCurrentWorkspaceCleared(IWorkspaceModel workspace) { // Profiling needs to be enabled per workspace so mark it false after closing isProfilingEnabled = false; + suppressNodeReset = false; + ClearCacheWorkspaceData(); CurrentWorkspace = viewLoadedParams.CurrentWorkspaceModel as HomeWorkspaceModel; } #endregion + #region Cache + + /// + /// Represents cached profiling data for a workspace. Includes node collections and execution times. + /// + private class WorkspaceProfilingData + { + /// + /// Guid to map graphs with cached data + /// + public Guid GraphGuid { get; set; } + /// + /// Collection to cache nodes executed in the latest run of the graph. + /// + public ObservableCollection LatestRunNodes { get; set; } = new(); + /// + /// Collection to cache nodes executed in the previous run of the graph. + /// + public ObservableCollection PreviousRunNodes { get; set; } = new(); + // + /// Collection to cache nodes that were not executed in the graph. + /// + public ObservableCollection NotExecutedNodes { get; set; } = new(); + /// + /// String to cache the Total execution time for the graph across all runs. + /// + public string TotalGraphExecutionTime { get; set; } + /// + /// String to cache the Execution time for the latest graph run. + /// + public string LatestGraphExecutionTime { get; set; } + /// + /// String to cache the Execution time for the previous graph run. + /// + public string PreviousGraphExecutionTime { get; set; } + } + + /// + /// Caches the current workspace data, including nodes, execution times, and clears old collections. + /// + private void CacheWorkspaceData() + { + // Ensure collections are initialized + if (ProfiledNodesLatestRun == null) + ProfiledNodesLatestRun = new ObservableCollection(); + if (ProfiledNodesPreviousRun == null) + ProfiledNodesPreviousRun = new ObservableCollection(); + if (ProfiledNodesNotExecuted == null) + ProfiledNodesNotExecuted = new ObservableCollection(); + + // Save the current data into the cache + cachedData.GraphGuid = CurrentWorkspace?.Guid ?? Guid.Empty; + cachedData.LatestRunNodes = new ObservableCollection(ProfiledNodesLatestRun); + cachedData.PreviousRunNodes = new ObservableCollection(ProfiledNodesPreviousRun); + cachedData.NotExecutedNodes = new ObservableCollection(ProfiledNodesNotExecuted); + cachedData.LatestGraphExecutionTime = LatestGraphExecutionTime ?? Resources.Label_DefaultExecutionTime; + cachedData.PreviousGraphExecutionTime = PreviousGraphExecutionTime ?? Resources.Label_DefaultExecutionTime; + cachedData.TotalGraphExecutionTime = TotalGraphExecutionTime ?? Resources.Label_DefaultExecutionTime; + + // Clear the old collections + ProfiledNodesLatestRun.Clear(); + ProfiledNodesPreviousRun.Clear(); + ProfiledNodesNotExecuted.Clear(); + LatestGraphExecutionTime = PreviousGraphExecutionTime = TotalGraphExecutionTime = defaultExecutionTime; + + // Refresh the UI + RefreshAllCollectionViews(); + UpdateTableVisibility(); + } + + /// + /// Restores cached workspace data to the current workspace and updates the UI. + /// + private void RestoreWorkspaceData() + { + // Safety check: Ensure cached data is not null + cachedData.LatestRunNodes ??= new ObservableCollection(); + cachedData.PreviousRunNodes ??= new ObservableCollection(); + cachedData.NotExecutedNodes ??= new ObservableCollection(); + cachedData.LatestGraphExecutionTime ??= Resources.Label_DefaultExecutionTime; + cachedData.PreviousGraphExecutionTime ??= Resources.Label_DefaultExecutionTime; + cachedData.TotalGraphExecutionTime ??= Resources.Label_DefaultExecutionTime; + + // Restore cached data + ProfiledNodesLatestRun = new ObservableCollection(cachedData.LatestRunNodes); + ProfiledNodesPreviousRun = new ObservableCollection(cachedData.PreviousRunNodes); + ProfiledNodesNotExecuted = new ObservableCollection(cachedData.NotExecutedNodes); + LatestGraphExecutionTime = cachedData.LatestGraphExecutionTime; + PreviousGraphExecutionTime = cachedData.PreviousGraphExecutionTime; + TotalGraphExecutionTime = cachedData.TotalGraphExecutionTime; + + ClearCacheWorkspaceData(); + + // Refresh the UI + RefreshAllCollectionViews(); + UpdateTableVisibility(); + } + + /// + /// Clears the cached workspace data, resetting all collections and execution times to default values. + /// + private void ClearCacheWorkspaceData() + { + cachedData.GraphGuid = Guid.Empty; + cachedData.LatestRunNodes = new ObservableCollection(); + cachedData.PreviousRunNodes = new ObservableCollection(); + cachedData.NotExecutedNodes = new ObservableCollection(); + cachedData.LatestGraphExecutionTime = defaultExecutionTime; + cachedData.PreviousGraphExecutionTime = defaultExecutionTime; + cachedData.TotalGraphExecutionTime = defaultExecutionTime; + } + + #endregion + #region Helpers /// @@ -1214,11 +1371,7 @@ public void ApplyCustomSortingToAllCollections() { ApplyCustomSorting(ProfiledNodesCollectionLatestRun); ApplyCustomSorting(ProfiledNodesCollectionPreviousRun); - // Apply custom sorting to NotExecuted collection only if sortingOrder is "name" - if (defaultSortingOrder == SortByName) - { - ApplyCustomSorting(ProfiledNodesCollectionNotExecuted); - } + ApplyCustomSorting(ProfiledNodesCollectionNotExecuted, SortByName); } /// @@ -1377,8 +1530,12 @@ private void RefreshUIAfterReset() ProfiledNodesCollectionNotExecuted = new CollectionViewSource { Source = ProfiledNodesNotExecuted }; // Refresh UI by raising property changes and applying sorting/filtering + RaisePropertyChanged(nameof(ProfiledNodesCollectionLatestRun)); + RaisePropertyChanged(nameof(ProfiledNodesCollectionPreviousRun)); RaisePropertyChanged(nameof(ProfiledNodesCollectionNotExecuted)); - ApplyCustomSorting(ProfiledNodesCollectionNotExecuted, SortByName); + + ApplyCustomSortingToAllCollections(); + ApplyGroupNodeFilter(); UpdateTableVisibility(); }