Textualize / textual

The lean application framework for Python. Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser.
https://textual.textualize.io/
MIT License
25.13k stars 768 forks source link

Ignore CSS styling in `DirectoryTree` #4028

Closed JoeZiminski closed 7 months ago

JoeZiminski commented 8 months ago

Hi, in our program (image below) we have overriden the render_label function on DirectoryTree to apply custom styling to different nodes (depending on their file-transfer status). However, when these are clicked the color is overridden by the default .CSS, and I asked on Discord and don't believe it is possible to set a kind of 'null' .CSS entry to just use the underlying styling.

As a workaround we can excise the relevant lines in _tree.py (e.g. here). However, it would be nice to acheive this without overriding the entire function.

Would you consider factoring out the blocks of code that handle this setting of CSS in the function e.g.

for this block to be replaced with a function call to something like:

def get_label_style():
            label_style = self.get_component_rich_style("tree--  label", partial=True)
            if self.hover_line == y:
                label_style += self.get_component_rich_style(
                    "tree--highlight", partial=True
                )
            if self.cursor_line == y:
                label_style += self.get_component_rich_style(
                    "tree--cursor", partial=False
                )
    return label_style

and something similar for the lines.

I'm sure there are nicer and more generalisable ways to do this, but from a selfish point of veiw it would work great 😄 .

Cheers, Joe

image

rodrigogiraoserrao commented 8 months ago

Hey there! I'm struggling to understand exactly what the underlying issue seems to be, maybe some sort of MRE would be helpful? Something with code that I can run.

I say this because I created a silly subclass of DirectoryTree that changes the colour of files/folders that have an a in the name and that custom colour doesn't go away when I click those files/folders or when I navigate the directory tree. The recording below shows said directory tree in action where all files/folders with an a in the name are coloured green:

https://github.com/Textualize/textual/assets/5621605/0759bdec-5acd-4d24-ae7c-f9a52b590bd5

Code ```py from textual.app import App, ComposeResult from textual.widgets import DirectoryTree class MyDictT(DirectoryTree): def render_label(self, node, base_style, style): text = super().render_label(node, base_style, style) if "a" in str(node.data.path): text.stylize("green") return text class MyApp(App[None]): def compose(self) -> ComposeResult: yield MyDictT(".") if __name__ == "__main__": MyApp().run() ```
JoeZiminski commented 8 months ago

Thanks @rodrigogiraoserrao! Apologies, did not think to supply an MRE 🤦‍♂️ Please find below:


from rich.style import Style
from rich.text import Text
from textual.app import App
from textual.widgets import DirectoryTree
from textual.widgets._directory_tree import DirEntry
from textual.widgets._tree import TOGGLE_STYLE, TreeNode

ROOT_PATH = "path/to/some/folder"
FOLDERNAME_TO_HIGHLIGHT = ["dir_1", "dir_2"]

class MyDirectoryTree(DirectoryTree):

    DEFAULT_CSS = """
    MyDirectoryTree:focus > .tree--cursor  {
    background: $panel-lighten-2;
    }
    """

    def render_label(
        self, node: TreeNode[DirEntry], base_style: Style, style: Style
    ) -> Text:
        """
        This function is exactly the same as the parent method
        except for the `style_node_color` call.
        """

        node_label = node._label.copy()
        node_label.stylize(style)

        node_path = node.data.path

        if node._allow_expand:
            prefix = (
                "📂 " if node.is_expanded else "📁 ",
                base_style + TOGGLE_STYLE,
            )
            node_label.stylize_before(
                self.get_component_rich_style(
                    "directory-tree--folder", partial=True
                )
            )
        else:
            prefix = (
                "📄 ",
                base_style,
            )
            node_label.stylize_before(
                self.get_component_rich_style(
                    "directory-tree--file", partial=True
                ),
            )
            node_label.highlight_regex(
                r"\..+$",
                self.get_component_rich_style(
                    "directory-tree--extension", partial=True
                ),
            )

        self.style_node_color(node_label, node_path)

        text = Text.assemble(prefix, node_label)
        return text

    def style_node_color(self, node_label, node_path):
        if node_path.stem in FOLDERNAME_TO_HIGHLIGHT:
            node_label.stylize_before("bright_red")

class TuiApp(App):

    def compose(self):
        yield MyDirectoryTree(ROOT_PATH)

if __name__ == "__main__":
    TuiApp().run()

It looks long because of the override of render_label but the only change in the overriden method is the call to style_node_color. To highlight some directories you will need to replace ROOT_PATH and FOLDERNAME_TO_HIGHLIGHT with some arbitary directory path on your system, and folder name/s within it, repsectively.

I wonder why we are seeing different behaviour, maybe it is due to how we are styling the node (in the overriden render_label). The result I get is like the below:

image

Thanks a lot for taking a look at this!

rodrigogiraoserrao commented 7 months ago

You don't have to copy and paste the whole code only to add an extra function call at the end.

This would do the same thing as your code:

    def render_label(
        self, node: TreeNode[DirEntry], base_style: Style, style: Style
    ) -> Text:
        text = super().render_label(node, base_style, style)
        self.style_node_color(text, node.data.path)
        return text

As for the overriding, notice that in my comment above I'm using Text.stylize whereas your code is using Text.stylize_before:

I'll close this issue as it seems to me it is resolved. Feel free to reopen if you feel I missed something!

github-actions[bot] commented 7 months ago

Don't forget to star the repository!

Follow @textualizeio for Textual updates.