Skip to content
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

Problem with scrollable containers #4187

Closed
jakubziebin opened this issue Feb 20, 2024 · 7 comments
Closed

Problem with scrollable containers #4187

jakubziebin opened this issue Feb 20, 2024 · 7 comments

Comments

@jakubziebin
Copy link

There seems to be a problem with scrollable containers. Consider an example:

from __future__ import annotations

from textual.app import App, ComposeResult
from textual.widgets import Label
from textual.containers import ScrollableContainer, Grid


class MyApp(App):
    DEFAULT_CSS = """
    Grid {
        grid-size: 2;
        grid-rows: auto;
        height: auto;
    }
    
    Label {
        height: 1;
        background: red;
    }
    """

    def compose(self) -> ComposeResult:
        with ScrollableContainer(), Grid():
            yield Label("First")
            yield Label("Second")
            yield Label("Third")
            yield Label("Fourth")
            yield Label("Fifth")
            yield Label("Sixth")


MyApp().run()

In this case I would expect the scroll to appear when the Fifth and Sixth labels are hidden, but:
image
There is no reaction.
After adding some padding, scroll appears when all the labels are hidden, and when I scroll down I don't see any of them.

I have noticed a similar problem with TabbedContent:

from __future__ import annotations

from textual.app import App, ComposeResult
from textual.widgets import Label, TabPane, TabbedContent
from textual.containers import ScrollableContainer, Grid


class MyTabPane(TabPane):
    DEFAULT_CSS = """
    Grid {
        grid-size: 2;
        grid-rows: auto;
        padding: 2 4;
        height: auto;
    }

    Label {
        height: 1;
        background: red;
    }
    """

    def compose(self) -> ComposeResult:
        with ScrollableContainer(), Grid():
            yield Label("First")
            yield Label("Second")
            yield Label("Third")
            yield Label("Fourth")
            yield Label("Fifth")
            yield Label("Sixth")


class MyApp(App):
    def compose(self) -> ComposeResult:
        with TabbedContent():
            yield MyTabPane("example")


MyApp().run()

This is when the scroll appears and again I can't see any element that is under the ScrollableContainer.
image

Copy link

We found the following entries in the FAQ which you may find helpful:

Feel free to close this issue if you found an answer in the FAQ. Otherwise, please give us a little time to review.

This is an automated reply, generated by FAQtory

@TomJGooding
Copy link
Contributor

It looks like you just need to change DEFAULT_CSS to CSS or use the CSS_PATH.

Here's a modified example I used to test this, just so I didn't have to awkwardly resize my terminal:

from textual.app import App, ComposeResult
from textual.containers import Grid, ScrollableContainer
from textual.widgets import Label


class MyApp(App):
    # DEFAULT_CSS = """
    CSS = """
    Grid {
        grid-size: 2;
        grid-rows: auto;
        height: auto;
    }

    Label {
        height: 1;
        background: red;
    }
    """

    def compose(self) -> ComposeResult:
        with ScrollableContainer():
            with Grid():
                for i in range(1, 101):
                    yield Label(f"Label {i}")


if __name__ == "__main__":
    app = MyApp()
    app.run()

@TomJGooding
Copy link
Contributor

TomJGooding commented Feb 20, 2024

The TabbedContent example was missing the parent widget in the selector - see https://textual.textualize.io/guide/CSS/#selectors

Textual maintainers: I'm afraid I haven't kept up with nested CSS, but this might have revealed a bug...?

from textual.app import App, ComposeResult
from textual.containers import Grid, ScrollableContainer
from textual.widgets import Label, TabbedContent, TabPane


class MyTabPane(TabPane):
    DEFAULT_CSS = """
    MyTabPane Grid {
        grid-size: 2;
        grid-rows: auto;
        height: auto;
    }

    MyTabPane Label {
        height: 1;
        background: red;
    }
    """

    def compose(self) -> ComposeResult:
        with ScrollableContainer():
            with Grid():
                for i in range(1, 101):
                    yield Label(f"Label {i}")


class MyApp(App):
    def compose(self) -> ComposeResult:
        with TabbedContent():
            yield MyTabPane("example")


if __name__ == "__main__":
    app = MyApp()
    app.run()

@jakubziebin
Copy link
Author

Thank you for correcting me with this CSS. But I still see problems with scrolling. I notice the problem when something is above TabbedContent, consider an example:

from textual.app import App, ComposeResult
from textual.containers import Grid, ScrollableContainer, Horizontal
from textual.widgets import Label, TabbedContent, TabPane


class MyTabPane(TabPane):
    DEFAULT_CSS = """
    MyTabPane Grid {
        grid-size: 2;
        grid-rows: auto;
        height: auto;
    }

    MyTabPane Label {
        height: 1;
        background: red;
    }
    
    MyTabPane {
        height: auto;
    }
    """

    def compose(self) -> ComposeResult:
        with ScrollableContainer():
            with Grid():
                for i in range(1, 13):
                    yield Label(f"Label {i}")


class MyApp(App):
    def compose(self) -> ComposeResult:
        with Horizontal():
            yield Label("X")
            yield Label("X")
        with Horizontal():
            yield Label("Y")
            yield Label("Y")
        with TabbedContent():
            yield MyTabPane("example")


if __name__ == "__main__":
    app = MyApp()
    app.run()

The scroll for TabPane is always visible, even when no element is hidden. In addition, when labels 11 and 12 are hidden, the scroll for the grid is not visible.

@willmcgugan
Copy link
Collaborator

The best way to diagnose these issues is to add borders around element to see if they are bahaving the way you would expect. i.e. border: red.

  • You probably don't want to subclass TabPane, since the TabbedContent widget may make certain assumptions about its dimensions.
  • Horizontals, like other containers, will expand to fill as much widget as possible by default. You probably want to give them height auto if you don't need them to expand.
  • Other than that, all that was missing was to set the heights to 1r to use up the remaining space.

I'm assuming that is the behaviour you are looking for?

from textual.app import App, ComposeResult
from textual.containers import Grid, ScrollableContainer, Horizontal
from textual.widgets import Label, TabbedContent, TabPane


class MyTabContent(ScrollableContainer):
    DEFAULT_CSS = """
    MyTabContent Grid {
        grid-size: 2;
        grid-rows: auto;
        height: auto;
    }
    """

    def compose(self) -> ComposeResult:
        with Grid():
            for i in range(1, 13):
                yield Label(f"Label {i}")


class MyApp(App):
    CSS = """
    TabbedContent {
        height: 1fr;
    }
    TabbedContent ContentSwitcher {
        height: 1fr;
    }
    Horizontal {
        height: auto;
    }
    """

    def compose(self) -> ComposeResult:
        with Horizontal():
            yield Label("X")
            yield Label("X")
        with Horizontal():
            yield Label("Y")
            yield Label("Y")
        with TabbedContent():
            with TabPane("example"):
                yield MyTabContent()


if __name__ == "__main__":
    app = MyApp()
    app.run()

@jakubziebin
Copy link
Author

That's it, thank you very much! It was the missing height: 1fr in TabbedContent

Copy link

Don't forget to star the repository!

Follow @textualizeio for Textual updates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants