Skip to content

Color swatch widget. #20237

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open

Color swatch widget. #20237

wants to merge 4 commits into from

Conversation

viridia
Copy link
Contributor

@viridia viridia commented Jul 22, 2025

A simple color swatch with an alpha pattern; this has been split out from the other color widgets.

The main reason for wanting this in is to set up the infrastructure for custom shaders in feathers.

Part of #16900

Note this is pre-BSN, and will need to be revised when merging with the BSN stuff. I deliberately left some stuff out to make merging easier.

color-swatch

@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible A-UI Graphical user interfaces, styles, layouts, and widgets M-Needs-Release-Note Work that should be called out in the blog due to impact S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Jul 22, 2025
return vec4<f32>(bg, alpha);
}

// From: https://github.com/bevyengine/bevy/pull/8973
Copy link
Member

Choose a reason for hiding this comment

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

Is there no cleaner way to reuse this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's two problems with that:

  1. Sharing shader code is hard
  2. It's not exactly the same code, I had to tweak some of the constants to avoid a tiny AA halo problem.

@viridia
Copy link
Contributor Author

viridia commented Jul 22, 2025

@alice-i-cecile The way I have implemented this may not be the best way, but it's the only way that works currently.

ColorSwatch is a more complex widget than it might appear to be. It doesn't just display a color: it also updates the color displayed in response to user actions (such as dragging an RGB slider). This means that it needs to have a way to easily and dynamically modify what color is being displayed.

In a reactive framework this would be trivial: the swatch would simply take a Color-valued signal as input. However, we don't have that, so we have to look for another way.

Now, you might think, "Oh, just modify the BackgroundColor component" but that won't work. The reason is that the widget, as currently constructed, has two entities, one in front of the other. Since there is no way for a child entity to appear behind its parent (other than using GlobalZIndex), the child has to be the foreground element. So the color we are displaying is on the child entity, while the alpha pattern that bleeds through is on the root element.

This means that in order to update the displayed color, you need to modify the BackgroundColor of the child element. This requires the caller to know which element to update. As a consequence, you can't set the swatch color merely by patching in a BackgroundColor component at the right place in a BSN block - not unless we want to divide the swatch into two separate templates.

This also assumes that the compositing operation between a child entity and it's parent is correct: recall that shaders deal in linear colors, so the alpha blend factor used will be a linear alpha. Fortunately, when we pass a Color to a shader as a parameter, the alpha value is gamma-corrected, so (AFAIK) the compositing is correct. This is fortunate because we have no control over this: that is, while we might be able to control the mixing of colors within a WGSL shader, we have no way to control what color space of pixels are stored in the frame buffer, which is what controls compositing between successive rendering operations.

A different approach would be to use a single entity, with a custom shader that does both the alpha pattern and the constant color, and do the mixing ourselves. This is in fact how I did it in bevy_thorium, and in some ways it is simpler. The problem is that changing the color requires updating a shader uniform, and there's no general mechanism in Bevy for updating shader uniforms in response to changes to a Component. In bevy_reactor, the way I handled this was to give each color swatch it's own instance of UiMaterial (registering them with the AssetServer), and then have a reactive effect which updated the shader uniforms in response to changes in the input Signal. But again, that's not available.

Another strategy is to use GlobalZIndex to force the child entity behind its parent. This would allow the child to contain the alpha pattern, and the root to contain the constant color. However, if the user decides they want to use GlobalZIndex for some other purpose, they are screwed: the child's z-index can't be less than zero (otherwise it will be behind the window background and won't show at all), but must be less than the parent's z-index. Any z-index value we pick may come into conflict with other elements at other global z-indices. Regular z-index won't work, since it only controls the relative order of siblings and can't cause a child to appear behind its parent.


/// Observer to fill in the material handle (since we don't have access to the materials asset
/// in the template)
fn on_add_color_swatch(

Choose a reason for hiding this comment

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

I don't want to nitpick at all, but when I read the name of the function and then the list of parameters, I expected something like "On<Add, ColorSwatch>" because that's literally what the function is named. Instead it's AlphaPattern.

Maybe AlphaPattern should be renamed? Or this function? (Yes, I saw line 44 and the mut q_swatch, which again doesn't query a ColorSwatch, but a MaterialNode

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for spotting that. Cut & paste error from an earlier refactor.

@@ -39,7 +39,7 @@ pub(crate) struct AlphaPattern;

/// Observer to fill in the material handle (since we don't have access to the materials asset
/// in the template)
fn on_add_color_swatch(
fn on_add_alpha_pattern(
ev: On<Add, AlphaPattern>,
mut q_swatch: Query<&mut MaterialNode<AlphaPatternMaterial>>,
Copy link

@mogambro mogambro Jul 22, 2025

Choose a reason for hiding this comment

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

@viridia sorry, not to be annoying, but is this intended? Or should it be just q or q_material or something?

I am sorry, I am a non-native speaker, it just stands out. I may shut up at any point if you tell me to.

Super grateful for all your work related to UI... it's the sore spot in bevy and you do amazing stuff!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Doh...!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

BTW it's possible that much of this can go away when we migrate this widget to BSN, since we have access to the asset server.

Copy link
Contributor

@ickshonpe ickshonpe left a comment

Choose a reason for hiding this comment

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

Seems fine considering the limitations of the UI material API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-UI Graphical user interfaces, styles, layouts, and widgets C-Feature A new feature, making something new possible M-Needs-Release-Note Work that should be called out in the blog due to impact S-Needs-Review Needs reviewer attention (from anyone!) to move forward
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants