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

Question: How do I style within TabbedContent? #4896

Closed
grahamwoodward opened this issue Aug 18, 2024 · 16 comments
Closed

Question: How do I style within TabbedContent? #4896

grahamwoodward opened this issue Aug 18, 2024 · 16 comments

Comments

@grahamwoodward
Copy link

grahamwoodward commented Aug 18, 2024

Previously I just had some DataTables/TextAreas and within my CSS file I'd specified within the Screen { ... } the layout/grid-size and each DT/TA had an id, which I could also reference (#<my_id>) within the CSS to specify how many rows/columns they spanned etc etc. Now I'm trying to add tabbed content, I basically want all what I had in its own tab, so I can have a completely different layout on another tab...but with TabbedContent, my CSS isn't taking affect...presumably because the layout/screen is now "wrapped" within the TC...I tried changing #<my_id> to TabbedContent #--content-tab-<my_id> without success

Copy link

We found the following entry 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

@grahamwoodward
Copy link
Author

grahamwoodward commented Aug 18, 2024

I have the following

122     def compose(self) -> ComposeResult:
123         """Create child widgets for the app."""
124         yield Footer()
125         with TabbedContent(initial="default"):
126             with TabPane("Default", id="default"):
127                 yield DataTable(id="allocs")
128                 yield TextArea.code_editor("Some random text", id="testA", read_only=True, show_line_numbers=False)
129                 yield DataTable(id="testB")
130                 yield DataTable(id="testC")
131                 yield DataTable(id="testD")
132                 yield DataTable(id="testE")
133                 yield DataTable(id="testF")
134             with TabPane("Interleaving", id="interleaving"):
135                 yield Markdown("Interleaving - coming soon")

and a snipet of my CSS is

 1 TabbedContent #--content-tab-default {
  2     layout: grid;
  3     grid-size: 4 5;
  4 }
  5
  6 .box {
  7     height: 100%;
  8     border: solid green;
  9 }
 10
 11 #allocs {
 12     column-span: 3;
 13     row-span: 2;
 14     height: 40;
 16 }

but in the "default" tab, each widget is on its own line (1 column) and also much larger in height so I'm having to scroll down on the screen

@grahamwoodward
Copy link
Author

Appreciate any guidance/help/pointers

@TomJGooding
Copy link
Contributor

TomJGooding commented Aug 19, 2024

You don't need to prefix your IDs. Are you sure none of your CSS is taking effect, or just that the layout isn't quite what you expected?

Here's a quick example:

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


class ExampleApp(App):
    CSS = """
    TabbedContent #my-tab {
        layout: grid;
        grid-size: 4 5;
        border: red;
    }

    Label {
        width: 100%;
        text-align: center;
        border: solid white;
    }
    """

    def compose(self) -> ComposeResult:
        with TabbedContent():
            with TabPane("My Tab", id="my-tab"):
                for i in range(1, 21):
                    yield Label(f"{i}")


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

@grahamwoodward
Copy link
Author

grahamwoodward commented Aug 19, 2024

with that change

121     def compose(self) -> ComposeResult:
122         """Create child widgets for the app."""
123         yield Footer()
124         with TabbedContent():
125             with TabPane("Default", id="default"):
127                 yield DataTable(id="allocs")
128                 yield TextArea.code_editor("Some random text", id="testA", read_only=True, show_line_numbers=False)
129                 yield DataTable(id="testB")
130                 yield DataTable(id="testC")
131                 yield DataTable(id="testD")
132                 yield DataTable(id="testE")
133                 yield DataTable(id="testF")
134             with TabPane("Interleaving", id="interleaving"):
135                 yield Markdown("Interleaving - coming soon")

and in my CSS file I now have

  1 TabbedContent #default {
  2     layout: grid;
  3     grid-size: 4 5;
  4 }
  5
  6 .box {
  7     height: 100%;
  8     border: solid green;
  9 }
 10
 11 #allocs {
 12     column-span: 3;
 13     row-span: 2;
 14     height: 40;
 16 }
 17
 18 #testA {
 19     row-span: 2;
 20     height: 40;
 22 }
...
...
...

I now end up with just the tabs and nothing else on the screen

Screenshot 2024-08-19 at 08 02 55

@grahamwoodward
Copy link
Author

hmm so if I completely remove the CSS stuff for the different DataTables/TextAreas...then the layout is "expected" i.e 5 columns across 4 rows...so presumably the # need to be prefixed by the TabbedContent and/or TabPane id?

@grahamwoodward
Copy link
Author

Ah they need to be TabbedContent #

@grahamwoodward
Copy link
Author

hmm something still not right...if I use

TabbedContent #allocs {
...
}

Then that looks as expected..however I update the other widgets in the CSS, then the screen is back to as above?!?!

124         with TabbedContent():
125             with TabPane("Default", id="default"):
127                 yield DataTable(id="allocs")
128                 yield TextArea.code_editor("Some random text", id="testA", read_only=True, show_line_numbers=False)
129                 yield DataTable(id="testB")
130                 yield DataTable(id="testC")
131                 yield DataTable(id="testD")
132                 yield DataTable(id="testE")
133                 yield DataTable(id="testF")
134             with TabPane("Interleaving", id="interleaving"):
135                 yield Markdown("Interleaving - coming soon")

