1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-12-24 07:29:42 +00:00

Improve save & restore of top line

Whenever the display list gets regenerated, we need to restore the
code list view scroll position to the previous location in the file.
This gets tricky when multiple lines are appearing or disappearing.
We were saving the file offset of the line, but that works poorly
when there's a multi-line comment associated with that offset,
because we end up scrolling to the top of the comment whenever any
part of the comment is at the top of the screen.

We now track the file offset and the number of lines we were from
the top of that offset's content.  This works well unless we remove
a lot of lines.  If the adjusted line index would put us into a
different file offset, we punt and just scroll to the top of the item.

Also, fix a crasher in Edit Note.

Also, fix behavior when the list shrinks while a line near the end
of the file is selected.

Also, change a few instances of "Color.FromArgb(0,0,0,0)" to use a
common constant.
This commit is contained in:
Andy McFadden 2019-07-17 13:47:43 -07:00
parent 0ff2ebefdf
commit a0dca6a5be
11 changed files with 80 additions and 24 deletions

View File

@ -24,6 +24,8 @@ namespace CommonWPF {
/// Miscellaneous helper functions. /// Miscellaneous helper functions.
/// </summary> /// </summary>
public static class Helper { public static class Helper {
public static Color ZeroColor = Color.FromArgb(0, 0, 0, 0);
/// <summary> /// <summary>
/// Measures the size of a string when rendered with the specified parameters. Uses /// Measures the size of a string when rendered with the specified parameters. Uses
/// the current culture, left-to-right flow, and 1 pixel per DIP. /// the current culture, left-to-right flow, and 1 pixel per DIP.

View File

@ -303,7 +303,7 @@ namespace SourceGenWPF {
" new=" + newCount + " (mList.Count=" + mList.Count + ")"); " new=" + newCount + " (mList.Count=" + mList.Count + ")");
Debug.Assert(startIndex >= 0 && startIndex < mList.Count); Debug.Assert(startIndex >= 0 && startIndex < mList.Count);
Debug.Assert(oldCount > 0 && startIndex + oldCount < mList.Count); Debug.Assert(oldCount > 0 && startIndex + oldCount <= mList.Count);
Debug.Assert(newCount >= 0); Debug.Assert(newCount >= 0);
// Remove the old elements to clear them. // Remove the old elements to clear them.
@ -358,7 +358,7 @@ namespace SourceGenWPF {
public int ListIndex { get; set; } = -1; public int ListIndex { get; set; } = -1;
private static Color NoColor = Color.FromArgb(0, 0, 0, 0); private static Color NoColor = CommonWPF.Helper.ZeroColor;
// Private constructor -- create instances with factory methods. // Private constructor -- create instances with factory methods.
@ -448,6 +448,10 @@ namespace SourceGenWPF {
newParts.HasAddrLabelHighlight = false; newParts.HasAddrLabelHighlight = false;
return newParts; return newParts;
} }
public override string ToString() {
return "[Parts: index=" + ListIndex + " off=" + Offset + "]";
}
} }
} }
} }

View File

@ -111,8 +111,12 @@ namespace SourceGenWPF {
this[parts.ListIndex] = true; this[parts.ListIndex] = true;
} }
foreach (DisplayList.FormattedParts parts in e.RemovedItems) { foreach (DisplayList.FormattedParts parts in e.RemovedItems) {
Debug.Assert(parts.ListIndex >= 0 && parts.ListIndex < mSelection.Length); Debug.Assert(parts.ListIndex >= 0);
this[parts.ListIndex] = false; if (parts.ListIndex < mSelection.Length) {
this[parts.ListIndex] = false;
} else {
Debug.WriteLine("Attempted to remove selected item off end of list: " + parts);
}
} }
} }

View File

