Skip to content

Conversation

@unixzii
Copy link
Contributor

@unixzii unixzii commented Jul 31, 2025

Rendering breaks when both an element and its parent have opacity set. The following code reproduces the issue:

struct Repro;

impl Render for Repro {
    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
        fn make_box(bg: impl Into<Fill>) -> impl IntoElement {
            div().size_8().bg(bg).hover(|style| style.opacity(0.5))
        }

        div()
            .flex()
            .items_center()
            .justify_center()
            .size(px(500.0))
            .hover(|style| style.opacity(0.5))
            .child(make_box(gpui::red()))
            .child(make_box(gpui::green()))
            .child(make_box(gpui::blue()))
    }
}

Before (broken behavior):

Screen.Recording.2025-07-31.at.22.46.30.mov

The child element resets its parent and siblings' opacity, which is an unexpected behavior.

After (fixed behavior):

Screen.Recording.2025-07-31.at.22.50.16.mov

Release Notes:

  • Fixed an issue where nested opacity is rendered incorrectly.
@cla-bot
Copy link

cla-bot bot commented Jul 31, 2025

We require contributors to sign our Contributor License Agreement, and we don't have @unixzii on file. You can sign our CLA at https://zed.dev/cla. Once you've signed, post a comment here that says '@cla-bot check'.

@unixzii
Copy link
Contributor Author

unixzii commented Jul 31, 2025

@cla-bot check

@cla-bot cla-bot bot added the cla-signed The user has signed the Contributor License Agreement label Jul 31, 2025
@cla-bot
Copy link

cla-bot bot commented Jul 31, 2025

The cla-bot has been summoned, and re-checked this pull request!

@unixzii unixzii changed the title gpui: Fix opacity state management Jul 31, 2025
Copy link
Member

@MrSubidubi MrSubidubi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, sorry for the slow reply, thank you for both finding and fixing this issue!

I think we can slightly improve this though: We don't need to change the element_opacity from an Option to a Vec, as we can just keep the old element_opacity value around within with_element_opacity and then reassign it after f was called instead of always resetting it to None. What do you think?

Also, I think it would be nice if we could test this somehow or at least extend one example to cover this behavior, if you see fit somewhere for that.

@MrSubidubi MrSubidubi self-assigned this Sep 1, 2025
@unixzii
Copy link
Contributor Author

unixzii commented Sep 2, 2025

Hi @MrSubidubi, thank you for the advice. Nice observation!

I'll keep the Option type and manage the opacity state properly in with_element_opacity. And I updated the opacity example to demonstrate the use case this PR fixes.

Before the fix:

Screen.Recording.2025-09-02.at.21.01.45.mov

After the fix:

Screen.Recording.2025-09-02.at.21.05.19.mov

Btw, there are also some other rendering properties that can be maintained without using Vec stack. Do you think we should remove them as well?

Copy link
Member

@MrSubidubi MrSubidubi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks better, thank you! I'll leave the final verdict here to @\mikayla-maki once she is back from vacation, and then we should be good to go!

One last thing - we could probably even change the self.element_opacity to a f32 only and just assign it 1.0 by default - that's basically what the unwrap_or call already does currently for us, only now in two places. But I don't think this is too important, happy to hear your opinion on it.


Also, I do think we would be interested, but this is probably better for a separate PR so we can also review this and decide upon separately. Yet, sounds good and reasonable to me, if you agree there, feel free to take a look!

@unixzii
Copy link
Contributor Author

unixzii commented Sep 8, 2025

One last thing - we could probably even change the self.element_opacity to a f32 only and just assign it 1.0 by default - that's basically what the unwrap_or call already does currently for us, only now in two places. But I don't think this is too important, happy to hear your opinion on it.

That makes sense -- I'll change it, since I think it should help with performance.

Also, I do think we would be interested, but this is probably better for a separate PR so we can also review this and decide upon separately. Yet, sounds good and reasonable to me, if you agree there, feel free to take a look!

Yeah, maybe I can open another PR for those later.

Copy link
Member

@MrSubidubi MrSubidubi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that this has been sitting around for quite some time without any objections, I am gonna go ahead and merge this. Thank you for this fix and being that receptive to feedback! Much appreciated and a sincere sorry this has been sitting here for so long.

Also, congratulations to your first contribution! 🎉

@MrSubidubi MrSubidubi merged commit d6becab into zed-industries:main Oct 10, 2025
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed The user has signed the Contributor License Agreement

3 participants