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.
The ListView control provides a ScrollIntoView() method that ensures
the specified item is on screen, scrolling the ListView if needed.
Unfortunately this method is very slow (50-100 ms) and sometimes
fails entirely on larger lists. (Yay WPF.)
Because the ListView is scrolling by fixed-height item, it's possible
to use the underlying ScrollViewer to move the list instantaneously
and reliably. So now we do that.
Also, fixed a bug with select-all, where we weren't clearing the
previous selection before calling SelectAll(), leading to a mismatch
with the secondary data structure that we maintain because WPF
ListViews can't deal with large selections efficiently. (Yay WPF.)
There's still some weird behavior, e.g. sometimes hitting F5 clears
the current selection and sometimes it doesn't. I think it's related
to which item has focus and the fact you're hitting a key; using the
debug menu item doesn't cause the behavior.
Also, increased MAX_SEL_COUNT from 2000 to 5000. That takes about
200ms to restore to a ListView on my 5-year-old system.
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.
While I was at it, I noticed that the ASCII chart and hex dump
viewer windows were always in front of the main app window. It looks
like child windows are always in front. The easy fix is to not
set an owner.
This causes a new problem: the windows don't get closed automatically
when the parent window closes, and the app won't exit until all
windows are closed. So we now explicitly close the hex dump and
ASCII chart windows when the main window is closed, and we now keep
track of all the external-file hex dump windows. (It always sort of
bothered me that we were creating hex dump windows and not keeping
track of them. Itch has now been scratched.)
Also, changed the ascch-mode setting to be an enum rather than an
integer. Renamed the setting to ascch-mode1.
The contents of cdlv-col-widths and hexd-char-conv have changed. The
app correctly ignores things it doesn't understand when going either
forward or backward (between SourceGen and SourceGenWPF), but it's
nicer to just not have the settings get clobbered.
If you select a note, delete it, select some nearby lines, and hit
undo, then the next time you hit up/down arrow the list will jump
back to the first line. The workaround for this appears to be to
set the focus on a selected ListViewItem from an obscure event.
Maybe there's a simpler way that I just missed? This is absurd.
(Yay WPF.)
Because of the way data virtualization works (or doesn't) in WPF,
this was a whole lot different from WinForms. What I'm doing is
pretty simple, so I avoided the complexity (and quirks) inherent
to more complete data virtualization solutions. (All I really want
is to not render the entire thing up front.)
I looked at doing this in full WPF fashion, binding context object
properties to radio button IsChecked properties, but there's a lot
of conditional logic behind the scenes. I also wanted to do the
various formatted strings with a StringFormat binding, but that only
takes one argument, and some of them need two.
On the plus side, the format strings are now in Window.Resources,
rather than the global string table or the buttons themselves.
(The latter always felt a little sleazy, but it seemed like the
least annoying way to do it in WinForms.)
Also, add keyboard shortcuts to toolbar tooltips. This is particularly
useful for navigate forward/backward, as those aren't part of the
menu structure, and have two keyboard shortcuts apiece.
I'm planning to rework the dialog, so I didn't bother WPFing it up.
Also, explicitly recreate the contents of the Symbols window when
applying changes. This used to happen implicitly via
SymbolTableSubset and the "change serial" in SymbolTable, but that
approach doesn't make sense for a DataGrid.
The change in 779566 missed a step, so the TextChanged method wasn't
getting called. (When you bind TextBox Text to a string property, the
TextChanged events stop firing. You need to react to the property
"set" call instead.)
Also, enable UseKeepAliveHack. I have no reason to believe switching
to WPF will make remoting less flaky.
We can now Edit Long Comment and Edit Header Comment.
Changing the number of lines in a long comment exposed a bug in the
display list update. Fixed.
Running the WinForms version trashed the column widths, which exposed
the fact that the default column widths were set too narrow. Fixed.
On an unrelated note, hitting undo/redo quickly will sometimes leave
you scrolled to the bottom of the code list. Some sort of command-
handling race in the WPF framework? (Not fixed.)
The dialog is pretty straightforward. This is the first editor that
can cause a partial update -- if you edit a label, only the lines
that refer to that label are regenerated -- so I added the code for
partial display list updates as well.
Also, while working on this I noticed that long comments were getting
ellipsized at a bad place. The column width for the semi-hidden
column used for notes and long comments wasn't getting set. After
fiddling with it a bit I determined that it really needed to be set to
the sum of the widths of the rightmost four columns, and updated
whenever column sizes change. This was easier to do than I expected.
I may actually be getting the hang of WPF.
I haven't figured out how to make the AppendText(text, color) extension
work right with the WPF version of RichTextBox, but I was able to work
around the issue, and it's not really worth sinking time into.
The order of items in XAML was tied to enum values, which isn't
great. Since the value gets stored in the settings file, we want
the values to come from the code-behind. So now we provide an
ItemsSource for the combo box from the code-behind instead of
defining the items in XAML.
Took another swing at doing things WPF-style. Ended up with TextBox
objects backed by integer fields, which worked pretty well but
didn't get the min/max values. Set up validation instead, once I
figured out how to make it update in real time.
Relatively straightforward, thanks to the way WPF ComboBoxes work.
Spent some time fiddling with the column width text boxes, but
ended up with essentially the WinForms approach.
Notable items include the column show/hide buttons, which were
straightforward except for the "determine the default width" part,
and the font picker, which is no longer a standard dialog. The
latter was complicated by the absence of a good way to detect
whether a font is mono-spaced or not without calling back into code
meant for WinForms font manipulation (with a dash of PInvoke).
Yay WPF.
Also, enabled character ellipsis for code list items.