@ -75,6 +75,8 @@ namespace SourceGenWPF {
// Extremely-negative offset value ensures it's at the very top. // Extremely-negative offset value ensures it's at the very top.
public const int HEADER_COMMENT_OFFSET = int.MinValue + 1; public const int HEADER_COMMENT_OFFSET = int.MinValue + 1;
// These need to be bit flags so we can record which parts associated with a
// given offset are selected.
[FlagsAttribute] [FlagsAttribute]
public enum Type { public enum Type {
Unclassified = 0, Unclassified = 0,
@ -131,7 +133,7 @@ namespace SourceGenWPF {
public FormattedParts Parts { get; set; } public FormattedParts Parts { get; set; }
/// <summary> /// <summary>
/// Background color, used for notes. /// Background color, used for Notes.
/// </summary> /// </summary>
public Color BackgroundColor { get; set; } public Color BackgroundColor { get; set; }
@ -213,11 +215,28 @@ namespace SourceGenWPF {
private List<Tag> mSelectionTags = new List<Tag>(); private List<Tag> mSelectionTags = new List<Tag>();
private class Top {
// File offset of line.
public int FileOffset { get; private set; }
// Number of lines between the first line at the specified offset and the
// target line.
public int LineDelta { get; private set; }
public Top(int fileOffset, int lineDelta) {
FileOffset = fileOffset;
LineDelta = lineDelta;
Debug.WriteLine("New Top: " + this);
}
public override string ToString() {
return "[Top: off=+" + FileOffset.ToString("x6") + " delta=" + LineDelta + "]";
}
}
/// <summary> /// <summary>
/// This is a place to save the file offset associated with the ListView's /// This is a place to save the file offset associated with the ListView's
/// TopItem, so we can position the list appropriately. /// TopItem, so we can position the list appropriately.
/// </summary> /// </summary>
private int mTopOffset; private Top mTopPosition;
// Use Generate(). // Use Generate().
private SavedSelection() { } private SavedSelection() { }
@ -230,12 +249,18 @@ namespace SourceGenWPF {
/// </summary> /// </summary>
/// <param name="dl">Display list, with list of Lines.</param> /// <param name="dl">Display list, with list of Lines.</param>
/// <param name="sel">Bit vector specifying which lines are selected.</param> /// <param name="sel">Bit vector specifying which lines are selected.</param>
/// <param name="topIndex">Index of line that appears at the top of the list
/// control.</param>
/// <returns>New SavedSelection object.</returns> /// <returns>New SavedSelection object.</returns>
public static SavedSelection Generate(LineListGen dl, DisplayListSelection sel, public static SavedSelection Generate(LineListGen dl, DisplayListSelection sel,
int topOffset) { int topIndex) {
SavedSelection savedSel = new SavedSelection(); SavedSelection savedSel = new SavedSelection();
//Debug.Assert(topOffset >= 0); //Debug.Assert(topOffset >= 0);
savedSel.mTopOffset = topOffset;
int topOffset = dl[topIndex].FileOffset;
int firstIndex = dl.FindLineIndexByOffset(topOffset);
Debug.Assert(topIndex >= firstIndex);
savedSel.mTopPosition = new Top(topOffset, topIndex - firstIndex);
List<Line> lineList = dl.mLineList; List<Line> lineList = dl.mLineList;
Debug.Assert(lineList.Count == sel.Length); Debug.Assert(lineList.Count == sel.Length);
@ -313,7 +338,7 @@ namespace SourceGenWPF {
// If a line encompassing this offset was at the top of the ListView // If a line encompassing this offset was at the top of the ListView
// control before, use this line's index as the top. // control before, use this line's index as the top.
if (topIndex < 0 && lineList[lineIndex].Contains(mTopOffset)) { if (topIndex < 0 && lineList[lineIndex].Contains(mTopPosition.FileOffset)) {
topIndex = lineIndex; topIndex = lineIndex;
} }
@ -336,14 +361,29 @@ namespace SourceGenWPF {
// Continue search for topIndex, if necessary. // Continue search for topIndex, if necessary.
while (topIndex < 0 && lineIndex < lineList.Count) { while (topIndex < 0 && lineIndex < lineList.Count) {
if (lineList[lineIndex].Contains(mTopOffset)) { if (lineList[lineIndex].Contains(mTopPosition.FileOffset)) {
topIndex = lineIndex; topIndex = lineIndex;
break; break;
} }
lineIndex++; lineIndex++;
} }
Debug.WriteLine("TopOffset +" + mTopOffset.ToString("x6") + Debug.WriteLine("TopOffset " + mTopPosition + " --> index " + topIndex);
" --> index " + topIndex);
// Adjust position within an element. This is necessary so we don't jump to
// the top of multi-line long comments or notes whenever any part of that
// comment or note is at the top of the list.
if (topIndex >= 0 && mTopPosition.LineDelta > 0) {
int adjIndex = topIndex + mTopPosition.LineDelta;
if (adjIndex >= lineList.Count ||
lineList[adjIndex].FileOffset != mTopPosition.FileOffset) {
Debug.WriteLine("Can't adjust top position");
// can't adjust; maybe they deleted several lines from comment
} else {
topIndex = adjIndex;
Debug.WriteLine("Top index adjusted to " + adjIndex);
}
}
if (topIndex < 0) { if (topIndex < 0) {
// This can happen if you delete the header comment while scrolled // This can happen if you delete the header comment while scrolled
// to the top of the list. // to the top of the list.
@ -775,7 +815,7 @@ namespace SourceGenWPF {
out MultiLineComment headerComment)) { out MultiLineComment headerComment)) {
List<string> formatted = headerComment.FormatText(formatter, string.Empty); List<string> formatted = headerComment.FormatText(formatter, string.Empty);
StringListToLines(formatted, Line.HEADER_COMMENT_OFFSET, Line.Type.LongComment, StringListToLines(formatted, Line.HEADER_COMMENT_OFFSET, Line.Type.LongComment,
Color.FromArgb(0, 0, 0, 0), tmpLines); CommonWPF.Helper.ZeroColor, tmpLines);
} }
// Format symbols. // Format symbols.

View File

@ -697,8 +697,7 @@ namespace SourceGenWPF {
mReanalysisTimer.StartTask("Save selection"); mReanalysisTimer.StartTask("Save selection");
int topItemIndex = mMainWin.CodeListView_GetTopIndex(); int topItemIndex = mMainWin.CodeListView_GetTopIndex();
LineListGen.SavedSelection savedSel = LineListGen.SavedSelection.Generate( LineListGen.SavedSelection savedSel = LineListGen.SavedSelection.Generate(
CodeLineList, mMainWin.CodeDisplayList.SelectedIndices, CodeLineList, mMainWin.CodeDisplayList.SelectedIndices, topItemIndex);
CodeLineList[topItemIndex].FileOffset);
//savedSel.DebugDump(); //savedSel.DebugDump();
// Clear this so we don't try to fiddle with it later. // Clear this so we don't try to fiddle with it later.
@ -1692,7 +1691,7 @@ namespace SourceGenWPF {
MultiLineComment oldNote; MultiLineComment oldNote;
if (!mProject.Notes.TryGetValue(offset, out oldNote)) { if (!mProject.Notes.TryGetValue(offset, out oldNote)) {
oldNote = new MultiLineComment(string.Empty); oldNote = null;
} }
EditNote dlg = new EditNote(mMainWin, oldNote); EditNote dlg = new EditNote(mMainWin, oldNote);
dlg.ShowDialog(); dlg.ShowDialog();

View File

@ -62,7 +62,7 @@ namespace SourceGenWPF {
Text = text; Text = text;
BoxMode = false; BoxMode = false;
MaxWidth = 80; MaxWidth = 80;
BackgroundColor = Color.FromArgb(0, 0, 0, 0); BackgroundColor = CommonWPF.Helper.ZeroColor;
} }
/// <summary> /// <summary>

View File

@ -70,7 +70,7 @@ namespace SourceGenWPF {
return obj is Location && this == (Location)obj; return obj is Location && this == (Location)obj;
} }
public override int GetHashCode() { public override int GetHashCode() {
return Offset + (IsNote ? 65536 : 0); return Offset + (IsNote ? (1<<24) : 0);
} }
} }

View File

@ -26,7 +26,7 @@ namespace SourceGenWPF.Tests {
public Color Color { get; private set; } public Color Color { get; private set; }
public bool HasColor { get { return Color.A != 0; } } public bool HasColor { get { return Color.A != 0; } }
public ProgressMessage(string msg) : this(msg, Color.FromArgb(0, 0, 0, 0)) { } public ProgressMessage(string msg) : this(msg, CommonWPF.Helper.ZeroColor) { }
public ProgressMessage(string msg, Color color) { public ProgressMessage(string msg, Color color) {
Text = msg; Text = msg;

View File

@ -32,9 +32,9 @@ limitations under the License.
<StackPanel Margin="0,16,0,0" Orientation="Horizontal"> <StackPanel Margin="0,16,0,0" Orientation="Horizontal">
<Button Name="saveButton" Width="120" <Button Name="saveButton" Width="120"
Content="_Save &amp; Continue" Click="SaveButton_Click"/> Content="_Save &amp; Continue" Click="SaveButton_Click"/>
<Button Name="dontSaveButton" Width="120" Margin="8,0,0,0" <Button Name="dontSaveButton" Width="120" Margin="12,0,0,0"
Content="_Discard &amp; Continue" Click="DontSaveButton_Click"/> Content="_Discard &amp; Continue" Click="DontSaveButton_Click"/>
<Button Name="cancelButton" Width="120" Margin="8,0,0,0" <Button Name="cancelButton" Width="120" Margin="12,0,0,0"
Content="Cancel" IsCancel="True"/> Content="Cancel" IsCancel="True"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>

View File

@ -53,7 +53,7 @@ namespace SourceGenWPF.WpfGui {
None = 0, Green, Blue, Yellow, Pink, Orange None = 0, Green, Blue, Yellow, Pink, Orange
} }
private static Color[] sColors = new Color[] { private static Color[] sColors = new Color[] {
Color.FromArgb(0, 0, 0, 0), // no highlight CommonWPF.Helper.ZeroColor, // no highlight
Colors.LightGreen, Colors.LightGreen,
Colors.LightBlue, Colors.LightBlue,
Colors.Yellow, //LightGoldenrodYellow, Colors.Yellow, //LightGoldenrodYellow,
@ -74,7 +74,11 @@ namespace SourceGenWPF.WpfGui {
Owner = owner; Owner = owner;
DataContext = this; DataContext = this;
Note = note; if (note == null) {
Note = new MultiLineComment(string.Empty);
} else {
Note = note;
}
mColorButtons = new RadioButton[] { mColorButtons = new RadioButton[] {
colorDefaultRadio, colorDefaultRadio,
@ -102,6 +106,7 @@ namespace SourceGenWPF.WpfGui {
} }
} }
inputTextBox.Focus();
} }
// Handle Ctrl+Enter as a way to close the dialog, since plain Enter just // Handle Ctrl+Enter as a way to close the dialog, since plain Enter just

View File

@ -772,7 +772,9 @@ namespace SourceGenWPF.WpfGui {
} }
public int CodeListView_GetTopIndex() { public int CodeListView_GetTopIndex() {
return codeListView.GetTopItemIndex(); int index = codeListView.GetTopItemIndex();
Debug.Assert(index >= 0);
return index;
} }
public void CodeListView_SetTopIndex(int index) { public void CodeListView_SetTopIndex(int index) {