-
Notifications
You must be signed in to change notification settings - Fork 841
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
List view #1143
List view #1143
Conversation
darrenburns
commented
Nov 9, 2022
•
edited
Loading
edited
@@ -259,7 +259,7 @@ def screen(self) -> "Screen": | |||
from .screen import Screen | |||
|
|||
node = self | |||
while node and not isinstance(node, Screen): | |||
while node is not None and not isinstance(node, Screen): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the line that ultimately resulted in those weird NoScreen
errors. Because ListView has a __len__
, the condition was evaluating as False and terminating the search for the Screen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah. Good catch!
src/textual/widgets/_list_view.py
Outdated
class ListView(Vertical, can_focus=True, can_focus_children=False): | ||
DEFAULT_CSS = """ | ||
ListView { | ||
scrollbar-size-vertical: 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Subjective, but I prefer width 1 scrollbars for scrollbars that aren't Screen-level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was more of an issue with the click target being too small. Suspect for that reason, we should keep it at 2 , even if the aesthetics suffer for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice. Some requests to consider.
We're kind of finding our feet here. As we build these widgets, I think we will formulate some best practices / standards.
src/textual/widgets/_list_item.py
Outdated
ListItem > Widget :hover { | ||
background: $boost; | ||
} | ||
ListView ListItem.--highlight { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add a >
here? For the (hopefully rare) scenario of nested ListViews
src/textual/widgets/_list_view.py
Outdated
class ListView(Vertical, can_focus=True, can_focus_children=False): | ||
DEFAULT_CSS = """ | ||
ListView { | ||
scrollbar-size-vertical: 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was more of an issue with the click target being too small. Suspect for that reason, we should keep it at 2 , even if the aesthetics suffer for it.
src/textual/widgets/_list_view.py
Outdated
|
||
@property | ||
def highlighted_child(self) -> ListItem | None: | ||
"""Get the currently highlighted ListItem |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we're going to adopt property style docstrings in this format:
"""ListItem | None: description"""
src/textual/widgets/_list_view.py
Outdated
self._scroll_highlighted_region() | ||
self.emit_no_wait(self.Highlighted(self, new_child)) | ||
|
||
async def append(self, item: ListItem) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we make this non-async?
Use emit_no_wait
and return the awaitable from the mount
method.
Hopefully that won't break anything.
Ditto for clear
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically yes, but I think this will cause a lot confusion/subtle bugs. It needs to be async because we need to await
the call to remove
inside it. Otherwise we'll send a ChildrenUpdated
with undefined children
(could contain children before or after update, who knows).
If I receive a ChildrenUpdated
message, I won't know inside my on_children_updated
handler if the children have actually been updated yet or not, and I'll have no easy way of await
ing it (I won't have access to the AwaitRemove
in there). It would mean we can't actually trust that message any more since the state of the children is undefined. Doing anything to my ListView on receipt of this message would be dangerous.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need a children update message? Generally it is the parent that is doing the updating, so it will know there has been an updated.
Do you have a use-case in mind?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've removed it, don't think it's necessary.
It was to give the ancestors a single hook (the children update handler) that they could use when the children changed. e.g. a single place they can update a label to reflect the number of items in the list rather.
src/textual/widgets/_list_view.py
Outdated
self.index = self.children.index(event.sender) | ||
self.emit_no_wait(self.Selected(self, event.sender)) | ||
|
||
def key_up(self, event: events.Key) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these used at all? We want to use bindings wherever possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is where the handling is actually done. The bindings for up/down are dummies just to populate the footer. As far as I know when you use bindings you have no way to stop the event and so it was doing Textuals default scrolling behaviour when you pressed up and down.
I've hit a similar issue several times now - maybe I'm just missing something though. In my case I actually very rarely want the scrolling to occur when up/down is pressed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I think that is because the Widget class was using key events. I changed that in the tree pr to use bindings. You might find it will work with bindings now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That works with bindings now, thanks.
Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>
Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>
As pointed out in Textualize#1649, a `ChildrenUpdated` message is documented but it doesn't exist in the code. It looks like it got added during development, then removed after it was realised it wasn't needed, but presumably it got left in the docs: Textualize#1143 (comment)