mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 20:53:53 +00:00
b78a060af2
# Objective Change UI clipping to respect borders and padding. Fixes #15335 ## Solution Based on #15163 1. Add a `padding` field to `Node`. 2. In `ui_layout_size` copy the padding values from taffy to `Node::padding`. 4. Determine the node's content box (The innermost part of the node excluding the padding and border). 5. In `update_clipping` perform the clipping intersection with the node's content box. ## Notes * `Rect` probably needs some helper methods for working with insets but because `Rect` and `BorderRect` are in different crates it's awkward to add them. Left for a follow up. * We could have another `Overflow` variant (probably called `Overflow::Hidden`) to that clips inside of the border box instead of the content box. Left it out here as I'm not certain about the naming or behaviour though. If this PR is adopted, it would be trivial to add a `Hidden` variant in a follow up. * Depending on UI scaling there are sometimes gaps in the layout: <img width="532" alt="rounding-bug" src="https://github.com/user-attachments/assets/cc29aa0d-44fe-403f-8f0e-cd28a8b1d1b3"> This is caused by existing bugs in `ui_layout_system`'s coordinates rounding and not anything to do with the changes in this PR. ## Testing This PR also changes the `overflow` example to display borders on the overflow nodes so you can see how this works: #### main (The image is clipped at the edges of the node, overwriting the border). <img width="722" alt="main_overflow" src="https://github.com/user-attachments/assets/eb316cd0-fff8-46ee-b481-e0cd6bab3f5c"> #### this PR (The image is clipped at the edges of the node's border). <img width="711" alt="content-box-clip" src="https://github.com/user-attachments/assets/fb302e56-9302-47b9-9a29-ec3e15fe9a9f"> ## Migration Guide Migration guide is on #15561 --------- Co-authored-by: UkoeHB <37489173+UkoeHB@users.noreply.github.com>
118 lines
4.9 KiB
Rust
118 lines
4.9 KiB
Rust
//! Simple example demonstrating overflow behavior.
|
|
|
|
use bevy::{color::palettes::css::*, prelude::*, winit::WinitSettings};
|
|
|
|
fn main() {
|
|
App::new()
|
|
.add_plugins(DefaultPlugins)
|
|
// Only run the app when there is user input. This will significantly reduce CPU/GPU use.
|
|
.insert_resource(WinitSettings::desktop_app())
|
|
.add_systems(Startup, setup)
|
|
.add_systems(Update, update_outlines)
|
|
.run();
|
|
}
|
|
|
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|
commands.spawn(Camera2d);
|
|
|
|
let text_style = TextFont::default();
|
|
|
|
let image = asset_server.load("branding/icon.png");
|
|
|
|
commands
|
|
.spawn(NodeBundle {
|
|
style: Style {
|
|
width: Val::Percent(100.),
|
|
height: Val::Percent(100.),
|
|
align_items: AlignItems::Center,
|
|
justify_content: JustifyContent::Center,
|
|
..Default::default()
|
|
},
|
|
background_color: ANTIQUE_WHITE.into(),
|
|
..Default::default()
|
|
})
|
|
.with_children(|parent| {
|
|
for overflow in [
|
|
Overflow::visible(),
|
|
Overflow::clip_x(),
|
|
Overflow::clip_y(),
|
|
Overflow::clip(),
|
|
] {
|
|
parent
|
|
.spawn(NodeBundle {
|
|
style: Style {
|
|
flex_direction: FlexDirection::Column,
|
|
align_items: AlignItems::Center,
|
|
margin: UiRect::horizontal(Val::Px(25.)),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
})
|
|
.with_children(|parent| {
|
|
let label = format!("{overflow:#?}");
|
|
parent
|
|
.spawn(NodeBundle {
|
|
style: Style {
|
|
padding: UiRect::all(Val::Px(10.)),
|
|
margin: UiRect::bottom(Val::Px(25.)),
|
|
..Default::default()
|
|
},
|
|
background_color: Color::srgb(0.25, 0.25, 0.25).into(),
|
|
..Default::default()
|
|
})
|
|
.with_children(|parent| {
|
|
parent.spawn((Text::new(label), text_style.clone()));
|
|
});
|
|
parent
|
|
.spawn(NodeBundle {
|
|
style: Style {
|
|
width: Val::Px(100.),
|
|
height: Val::Px(100.),
|
|
padding: UiRect {
|
|
left: Val::Px(25.),
|
|
top: Val::Px(25.),
|
|
..Default::default()
|
|
},
|
|
border: UiRect::all(Val::Px(5.)),
|
|
overflow,
|
|
..Default::default()
|
|
},
|
|
border_color: Color::BLACK.into(),
|
|
background_color: GRAY.into(),
|
|
..Default::default()
|
|
})
|
|
.with_children(|parent| {
|
|
parent.spawn((
|
|
ImageBundle {
|
|
image: UiImage::new(image.clone()),
|
|
style: Style {
|
|
min_width: Val::Px(100.),
|
|
min_height: Val::Px(100.),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
},
|
|
Interaction::default(),
|
|
Outline {
|
|
width: Val::Px(2.),
|
|
offset: Val::Px(2.),
|
|
color: Color::NONE,
|
|
},
|
|
));
|
|
});
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
fn update_outlines(mut outlines_query: Query<(&mut Outline, Ref<Interaction>)>) {
|
|
for (mut outline, interaction) in outlines_query.iter_mut() {
|
|
if interaction.is_changed() {
|
|
outline.color = match *interaction {
|
|
Interaction::Pressed => RED.into(),
|
|
Interaction::Hovered => WHITE.into(),
|
|
Interaction::None => Color::NONE,
|
|
};
|
|
}
|
|
}
|
|
}
|