diff --git a/ULogViewer/Controls/SessionView.axaml.LogAnalysis.cs b/ULogViewer/Controls/SessionView.axaml.LogAnalysis.cs index 214da0ef..16c5cc5a 100644 --- a/ULogViewer/Controls/SessionView.axaml.LogAnalysis.cs +++ b/ULogViewer/Controls/SessionView.axaml.LogAnalysis.cs @@ -47,11 +47,13 @@ partial class SessionView bool isPointerPressedOnLogAnalysisResultListBox; bool isSmoothScrollingToLatestLogAnalysisResult; readonly Avalonia.Controls.ListBox keyLogAnalysisRuleSetListBox; + long lastLogAnalysisResultUpdateTime; IDisposable? logAnalysisPanelVisibilityObserverToken = EmptyDisposable.Default; readonly Avalonia.Controls.ListBox logAnalysisResultListBox; ScrollViewer? logAnalysisResultScrollViewer; readonly ToggleButton logAnalysisRuleSetsButton; readonly Popup logAnalysisRuleSetsPopup; + readonly Queue logAnalysisResultUpdateTimeQueue = new(LogUpdateIntervalStatisticCount); readonly Avalonia.Controls.ListBox logAnalysisScriptSetListBox; LogAnalysisScriptSetSelectionContextMenu? logAnalysisScriptSetSelectionContextMenu; readonly Avalonia.Controls.ListBox operationCountingAnalysisRuleSetListBox; @@ -859,18 +861,39 @@ void OnLogAnalysisResultsChanged(object? sender, NotifyCollectionChangedEventArg switch (e.Action) { case NotifyCollectionChangedAction.Add: - if (this.IsScrollingToLatestLogAnalysisResultNeeded) + // statistic update time + var currentTime = this.stopwatch.ElapsedMilliseconds; + if (this.lastLogAnalysisResultUpdateTime > 0) { - if (!this.scrollToLatestLogAnalysisResultAction.IsScheduled) + var interval = (currentTime - this.lastLogAnalysisResultUpdateTime); + if (interval >= LogUpdateIntervalToResetStatistic) + this.logAnalysisResultUpdateTimeQueue.Clear(); + else { - if (this.isSmoothScrollingToLatestLogAnalysisResult) - { - this.isSmoothScrollingToLatestLogAnalysisResult = false; - this.smoothScrollToLatestLogAnalysisResultAction.Reschedule(ScrollingToLatestLogInterval); - } - else - this.smoothScrollToLatestLogAnalysisResultAction.Schedule(ScrollingToLatestLogInterval); + while (this.logAnalysisResultUpdateTimeQueue.Count >= LogUpdateIntervalStatisticCount) + this.logAnalysisResultUpdateTimeQueue.Dequeue(); + this.logAnalysisResultUpdateTimeQueue.Enqueue(interval); + } + } + this.lastLogAnalysisResultUpdateTime = currentTime; + + // trigger scrolling to latest result + if (this.IsScrollingToLatestLogAnalysisResultNeeded && !this.scrollToLatestLogAnalysisResultAction.IsScheduled) + { + // select scrolling interval + var averageInternal = this.logAnalysisResultUpdateTimeQueue.Sum() / (double)this.logAnalysisResultUpdateTimeQueue.Count; + var scrollingInterval = averageInternal >= ScrollingToLatestLogInterval + ? ScrollingToLatestLogInterval + : SlowScrollingToLatestLogInterval; + + // scroll + if (this.isSmoothScrollingToLatestLogAnalysisResult) + { + this.isSmoothScrollingToLatestLogAnalysisResult = false; + this.smoothScrollToLatestLogAnalysisResultAction.Reschedule(scrollingInterval); } + else + this.smoothScrollToLatestLogAnalysisResultAction.Schedule(scrollingInterval); } break; case NotifyCollectionChangedAction.Remove: diff --git a/ULogViewer/Controls/SessionView.axaml.cs b/ULogViewer/Controls/SessionView.axaml.cs index 5321be7c..c90af67f 100644 --- a/ULogViewer/Controls/SessionView.axaml.cs +++ b/ULogViewer/Controls/SessionView.axaml.cs @@ -39,6 +39,7 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -157,8 +158,11 @@ enum LogDataSourceType // Constants. const int InitScrollingToLatestLogDelay = 800; + const int LogUpdateIntervalStatisticCount = 4; + const int LogUpdateIntervalToResetStatistic = 1000; const int ScrollingToLatestLogInterval = 200; const int ScrollingToTargetLogRangeInterval = 200; + const int SlowScrollingToLatestLogInterval = 400; const int SmoothScrollingToLatestLogInterval = 66; @@ -235,6 +239,7 @@ enum LogDataSourceType bool isWorkingDirNeededAfterLogProfileSet; bool keepSidePanelVisible; Control? lastClickedLogPropertyView; + long lastLogUpdateTime; double lastToolBarWidthWhenLayoutItems; int latestDisplayedLogCount; DisplayableLog[]? latestDisplayedLogRange; @@ -259,6 +264,7 @@ enum LogDataSourceType ScrollViewer? logScrollViewer; readonly ToggleButton logsShowingModeButton; readonly ContextMenu logsShowingModeMenu; + readonly Queue logUpdateTimeQueue = new(LogUpdateIntervalStatisticCount); readonly Avalonia.Controls.ListBox markedLogListBox; IDisposable? markedLogsPanelVisibilityObserverToken = EmptyDisposable.Default; readonly double minLogListBoxSizeToCloseSidePanel; @@ -276,6 +282,7 @@ enum LogDataSourceType readonly ColumnDefinition sidePanelColumn; readonly Control sidePanelContainer; readonly ScheduledAction smoothScrollToLatestLogAction; + readonly Stopwatch stopwatch = new Stopwatch().Also(it => it.Start()); int targetLogRangeScrollingDirectionChangeCount; DisplayableLog[]? targetLogRangeToScrollTo; readonly List targetMarkedLogsToScrollTo = new(); @@ -2474,6 +2481,8 @@ void DetachFromSession(Session session) this.updateLatestDisplayedLogRangeAction.Execute(); // stop auto scrolling + this.lastLogUpdateTime = 0; + this.logUpdateTimeQueue.Clear(); this.isSmoothScrollingToLatestLog = false; this.isSmoothScrollingToLatestLogAnalysisResult = false; this.scrollToLatestLogAction.Cancel(); @@ -3523,20 +3532,41 @@ void OnLogsChanged(object? sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Add) { + // statistic update time + var currentTime = this.stopwatch.ElapsedMilliseconds; + if (this.lastLogUpdateTime > 0) + { + var interval = (currentTime - this.lastLogUpdateTime); + if (interval >= LogUpdateIntervalToResetStatistic) + this.logUpdateTimeQueue.Clear(); + else + { + while (this.logUpdateTimeQueue.Count >= LogUpdateIntervalStatisticCount) + this.logUpdateTimeQueue.Dequeue(); + this.logUpdateTimeQueue.Enqueue(interval); + } + } + this.lastLogUpdateTime = currentTime; + + // trigger scrolling to target/latest log if (this.targetLogRangeToScrollTo != null) this.scrollToTargetLogRangeAction.Schedule(ScrollingToTargetLogRangeInterval); - else if (this.IsScrollingToLatestLogNeeded) + else if (this.IsScrollingToLatestLogNeeded && !this.scrollToLatestLogAction.IsScheduled) { - if (!this.scrollToLatestLogAction.IsScheduled) + // select scrolling interval + var averageInternal = this.logUpdateTimeQueue.Sum() / (double)this.logUpdateTimeQueue.Count; + var scrollingInterval = averageInternal >= ScrollingToLatestLogInterval + ? ScrollingToLatestLogInterval + : SlowScrollingToLatestLogInterval; + + // scroll + if (this.isSmoothScrollingToLatestLog) { - if (this.isSmoothScrollingToLatestLog) - { - this.isSmoothScrollingToLatestLog = false; - this.smoothScrollToLatestLogAction.Reschedule(ScrollingToLatestLogInterval); - } - else - this.smoothScrollToLatestLogAction.Schedule(ScrollingToLatestLogInterval); + this.isSmoothScrollingToLatestLog = false; + this.smoothScrollToLatestLogAction.Reschedule(scrollingInterval); } + else + this.smoothScrollToLatestLogAction.Schedule(scrollingInterval); } } else if (e.Action == NotifyCollectionChangedAction.Remove) @@ -4197,6 +4227,8 @@ void OnSessionPropertyChanged(object? sender, PropertyChangedEventArgs e) case nameof(Session.HasLogs): if (!session.HasLogs) { + this.lastLogUpdateTime = 0; + this.logUpdateTimeQueue.Clear(); this.isSmoothScrollingToLatestLog = false; this.scrollToLatestLogAction.Cancel(); this.smoothScrollToLatestLogAction.Cancel();