-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
UI Node Border Radius and Shadows #8973
Conversation
`UiCornerRadius` * New component that sets the radius of rounded corners for nodes. Wraps an f32 value. * Currently all corners of a node share the same radius. * Not tested yet, but should be broken for UI texture atlas images. Need to make changes to `prepare_uinodes`. `node_bundles` module * Added the `UiCornerRadius` component to all UI node bundles except for `TextBundle`. `ExtractedUiNode` * Added a `corner_radius` field. `extract_uinode_borders` * Repurposed the UV coords to control which part of the border is drawn. `UiVertex` * Added `radius` and `size` fields which are needed to draw the rounded corners. `UiPipeline` * Added vertex attributes for radius and size. `ui.wgsl` * Checks to see if we are outside a corner edge and if so sets the alpha channel to zero.
…only have min = zero, max = size.
…e otherwise it always draws a border middle section
* Refactored to remove duplicate code. `ui.wgsl` * Switched colors for the inverted mix function, which were in the wrong the wrong order. `button` example * Added a rounded border to the button.
Fixes bulging in corner curves. Updates examples to use new features. `UiCornerRadius` * Now wraps a `[f32; 4]` value. * Added a method `all` that sets all four corners to the same radius. `UiPipeline` * Added a `Float32x4` attribute to take the border thickness. * Radius attribute changed to a `Float32x4` `extract_uinode_borders` * No longer assembles a border from multiple rects, much simpler now. Most of this function was deleted. `ExtractedUiNode` * Added an [f32; 4] `thickness` field * Type of `corner_radius` changed to `[f32; 4]` `ui.wgsl` * Reworked to support new features and bug fixes.
…ed edges. `UiCornerRadius` * Now has named fields `top_left`, `top_right`, `bottom_left` and `bottom_right`. * Added lots of helper functions. * Implemented `From<UiCornerRadius>` for `[f32; 4] `prepare_uinodes` * Changed order of thickness values when added to `UiVertex`. Don't remember why. `ui.wgsl` * Cleaned up and fixed some bugs where corners and edges were taking wrong radius and thickness values.
Fixed all the obvious bugs. Just percentage and viewport Val support left to implement. |
The fields are now type `Val`. Full support for all variants, `Auto` maps to `0.` `ExtractUiNodes`: `thickness` field renamed to `border`. `UiVertex`: `thickness` field renamed to `border`.
|
* `UiBorderRadius` no longer a component and removed from all node bundles. `UiBorderRadius` * Renamed from `UiCornerRadius` * No longer a component, moved to a field of `Style` instead. * Added a const DEFAULT. * Default radii changed to `Val::Px(0.)` from `Val::Auto`. `Style` * Added `border_radius: UiBorderRadius` field. `ExtractedUiNodes` * `corner_radius` renamed to `border_radius` `extract_uinodes`, `extract_uinode_borders`, and `extract_atlas_uinodes`: Now query for `Style` instead of `UiBorderRadius`.
* Added a border to each button. * Borders can be disabled with the "no-borders" commandline argument.
Random question - why does the shadow on the light, transparent circle increase the brightness? That's not a shadow :) |
It would also be nice to be able to choose the shadow color. |
Squircles https://en.wikipedia.org/wiki/Squircle#/media/File:Squircle_rounded_square.svg |
I just wanted to add that if clipping is holding up this PR, I would say go ahead and commit even if clipping is not there yet, and finish it in a future PR. While round-corner clipping is very handy to have, there are lots of use cases that don't require clipping. And since no one is forced to use rounded corners, it is "strictly no worse" than what they had previously. The same logic holds for shadows. |
Found an article on how to render box shadows well on the GPU https://madebyevan.com/shaders/fast-rounded-rectangle-shadows |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty good!
I have a lot of nitpicks here, so I wanted to tell you that the main problem is examples/games/game_menu.rs:709
. Here you used UiBorderRadius::max()
which doesn't exist. Use UiBorderRadius::MAX
instead.
@@ -698,6 +706,7 @@ mod menu { | |||
width: Val::Px(200.0), | |||
height: Val::Px(65.0), | |||
margin: UiRect::all(Val::Px(20.0)), | |||
border_radius: UiBorderRadius::max(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UiBorderRadius::max()
doesn't exist, did you mean UiBorderRadius::MAX
?
border_radius: UiBorderRadius::max(), | |
border_radius: UiBorderRadius::MAX, |
/// assert_eq!(ui_rect.bottom, Val::Vw(25.0)); | ||
/// ``` | ||
#[inline] | ||
pub fn with_left(mut self, left: Val) -> UiRect { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should probably use Self
instead of UiRect
here. (Repeat with similar methods)
pub fn with_left(mut self, left: Val) -> UiRect { | |
pub fn with_left(mut self, left: Val) -> Self { |
@@ -465,6 +465,7 @@ mod tests { | |||
], | |||
grid_column: GridPlacement::start(4), | |||
grid_row: GridPlacement::span(3), | |||
border_radius: Default::default(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
border_radius: Default::default(), | |
border_radius: UiBorderRadius::default(), |
@@ -163,7 +168,7 @@ fn spawn_button_row(parent: &mut ChildBuilder, constraint: Constraint, text_styl | |||
align_items: AlignItems::Stretch, | |||
..Default::default() | |||
}, | |||
background_color: Color::BLACK.into(), | |||
//background_color: Color::BLACK.into(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
//background_color: Color::BLACK.into(), |
pub const MAX: Self = Self { | ||
top_left: Val::Px(f32::MAX), | ||
top_right: Val::Px(f32::MAX), | ||
bottom_right: Val::Px(f32::MAX), | ||
bottom_left: Val::Px(f32::MAX), | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pub const MAX: Self = Self { | |
top_left: Val::Px(f32::MAX), | |
top_right: Val::Px(f32::MAX), | |
bottom_right: Val::Px(f32::MAX), | |
bottom_left: Val::Px(f32::MAX), | |
}; | |
pub const MAX: Self = Self::all(Val::Px(f32::MAX)), |
height: Val::Px(100.0), | ||
left: Val::Px(-10.), | ||
bottom: Val::Px(-10.), | ||
border_radius: UiBorderRadius::all(Val::Px(f32::MAX)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
border_radius: UiBorderRadius::all(Val::Px(f32::MAX)), | |
border_radius: UiBorderRadius::MAX, |
position_type: PositionType::Absolute, | ||
left: Val::Px(30.), | ||
bottom: Val::Px(20.), | ||
border_radius: UiBorderRadius::all(Val::Px(f32::MAX)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
border_radius: UiBorderRadius::all(Val::Px(f32::MAX)), | |
border_radius: UiBorderRadius::MAX, |
position_type: PositionType::Absolute, | ||
left: Val::Px(70.), | ||
bottom: Val::Px(50.), | ||
border_radius: UiBorderRadius::all(Val::Px(f32::MAX)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
border_radius: UiBorderRadius::all(Val::Px(f32::MAX)), | |
border_radius: UiBorderRadius::MAX, |
position_type: PositionType::Absolute, | ||
left: Val::Px(110.), | ||
bottom: Val::Px(80.), | ||
border_radius: UiBorderRadius::all(Val::Px(f32::MAX)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
border_radius: UiBorderRadius::all(Val::Px(f32::MAX)), | |
border_radius: UiBorderRadius::MAX, |
position_type: PositionType::Absolute, | ||
left: Val::Px(150.), | ||
bottom: Val::Px(110.), | ||
border_radius: UiBorderRadius::all(Val::Px(f32::MAX)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
border_radius: UiBorderRadius::all(Val::Px(f32::MAX)), | |
border_radius: UiBorderRadius::MAX, |
@ickshonpe Are you still working on this PR? |
Q: Are there any plans to support "inset" shadows? In CSS, I occasionally use this feature for things like text input fields, slider tracks, and other widgets that require a "depressed" look. Implementation-wise, this should just involve drawing the shadow on top of the background (inside the border) and inverting the signed distance calculation. Also, given that this appears to be stalled, would it make sense to split out the changes to UiRect as a separate PR and commit that now? Those seem relatively uncontroversial. |
The creator of this PR has been inactive for a while. Maybe it's time to add the
S-Adopt-Me
|
# Objective Implements border radius for UI nodes. Adopted from #8973, but excludes shadows. ## Solution - Add a component `BorderRadius` which contains a radius value for each corner of the UI node. - Use a fragment shader to generate the rounded corners using a signed distance function. <img width="50%" src="https://github.com/bevyengine/bevy/assets/26204416/16b2ba95-e274-4ce7-adb2-34cc41a776a5"></img> ## Changelog - `BorderRadius`: New component that holds the border radius values. - `NodeBundle` & `ButtonBundle`: Added a `border_radius: BorderRadius` field. - `extract_uinode_borders`: Stripped down, most of the work is done in the shader now. Borders are no longer assembled from multiple rects, instead the shader uses a signed distance function to draw the border. - `UiVertex`: Added size, border and radius fields. - `UiPipeline`: Added three vertex attributes to the vertex buffer layout, to accept the UI node's size, border thickness and border radius. - Examples: Added rounded corners to the UI element in the `button` example, and a `rounded_borders` example. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Zachary Harrold <zac@harrold.com.au> Co-authored-by: Pablo Reinhardt <126117294+pablo-lua@users.noreply.github.com>
Hi! Are there any other work that needs to be done other than the code style changes? |
Oh I see. With #12500 merged, I believe the next work for this PR is to get the shadows in? |
Border radius was added in #12500. Shadows should be their own PR. |
Objective
Implement border radius and shadows for Bevy UI nodes.
fixes #5924
closely related PRs #7795, #8381, #3991
Solution
Add a component
UiBorderRadius
which contains a radius value for each corner of the UI node.The fragment shader uses a signed distance function to generate the rounded corners effect.
Remaining issues and Limitations
*UiBorderRadius
only takes values in logical pixels. Supporting all theVal
variants shouldn't be a problem. Mainly just need to work out sensible rules for how percentage values should be resolved.* The border radius value is clamped before being sent to the shader but with very unbalanced opposing borders you can get weird-looking results.* Anti-aliasing still seems slightly off.Changelog
Style
Added
border_radius: UiBorderRadius
field.UiBorderRadius
New struct that holds the border radius values.
extract_uinode_borders
Stripped down, most of the work is done in the shader now. Borders are no longer assembled from multiple rects, instead the shader uses a signed distance function to draw the border.
ExtractedUiNode
Added
border
andcorner_radius
fields.UiVertex
Added
size
,border
andradius
fields.UiPipeline
Added three vertex attributes to the vertex buffer layout, to accept the UI node's size, border thickness and border radius.
examples
Added rounded corners to UI elements in the
button
,ui
,size_constraints
andborders
examples.Migration Guide