The implicit type was creating mypy errors when defining bindings with
tuples. For example:
class MyApp(App):
BINDINGS = [("q", "quit", "Quit")]
Would give the error:
error: List item 0 has incompatible type "Tuple[str, str, str]"; expected "Binding" [list-item]
With #1719 in mind, and as an alternative to #2608, this allows for a child
class of DirectoryTree to specify how a fresh `Path` should be created. The
idea here being that whatever is created should be of the `Path` type, but
can have other abilities.
Normally it's not a great idea to eat and hide exceptions within library
code; but I think it makes sense to make an exception here. This is a UI
element that lets the user navigate about a filesystem. If there is
something they don't have permission for, that should not cause an
exception, it should just give up with the best possible outcome.
If actually doing something with the exception is important, the developer
using this could use the filter to do tests and act accordingly.
See #2564.
Plan C; or is it plan D? Something like that. Anyway... in this approach we
keep a single "forever" async task worker per directory tree, which in turn
looks at the async Queue, and when a new node appears on it it starts a
short-lived thread to load the directory data.
This seems to be working fine on macOS. Next up is testing on Windows.
Turns out, there's a maximum number of threads you can have going in the
underlying pool, that's tied to the number of CPUs. As such, there was a
limit on how many directory trees you could have up and running before it
would start to block all sorts of operations in the surrounding
application (in Parallels on macOS, with the Windows VM appearing to have
just the one CPU, it would give up after 8 directory trees).
So here we move to a slightly different approach: have the main loader still
run "forever", but be an async task; it then in turn farms the loading out
to threads which close once the loading is done.
So far tested on macOS and behaves as expected. Next to test on Windows.
When the default screen is first created it was not getting the event ScreenResume. All other screens receive a ScreenResume when first created and _all_ screens (the default one and custom screens) receive this event when they become the active screen again, so this was kind of an edge case that needed the event to be posted by hand.
Related comments: https://github.com/Textualize/textual/pull/2581\#issuecomment-1550231559