1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-11-26 06:49:19 +00:00

Minor reshuffling

This was an attempt to add a "loading..." dialog during the initial
open of the project.  This can have some lag because we create a
sandbox (which is currently taking about 300ms) and do a full project
refresh (which can take more than a second on a large 65816 project).

The trick is that we need to do these things on a background thread
while the main thread manages the UI.  We can't manipulate the UI
from the background thread.  For the most part this works, as the
project refresh stuff isn't tied to the UI, but we run into trouble
when generating the line list.  As currently implemented, the line
generator interacts directly with DisplayList, which is acting as an
ItemsSource for the main ListView.

To make this work correctly we'd need to dissociate DisplayList from
LineListGen, e.g. by having DisplayList record but defer changes
until a "go" method is called on the main thread.

The speed is only an issue for large programs, which aren't really
supported yet -- the UI is awkward to use with large files -- so I'm
not going to pursue this further for now.

Also, an unrelated fix: there was an issue where the current ListView
scroll position would be retained if you opened a project while one
was already open.  Harmless but weird.  We now scroll to the top.
This commit is contained in:
Andy McFadden 2019-07-19 15:24:51 -07:00
parent 84f4075ad4
commit be0e6bead1
3 changed files with 102 additions and 54 deletions

View File

@ -606,18 +606,41 @@ namespace SourceGenWPF {
return true;
}
#if false
private class FinishPrepProgress : WorkProgress.IWorker {
public string ExtMessages { get; private set; }
private MainController mMainCtrl;
public FinishPrepProgress(MainController mainCtrl) {
mMainCtrl = mainCtrl;
}
public object DoWork(BackgroundWorker worker) {
string messages = mMainCtrl.mProject.LoadExternalFiles();
mMainCtrl.DoRefreshProject(UndoableChange.ReanalysisScope.CodeAndData);
return messages;
}
public void RunWorkerCompleted(object results) {
ExtMessages = (string)results;
}
}
#endif
private void FinishPrep() {
CodeLineList = new LineListGen(mProject, mMainWin.CodeDisplayList,
mOutputFormatter, mPseudoOpNames);
string messages = mProject.LoadExternalFiles();
if (messages.Length != 0) {
// ProjectLoadIssues isn't quite the right dialog, but it'll do.
// ProjectLoadIssues isn't quite the right dialog, but it'll do. This is
// purely informative; no decision needs to be made.
ProjectLoadIssues dlg = new ProjectLoadIssues(mMainWin, messages,
ProjectLoadIssues.Buttons.Continue);
dlg.ShowDialog();
}
CodeLineList = new LineListGen(mProject, mMainWin.CodeDisplayList,
mOutputFormatter, mPseudoOpNames);
// Ideally we'd call DoRefreshProject (and LoadExternalFiles) from a progress
// dialog, but we're not allowed to update the DisplayList from a different thread.
RefreshProject(UndoableChange.ReanalysisScope.CodeAndData);
// Populate the Symbols list.
@ -753,6 +776,20 @@ namespace SourceGenWPF {
UpdateSelectionHighlight();
}
/// <summary>
/// Updates all of the specified ListView entries. This is called after minor changes,
/// such as editing a comment or renaming a label, that can be handled by regenerating
/// selected parts of the DisplayList.
/// </summary>
/// <param name="offsetSet"></param>
private void RefreshCodeListViewEntries(RangeSet offsetSet) {
IEnumerator<RangeSet.Range> iter = offsetSet.RangeListIterator;
while (iter.MoveNext()) {
RangeSet.Range range = iter.Current;
CodeLineList.GenerateRange(range.Low, range.High);
}
}
/// <summary>
/// Refreshes the project after something of substance has changed. Some
/// re-analysis will be done, followed by a complete rebuild of the DisplayList.
@ -768,6 +805,47 @@ namespace SourceGenWPF {
// reanalysis after many common operations, the program becomes unpleasant to
// use if we miss this goal, and progress bars won't make it less so.
if (mProject.FileDataLength > 65536) {
try {
Mouse.OverrideCursor = Cursors.Wait;
DoRefreshProject(reanalysisRequired);
} finally {
Mouse.OverrideCursor = null;
}
} else {
DoRefreshProject(reanalysisRequired);
}
if (mGenerationLog != null) {
//mReanalysisTimer.StartTask("Save _log");
//mGenerationLog.WriteToFile(@"C:\Src\WorkBench\SourceGen\TestData\_log.txt");
//mReanalysisTimer.EndTask("Save _log");
if (mShowAnalyzerOutputDialog != null) {
mShowAnalyzerOutputDialog.DisplayText = mGenerationLog.WriteToString();
}
}
if (FormatDescriptor.DebugCreateCount != 0) {
Debug.WriteLine("FormatDescriptor total=" + FormatDescriptor.DebugCreateCount +
" prefab=" + FormatDescriptor.DebugPrefabCount + " (" +
(FormatDescriptor.DebugPrefabCount * 100) / FormatDescriptor.DebugCreateCount +
"%)");
}
}
/// <summary>
/// Refreshes the project after something of substance has changed.
/// </summary>
/// <remarks>
/// Ideally from this point on we can run on a background thread. The tricky part
/// is the close relationship between LineListGen and DisplayList -- we can't update
/// DisplayList from a background thread. Until that's fixed, putting up a "working..."
/// dialog or other UI will be awkward.
/// </remarks>
/// <param name="reanalysisRequired">Indicates whether reanalysis is required, and
/// what level.</param>
private void DoRefreshProject(UndoableChange.ReanalysisScope reanalysisRequired) {
// Changing the CPU type or whether undocumented instructions are supported
// invalidates the Formatter's mnemonic cache. We can change these values
// through undo/redo, so we need to check it here.
@ -780,40 +858,6 @@ namespace SourceGenWPF {
mOutputFormatterCpuDef = mProject.CpuDef;
}
if (CodeLineList.Count > 40000) {
try {
Mouse.OverrideCursor = Cursors.Wait;
DoRefreshProject(reanalysisRequired);
} finally {
Mouse.OverrideCursor = null;
}
} else {
DoRefreshProject(reanalysisRequired);
}
if (FormatDescriptor.DebugCreateCount != 0) {
Debug.WriteLine("FormatDescriptor total=" + FormatDescriptor.DebugCreateCount +
" prefab=" + FormatDescriptor.DebugPrefabCount + " (" +
(FormatDescriptor.DebugPrefabCount * 100) / FormatDescriptor.DebugCreateCount +
"%)");
}
}
/// <summary>
/// Updates all of the specified ListView entries. This is called after minor changes,
/// such as editing a comment or renaming a label, that can be handled by regenerating
/// selected parts of the DisplayList.
/// </summary>
/// <param name="offsetSet"></param>
private void RefreshCodeListViewEntries(RangeSet offsetSet) {
IEnumerator<RangeSet.Range> iter = offsetSet.RangeListIterator;
while (iter.MoveNext()) {
RangeSet.Range range = iter.Current;
CodeLineList.GenerateRange(range.Low, range.High);
}
}
private void DoRefreshProject(UndoableChange.ReanalysisScope reanalysisRequired) {
if (reanalysisRequired != UndoableChange.ReanalysisScope.DisplayOnly) {
mGenerationLog = new CommonUtil.DebugLog();
mGenerationLog.SetMinPriority(CommonUtil.DebugLog.Priority.Debug);
@ -824,16 +868,6 @@ namespace SourceGenWPF {
mReanalysisTimer.EndTask("Call DisasmProject.Analyze()");
}
if (mGenerationLog != null) {
//mReanalysisTimer.StartTask("Save _log");
//mGenerationLog.WriteToFile(@"C:\Src\WorkBench\SourceGen\TestData\_log.txt");
//mReanalysisTimer.EndTask("Save _log");
if (mShowAnalyzerOutputDialog != null) {
mShowAnalyzerOutputDialog.DisplayText = mGenerationLog.WriteToString();
}
}
mReanalysisTimer.StartTask("Generate DisplayList");
CodeLineList.GenerateAll();
mReanalysisTimer.EndTask("Generate DisplayList");
@ -903,13 +937,14 @@ namespace SourceGenWPF {
}
/// <summary>
/// Handles opening an existing project, given a pathname to the project file.
/// Handles opening an existing project, given a full pathname to the project file.
/// </summary>
private void DoOpenFile(string projPathName) {
Debug.WriteLine("DoOpenFile: " + projPathName);
Debug.Assert(mProject == null);
if (!File.Exists(projPathName)) {
// Should only happen for projects in "recents".
string msg = string.Format(Res.Strings.ERR_FILE_NOT_FOUND_FMT, projPathName);
MessageBox.Show(msg, Res.Strings.ERR_FILE_GENERIC_CAPTION,
MessageBoxButton.OK, MessageBoxImage.Error);
@ -1188,11 +1223,8 @@ namespace SourceGenWPF {
mProjectPathName = null;
mTargetHighlightIndex = -1;
// Clear this to release the memory.
mMainWin.CodeDisplayList.Clear();
mMainWin.InfoPanelContents = string.Empty;
mMainWin.ShowCodeListView = false;
mMainWin.ProjectClosing();
mGenerationLog = null;

View File

@ -388,6 +388,22 @@ namespace SourceGenWPF.WpfGui {
}
}
/// <summary>
/// Cleans up state when MainController decides to close the project.
/// </summary>
public void ProjectClosing() {
// Clear this to release the memory.
CodeDisplayList.Clear();
InfoPanelContents = string.Empty;
// If you open a new project while one is already open, the ListView apparently
// doesn't reset certain state, possibly because it's never asked to draw after
// the list is cleared. This results in the new project being open at the same
// line as the previous project. This is a little weird, so we reset it here.
CodeListView_SetTopIndex(0);
}
/// <summary>
/// Catch mouse-down events so we can treat the fourth mouse button as "back".
/// </summary>

View File

@ -126,7 +126,7 @@ namespace SourceGenWPF.WpfGui {
DialogResult = false;
}
} else if (e.Error != null) {
// Unexpected -- shell command execution shouldn't throw exceptions.
// Unexpected; success/failure should be passed through e.Result.
MessageBox.Show(e.Error.ToString(), Res.Strings.OPERATION_FAILED,
MessageBoxButton.OK, MessageBoxImage.Error);
DialogResult = false;