If the CSS is

 1 TabbedContent #default {
  2     layout: grid;
  3     grid-size: 4 5;
  4 }
  5
  6 TabbedContent #allocs {
  7     column-span: 3;
  8     row-span: 2;
  9     height: 40;
 10 }

Then I see the allocs DT span 3 columns with testA TextArea next to it. However when I then add

 12 TabbedContent #testA {
 13     column-span: 2;
 14     row-span: 2;
 15     height: 40;
 16 }

Then the screen is like so:

Screenshot 2024-08-19 at 08 18 42

@grahamwoodward
Copy link
Author

grahamwoodward commented Aug 19, 2024

also I thought the docs said for the TabPane id, it needed to be --content-tab-<the id of the TabPane> when referenced in the CSS?

@TomJGooding
Copy link
Contributor

Could you post a screenshot of your app before you added the TabbedContent? This might help understand the layout you are expecting.

also I thought the docs said for the TabPane id, it needed to be --content-tab-<the id of the TabPane> when referenced in the CSS?

I think perhaps you are confusing this with how to style the tab titles, not the content: https://textual.textualize.io/widgets/tabbed_content/#styling

@grahamwoodward
Copy link
Author

Screenshot 2024-08-19 at 08 52 46

should look something like ^^

The CSS for that is

 1 Screen {
  2     layout: grid;
  3     grid-size: 4 5;
  4 }
  5
  6 #allocs {
  7     column-span: 3;
  8     row-span: 2;
  9     height: 40;
 10 }
 11
 12 #testA {
 13     column-span: 2;
 14     row-span: 2;
 15     height: 40;
 16 }
 17
 18 #testB {
 19     height: 10;
 20     border: round $accent;
 21 }
22
 23 #testC {
 24     height: 10;
 25     border: round $accent;
 26 }
 27
 28 #testD {
 29     height: 10;
 30     border: round $accent;
 31 }
 32
 33 #testE {
 34     height: 10;
 35     border: round $accent;
 36 }
 37
 38 #testF {
 39     column-span: 3;
 40     height: 50;
 41     border: round $accent;
 42 }
...
...

and the python is

119     def compose(self) -> ComposeResult:
120         """Create child widgets for the app."""
121         yield Footer()
122         #with TabbedContent():
123         #    with TabPane("Default", id="default"):
124         yield DataTable(id="allocs")
125         yield TextArea.code_editor(TA_TEXT, id="testA", read_only=True, show_line_numbers=False)
126         yield DataTable(id="testB")
127         yield DataTable(id="testC")
128         yield DataTable(id="testD")
129         yield DataTable(id="testE")
130         yield DataTable(id="testF")
131         #    with TabPane("Interleaving", id="interleaving"):
132         #        yield Markdown("Interleaving - coming soon")
133

@TomJGooding
Copy link
Contributor

Here's my quick attempt to approximate your layout - am I close? I haven't included the heights of 40/50, as that doesn't seem to be reflected in your screenshot and seem very big!

from textual.app import App, ComposeResult
from textual.widgets import DataTable, TabbedContent, TabPane, TextArea


class ExampleApp(App):
    CSS = """
    #default {
        layout: grid;
        grid-size: 4 5;
    }

    #allocs {
        column-span: 3;
        row-span: 2;
    }

    #testA {
        column-span: 2;
        row-span: 2;
    }

    .foo {
        height: 10;
        border: round $accent;
    }

    #testF {
        column-span: 3;
        border: round $accent;
    }
    """

    def compose(self) -> ComposeResult:
        with TabbedContent():
            with TabPane("Default", id="default"):
                yield DataTable(id="allocs")
                yield TextArea("Some random text", id="testA")
                yield DataTable(id="testB", classes="foo")
                yield DataTable(id="testC", classes="foo")
                yield DataTable(id="testD", classes="foo")
                yield DataTable(id="testE", classes="foo")
                yield DataTable(id="testF")

    def on_mount(self) -> None:
        for table in self.query(DataTable):
            table.add_columns("C1", "C2", "C3")
            table.add_row("R1C1", "R1C2", "R1C3")


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

@grahamwoodward
Copy link
Author

yeah that looks fine when I run it

@TomJGooding
Copy link
Contributor

TomJGooding commented Aug 19, 2024

By the way, you can use the grid-rows style if you want to define the height of the rows of the grid.

For example, here's a modified version of my simplified example with a 4x3 grid:

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


class ExampleApp(App):
    CSS = """
    #my-tab {
        layout: grid;
        grid-size: 4 3;
        grid-rows: 15 10 5;
    }

    #label-1 {
        column-span: 3;
    }

    #label-2 {
        column-span: 1;
    }

    #label-7 {
        column-span: 3;
    }

    Label {
        height: 100%;
        width: 100%;
        content-align: center middle;
        border: solid white;
    }
    """

    def compose(self) -> ComposeResult:
        with TabbedContent():
            with TabPane("My Tab", id="my-tab"):
                for i in range(1, 8):
                    yield Label(f"{i}", id=f"label-{i}")


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

@grahamwoodward
Copy link
Author

Sorted now. Thanks for the help. I'll close this issue

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

2 participants