This was kind of mind-boggling, narrowing it down to dark mode,
and then to dark mode *but not CSS*.
I kept narrowing it down, and looked into how dark mode was implemented,
and finally figured this out.
`app.call_later(app.refresh_css)` in `App.watch_dark` causes a timing issue.
I'm basically doing TDD to snapshot testing!
I'm creating tests that don't pass yet, setting up an expectation
that the app match the given screenshots, which is funny in a nice
"improper hierarchy" sort of way, but it's possible because I do
actually have the app rendering how I want, just only in isolation.
If I run the ascii_only tests by themselves, I can get good results
from them, but running them interwoven with default Unicode-using UI
tests doesn't work yet, since the ASCII-only mode permanently changes
how certain widgets render, for the life of the process, so that's
what I'm applying TDD to: making it toggleable at runtime.
I commented out the Unicode tests, and uncommented the ASCII-only tests,
renamed test_snapshots.ambr to test_snapshots_ascii.ambr,
reverted the changes to test_snapshots.ambr to get the Unicode version,
ran my new merge_ambr.py script to join the sets of snapshots,
then replaced test_snapshots.ambr with test_snapshots_merged.ambr
Finally, I uncommented both sets of tests, and I'm ready to do TDD!
I already fixed my first bug caught by the snapshot testing!
These variables were intended as constants, but were being mutated.
I recall writing it as `prefix = (...).stylize_before(...)` and then
moving it to a new line when I realized it was mutation-style method,
not so much the chaining-style factory that I wanted, but I conceived
of it too much as a stylistic distinction in the moment, looking back.
Mutation style means mutation!
Side note: tests also showed a spurious change of a cursor blinking.
I don't really know whether that's in this changeset or not, because
the workflow involves re-running the tests to update the baseline, and
the nice visual diffs provided in the snapshot report aren't available
when viewing the commit diff.
1. If the SVGs were separate files, I could see the diffs on GitHub
or in GitHub Desktop, and maybe some other Git clients.
It would also make it a lot easier to simply view the baselines,
which is useful in general.
2. It would be nice if built-in components didn't cause spurious diffs,
including the Input's cursor blinking and the Header's clock ticking.
I already removed the clock from my gallery app, because it's a sort
of trivial decision, but Inputs I'll have to reckon with.
First I tried setting PYTEST_TEXTUAL_PAINT_ARGS as an environment variable, to be interpreted by args.py, but it turns out args.py is only executed once, not once per test. It's not using subprocesses, only importing and reimporting the app code, and instantiating new App instances, so parts of the code that are at the top level of modules is only evaluated once.
So I found a new strategy, of importing the `args` object in the test fixture and modifying it directly.
I also realized the --ascii-only option permanently modifies Textual's widgets and borders, and my own widgets, for the life of the process, so I'm holding off on that one. I should be able to make --ascii-only mode more dynamic, and could even target it as a runtime toggle, as a goal, since that's basically what I'll need to achieve to get it working for the tests, but thinking of it as a feature is more fun.
I used a very dangerous extension that has a lot of include/exclude
options that are unclear how they interact, and it doesn't apparently
respect your gitignore settings, and it has no no preview or warning.
"commandOnAllFiles.excludeFolders": ["node_modules", "out", ".vscode-test", "media", ".git", ".history", ".venv", ".venv*", ".*"],
"commandOnAllFiles.commands": {
"Organize Imports": {
"command": "editor.action.organizeImports",
"includeFileExtensions": [".py"],
"includeFolders": ["src"]
}
},
They honestly seem like a half-baked language feature.
- Relative imports don't allow running a script via shebang line.
- Relative imports don't work without a package, which means they
don't work in situations other than when absolute imports also work,
as far as I understand it.
- Relative imports don't work with pytest-textual-snapshot currently,
so I'm changing it to absolute imports in order to set up testing.
Before, the horizontal scrollbar went below the caption, offsetting it,
although the caption didn't scroll with the content, to be clear.
At any rate, it looks much better for the caption to stay in one place,
and be grouped outside the scrollable container which doesn't scroll it.
The dict is sparse, so its indices are not always the gallery's indices.
This fixes the behavior when passing a file as an argument.
(I already fixed this code and must've undone it somehow! I was losing my mind over here!)
- Make it more efficient by loading files progressively.
- Remove the HorizontalScroll, and instead position items absolutely, animating offsets to imitate the movement of scrolling horizontally.
- This fixes the left/right bindings not showing in the footer, due to ScrollableContainer's hidden left/right bindings.
- This also removes the possibility of scrolling half-way away from an item.
- This also fixes a problem where you could lose track of the currently viewed item when resizing the terminal, due to the 100% width of gallery items not jiving with the absolute notion of scroll position. (If the scroll position were stored as a fraction, it wouldn't have been a problem.)
- Simplify the keyboard navigation logic by storing an index into the gallery, instead of having to figure out what item is centered.
This could make debugging less confusing if the nearby debugging code were enabled and there were no paths found, but also I'm planning on changing how gallery items are loaded, making it load progressively, and decoupling this logic from the UI helps prepare for that.
I don't really like the style of wrapping to different points depending
on where the opening parenthesis is, especially when it wraps for just
one item, and these changes may remove some semantic grouping or
ordering (i.e. by importance), but I don't think I put THAT much thought
into it, and I think I can accept some lack of control, to freely use
the Organize Imports command in VS Code, which does nicely automate
grouping imports of different types.
With `textual run --dev "src.textual_paint.gallery foobar"` it output:
Folder not found: foobar
Not a folder: foobar
No ANSI art (*.ans, *.txt) found in folder: foobar
NOTE: 3 errors shown above.
Now it correctly outputs only:
Folder not found: foobar
This fixes the gallery app's --help, because before it was importing the "paint" module, which imported "args", which parsed arguments for the paint app instead of the gallery app.
This is also a refactor I've been meaning to do — since the very beginning, really — and it would've been a lot less trouble if I could've done it from the beginning, but I couldn't get imports to work. Yeah, really. Sounds pretty stupid 'cause it is. Python's module system is terrible.
This fixes "NameError: name 'PaintApp' is not defined"
PaintApp is only defined during type checking (i.e. if TYPE_CHECKING).
It would be nice if the type checker warned about usage of such vars outside of type annotations.