- This fixes a potential race condition where it could auto-save
to the wrong backup file while the file_path was temporarily
set to the path attempting to be written to,
overwriting the backup if it existed, and leaving it behind on close,
since the file_path would be reverted momentarily.
- save_as() no longer uses save(), so save doesn't need a from_save_as
parameter to change the error dialog titles, and there's no more
conceptual awkwardness due to the fact that each function could call
the other.
- discard_backup() no longer needs an optional parameter, because
we no longer discard a backup that isn't associated to the current
document's file_path.
- The "correct thing" is now obvious enough that I don't feel the need
for the "Don't discard the backup until..." comment.
(And of course all that todo junk can go, although the Copy To bit
remains to do.)
It's nice when things Actually Get Simpler!
"Auto-save" can mean saving the actual document you're editing;
in this case it means saving a temporary backup file.
I might even want to introduce auto-saving of the main file later on.
- Recover from backup file if it exists for the document when loading
a document via the CLI or TUI, or if it exists for Untitled when
creating a new document.
- Reorganize CLI arguments handling into passive and active,
so that the active recovery step can use the passive* backup folder
option. (*Directories are created if they don't exist.)
Working on automatically recovering the document,
I'm realizing this is more complicated than I thought.
Consider two instances of the app (A and B) editing the same file:
- Make changes in A
- Open same file in B
- A auto-saves after timer
- Close B
- B deletes auto-save when closing
- A doesn't re-create the auto-save unless further changes are made to
the document, because of the undo tracking, which is basically an
optimization to avoid writing to the disk all day while idle.
Also, there could be a race condition where it saves over the auto save
before recovering it — potentially — like if the auto save interval
was implemented such that it made an immediate call at the start, or
if some other app code tries to auto save initially.
Or, consider if you:
- Make changes in A
- Open same file in B
- Make changes in B, maybe before it detects the backup file if the disk
is very slow for some reason, or if you just ignore the prompt.
(None of the prompts are modal, currently.)
To address these issues, I could add a flag that says whether the
auto save was written, and thus owned by, this session, and make it
not write to a backup file that it didn't create in the current session.
But it still ideally would auto save, just to a different file,
so I could instead have a map of file paths to bools saying whether it's
owned by the app or not and then have a number incremented in the
filename (maybe like `.ans~` and then `.ans~SESSION2~`, including
"SESSION" so it's clear it's not time-based), adding 1 until it's a file
that can be owned by the session when auto saving.
But if an earlier-numbered session closes, you wouldn't want it to leave
a file behind. Either it should delete/rename its backup file to the
earlier number and start saving to that, or it should keep the existing
higher number. I guess if looks at the map first, before checking the
file system, it shouldn't be a problem. It should keep using the higher
number. Would this be a little overkill? Eh, maybe.
Don't try to focus an unfocusable control; find the first focusable one.
The Edit Colors dialog is not yet keyboard accessible, or very fully
implemented in general, but this at least prevents a scenario where
focus is unexpectedly left in an important dialog window, such as the
"Save changes to X?" prompt, which may also be obscured by Edit Colors.
You don't want pressing Enter to interact with a different dialog than
you're expecting, and suddenly quit without saving.
This reverts commit 42ca86f7eb.
It's needed again after the last commit...
I did override `focus`, so it's not that surprising.
But I definitely want to figure this out.
I'm setting up a virtual environment for the first time, and, trying
to install my dependencies, I ran into errors, 1. because of appscript
which is only intended for Mac, 2. PyGObject which IS for Linux, but
fails to install, missing "libgirepository1.0-dev", but it's optional,
it's just for wallpaper support. pyxgd is also for wallpaper support,
so maybe I should comment it out too.
And finally, [dev] is needed in order to use the `textual` command.
I don't know how well this can install on other systems, but at least
I can install it now, on Ubuntu.
Also, when the version numbers in my requirements.txt were bumped
when regenerating it, I'm guessing now that the dependencies weren't
actually upgraded, since `textual --version` gave me "0.19.1" up until
I set up my virtual environment (.venv), and now it matches the
requirements.txt version of "0.22.3". I still don't know why the version
numbers were bumped, or for sure that they weren't installed at those
versions somewhere in my filesystem. (I hope virtualenv makes things
more comprehensible, rather than more confusing...)
Also, now that I'm using textual 0.22.3, the radio button set has
arrow key navigation. Seeing this feature missing was what prompted me
to set up the virtual environment and (sort of implicitly) upgrade.
However, it doesn't work correctly, for the flipped horiz./vert. layout.
I asked ChatGPT to do this because I'm tired and I was curious if it
could understand. It did it straight away. I didn't even think about
aligning the code like that, it's a good idea.
> Prompt
> Reorder these such that when layed out in a left to right top to bottom grid they're in order top to bottom left to right, with 3 columns:
>
>
> RadioButton(_("100%"), id="value_1"),
> RadioButton(_("200%"), id="value_2"),
> RadioButton(_("400%"), id="value_4"),
> RadioButton(_("600%"), id="value_6"),
> RadioButton(_("800%"), id="value_8"),
> ChatGPT
> Here's the reordered code:
>
> bash
> Copy code
> RadioButton(_("100%"), id="value_1"), RadioButton(_("400%"), id="value_4"), RadioButton(_("800%"), id="value_8"),
> RadioButton(_("200%"), id="value_2"), RadioButton(_("600%"), id="value_6"),