textual icon indicating copy to clipboard operation
textual copied to clipboard

Opacity/transparency issues with overlapped widgets

Open rodrigogiraoserrao opened this issue 2 years ago • 4 comments

This change [to how opacity works] might have inadvertently introduced a bug for layers with opacity. Here's a simple example adapted from the docs for Layers, with a binding that changes the opacity of #box1.

Before v0.29.0

before-screenshot

After v0.29.0

after-screenshot

Excerpt of a comment posted by @TomJGooding in https://github.com/Textualize/textual/issues/3652#issuecomment-1802888662

rodrigogiraoserrao avatar Nov 14 '23 14:11 rodrigogiraoserrao

The examples below show situations in which widget overlaps don't take transparencies into account correctly.

Base app:

from textual.app import App
from textual.widgets import Label


class BGApp(App):
    CSS_PATH = "bg.tcss"
    def compose(self):
        yield Label("below", id="below")
        yield Label("above", id="above")


if __name__ == "__main__":
    BGApp().run()
  1. Overlapping widgets because of layers and the top one has a transparent background:
Screen {
    layers: below above;
}

#below {
    width: 30;
    height: 5;
    layer: below;
    background: blue;
}

#above {
    width: 20;
    height: 3;
    layer: above;
    background: red 0%;
}
Screenshot 2023-11-14 at 14 29 26
  1. Overlapping widgets because of layers and opacity: 0% set on the top one:
Screen {
    layers: below above;
}

#below {
    layer: below;
    width: 30;
    height: 5;
    background: blue;
}

#above {
    layer: above;
    width: 20;
    height: 3;
    background: red;
    opacity: 0%;
    # background: red 0%;
}
Screenshot 2023-11-14 at 14 46 53
  1. Overlapping widgets because of offsets and the top one has opacity: 0%:
#below {
    width: 30;
    height: 5;
    background: blue;
}

#above {
    offset: 0 -3;
    width: 20;
    height: 3;
    background: red;
    opacity: 0%;
}
Screenshot 2023-11-14 at 14 49 22
  1. Overlapping widgets because of offsets and top one has transparent background:
#below {
    width: 30;
    height: 5;
    background: blue;
}

#above {
    offset: 0 -3;
    width: 20;
    height: 3;
    background: red 0%;
}
Screenshot 2023-11-14 at 14 50 05

rodrigogiraoserrao avatar Nov 14 '23 14:11 rodrigogiraoserrao

Thanks @rodrigogiraoserrao for checking all these scenarios.

After looking at this again , it seems only the opacity style worked before v0.29, whereas the background percentage has possibly never been taken into account.

This probably isn't helpful given the significant changes in #2814, but making a note just in case.

EDIT: Digging deeper, actually only opacity: 0% worked before v0.29. For example background: white; opacity: 50%; would show as grey, rather than a light blue as you might expect.

TomJGooding avatar Mar 14 '24 21:03 TomJGooding

Confirmed this is still broken on main as of 17th July 2024 (commit hash: 0d256073740264bf).

MRE without external CSS file:

from textual.app import App
from textual.widgets import Label


class BGApp(App):
    CSS = """\
Screen {
    layers: below above;
}

#below {
    width: 30;
    height: 5;
    layer: below;
    background: blue;
}

#above {
    width: 20;
    height: 3;
    layer: above;
    background: red 0%;
}
"""

    def compose(self):
        yield Label("below", id="below")
        yield Label("above", id="above")


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

darrenburns avatar Jul 17 '24 09:07 darrenburns