Unable to scroll widget visible
While working on #2031 I found out that in some situations, you can't scroll a widget into visibility.
It seems that what you cannot do is scroll into visibility a widget that is a sub-widget of a compound widget.
I'm not sure if this is a bug or just a limitation. It would be nice if I could scroll to sub-widgets. On the other hand, if we consider a compound widget to be a unit, it also makes sense that we don't allow scrolling to specific parts of that unit.
Consider the app below; the compound widget MyCustomWidget contains two labels, one very tall and another one with a specific ID. Pressing T should scroll to that last label (to the bottom of the app) but it doesn't.
MWE
from textual.app import App, ComposeResult
from textual.containers import VerticalScroll
from textual.widgets import Label, Static
class MyCustomWidget(Static):
def compose(self) -> ComposeResult:
yield Label(("|\n" * 100)[:-1])
yield Label("CAN'T GET HERE", id="target")
class MyApp(App):
def compose(self) -> ComposeResult:
with VerticalScroll():
yield MyCustomWidget()
def key_t(self) -> None:
self.query_one("#target").scroll_visible()
if __name__ == "__main__":
MyApp().run()
Upon further thought, I'm not sure if this is a bug or just a limitation. The title, prose, and labels, of the issue have been edited to reflect that.
That should work. I am disappointed it didn't!
This still doesn't work in some situations. The example below contains a couple of levels of nesting.
Run the app below, scroll all the way down manually, then press either s or t.
The app scrolls a bit but it doesn't scroll enough to show the coloured lines that say "second" or "third", respectively.
App
from itertools import cycle
from textual.app import App, ComposeResult
from textual.containers import VerticalScroll
from textual.widgets import Label, Static
NAMES = [
"Paul Atreidies",
"Duke Leto Atreides",
"Lady Jessica",
"Gurney Halleck",
"Baron Vladimir Harkonnen",
"Glossu Rabban",
"Chani",
"Silgar",
]
class MyCustomWidget(Static):
def __init__(self, id):
self.__id = id
super().__init__()
def compose(self) -> ComposeResult:
yield Label(("|\n" * 10)[:-1])
blue = "[blue]@@@@@@@@@@@@@@@@@@@@@@@[/]\n" * 2
red = f"[red]========={self.__id}==========[/]\n" * 3
text = (blue + red + blue)[:-1]
yield Label(text, id=self.__id)
yield Label(("|\n" * 1)[:-1])
class MyApp(App):
CSS = """
#vertical {
height: 20;
}
VerticalScroll {
border: round $primary;
}
"""
def compose(self) -> ComposeResult:
with VerticalScroll():
for _, name in zip(range(50), cycle(NAMES)):
yield Label(name)
yield MyCustomWidget("first")
with VerticalScroll():
for _, name in zip(range(50), cycle(NAMES)):
yield Label(name)
yield MyCustomWidget("second")
with VerticalScroll(id="vertical"):
yield Label("[yellow]Vertical here[/]")
for _, name in zip(range(50), cycle(NAMES)):
yield Label(name)
yield MyCustomWidget("third")
for _, name in zip(range(30), cycle(NAMES)):
yield Label(name)
yield Label("[yellow]end of v[/]")
for _, name in zip(range(100), cycle(NAMES)):
yield Label(name)
for _, name in zip(range(50), cycle(NAMES)):
yield Label(name)
def key_f(self) -> None:
self.query_one("#first").scroll_visible()
def key_s(self) -> None:
self.query_one("#second").scroll_visible()
def key_t(self) -> None:
self.query_one("#third").scroll_visible()
def key_v(self) -> None:
self.query_one("#vertical").scroll_visible()
if __name__ == "__main__":
MyApp().run()
I think this same issue (or something very similar) has bit someone on Discord this morning. When there's nesting involved, the margin of the parent seems to not be taken into account. In the example below, if you press "x", our scrolling seems to be off-by-8, and the container has a margin-top of 8.
from textual.app import App, ComposeResult
from textual.containers import VerticalScroll, Container
from textual.widgets import Button
class ScrollVisibleMargin(App):
CSS = """
Container {
height: auto;
margin-top: 8;
border: wide red;
}
"""
def compose(self) -> ComposeResult:
with VerticalScroll():
with Container():
for index in range(1, 51):
yield Button(f"Hello, world! ({index})", id=f"b{index}")
def key_x(self):
button_twenty = self.query_one("#b26")
button_twenty.scroll_visible()
app = ScrollVisibleMargin()
if __name__ == "__main__":
app.run()
https://discord.com/channels/1026214085173461072/1033754296224841768/1164118381973217362
Original MRE appears to work. Darren's issue still seems to be present.