Path: blob/main/examples/ui/layout/display_and_visibility.rs
9351 views
//! Demonstrates how Display and Visibility work in the UI.12use bevy::{3color::palettes::css::{DARK_CYAN, DARK_GRAY, YELLOW},4ecs::{component::Mutable, hierarchy::ChildSpawnerCommands},5prelude::*,6};78const PALETTE: [&str; 4] = ["27496D", "466B7A", "669DB3", "ADCBE3"];9const HIDDEN_COLOR: Color = Color::srgb(1.0, 0.7, 0.7);1011fn main() {12App::new()13.add_plugins(DefaultPlugins)14.add_systems(Startup, setup)15.add_systems(16Update,17(18buttons_handler::<Display>,19buttons_handler::<Visibility>,20text_hover,21),22)23.run();24}2526#[derive(Component)]27struct Target<T> {28id: Entity,29phantom: std::marker::PhantomData<T>,30}3132impl<T> Target<T> {33fn new(id: Entity) -> Self {34Self {35id,36phantom: std::marker::PhantomData,37}38}39}4041trait TargetUpdate {42type TargetComponent: Component<Mutability = Mutable>;43const NAME: &'static str;44fn update_target(&self, target: &mut Self::TargetComponent) -> String;45}4647impl TargetUpdate for Target<Display> {48type TargetComponent = Node;49const NAME: &'static str = "Display";50fn update_target(&self, node: &mut Self::TargetComponent) -> String {51node.display = match node.display {52Display::Flex => Display::None,53Display::None => Display::Flex,54Display::Block | Display::Grid => unreachable!(),55};56format!("{}::{:?} ", Self::NAME, node.display)57}58}5960impl TargetUpdate for Target<Visibility> {61type TargetComponent = Visibility;62const NAME: &'static str = "Visibility";63fn update_target(&self, visibility: &mut Self::TargetComponent) -> String {64*visibility = match *visibility {65Visibility::Inherited => Visibility::Visible,66Visibility::Visible => Visibility::Hidden,67Visibility::Hidden => Visibility::Inherited,68};69format!("{}::{visibility:?}", Self::NAME)70}71}7273fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {74let palette: [Color; 4] = PALETTE.map(|hex| Srgba::hex(hex).unwrap().into());7576let text_font = TextFont {77font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),78..default()79};8081commands.spawn(Camera2d);82commands83.spawn((84Node {85width: percent(100),86height: percent(100),87flex_direction: FlexDirection::Column,88align_items: AlignItems::Center,89justify_content: JustifyContent::SpaceEvenly,90..Default::default()91},92BackgroundColor(Color::BLACK),93))94.with_children(|parent| {95parent.spawn((96Text::new("Use the panel on the right to change the Display and Visibility properties for the respective nodes of the panel on the left"),97text_font.clone(),98TextLayout::new_with_justify(Justify::Center),99Node {100margin: UiRect::bottom(px(10)),101..Default::default()102},103));104105parent106.spawn(Node {107width: percent(100),108..default()109})110.with_children(|parent| {111let mut target_ids = vec![];112parent113.spawn(Node {114width: percent(50),115height: px(520),116justify_content: JustifyContent::Center,117..default()118})119.with_children(|parent| {120target_ids = spawn_left_panel(parent, &palette);121});122123parent124.spawn(Node {125width: percent(50),126justify_content: JustifyContent::Center,127..default()128})129.with_children(|parent| {130spawn_right_panel(parent, text_font, &palette, target_ids);131});132});133134parent135.spawn(Node {136flex_direction: FlexDirection::Row,137align_items: AlignItems::Start,138justify_content: JustifyContent::Start,139column_gap: px(10),140..default()141})142.with_children(|builder| {143let text_font = TextFont {144font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),145..default()146};147148builder.spawn((149Text::new("Display::None\nVisibility::Hidden\nVisibility::Inherited"),150text_font.clone(),151TextColor(HIDDEN_COLOR),152TextLayout::new_with_justify(Justify::Center),153));154builder.spawn((155Text::new("-\n-\n-"),156text_font.clone(),157TextColor(DARK_GRAY.into()),158TextLayout::new_with_justify(Justify::Center),159));160builder.spawn((Text::new("The UI Node and its descendants will not be visible and will not be allotted any space in the UI layout.\nThe UI Node will not be visible but will still occupy space in the UI layout.\nThe UI node will inherit the visibility property of its parent. If it has no parent it will be visible."), text_font));161});162});163}164165fn spawn_left_panel(builder: &mut ChildSpawnerCommands, palette: &[Color; 4]) -> Vec<Entity> {166let mut target_ids = vec![];167builder168.spawn((169Node {170padding: UiRect::all(px(10)),171..default()172},173BackgroundColor(Color::WHITE),174))175.with_children(|parent| {176parent177.spawn((Node::default(), BackgroundColor(Color::BLACK)))178.with_children(|parent| {179let id = parent180.spawn((181Node {182align_items: AlignItems::FlexEnd,183justify_content: JustifyContent::FlexEnd,184..default()185},186BackgroundColor(palette[0]),187Outline {188width: px(4),189color: DARK_CYAN.into(),190offset: px(10),191},192))193.with_children(|parent| {194parent.spawn(Node {195width: px(100),196height: px(500),197..default()198});199200let id = parent201.spawn((202Node {203height: px(400),204align_items: AlignItems::FlexEnd,205justify_content: JustifyContent::FlexEnd,206..default()207},208BackgroundColor(palette[1]),209))210.with_children(|parent| {211parent.spawn(Node {212width: px(100),213height: px(400),214..default()215});216217let id = parent218.spawn((219Node {220height: px(300),221align_items: AlignItems::FlexEnd,222justify_content: JustifyContent::FlexEnd,223..default()224},225BackgroundColor(palette[2]),226))227.with_children(|parent| {228parent.spawn(Node {229width: px(100),230height: px(300),231..default()232});233234let id = parent235.spawn((236Node {237width: px(200),238height: px(200),239..default()240},241BackgroundColor(palette[3]),242))243.id();244target_ids.push(id);245})246.id();247target_ids.push(id);248})249.id();250target_ids.push(id);251})252.id();253target_ids.push(id);254});255});256target_ids257}258259fn spawn_right_panel(260parent: &mut ChildSpawnerCommands,261text_font: TextFont,262palette: &[Color; 4],263mut target_ids: Vec<Entity>,264) {265let spawn_buttons = |parent: &mut ChildSpawnerCommands, target_id| {266spawn_button::<Display>(parent, text_font.clone(), target_id);267spawn_button::<Visibility>(parent, text_font.clone(), target_id);268};269parent270.spawn((271Node {272padding: UiRect::all(px(10)),273..default()274},275BackgroundColor(Color::WHITE),276))277.with_children(|parent| {278parent279.spawn((280Node {281width: px(500),282height: px(500),283flex_direction: FlexDirection::Column,284align_items: AlignItems::FlexEnd,285justify_content: JustifyContent::SpaceBetween,286padding: UiRect {287left: px(5),288top: px(5),289..default()290},291..default()292},293BackgroundColor(palette[0]),294Outline {295width: px(4),296color: DARK_CYAN.into(),297offset: px(10),298},299))300.with_children(|parent| {301spawn_buttons(parent, target_ids.pop().unwrap());302303parent304.spawn((305Node {306width: px(400),307height: px(400),308flex_direction: FlexDirection::Column,309align_items: AlignItems::FlexEnd,310justify_content: JustifyContent::SpaceBetween,311padding: UiRect {312left: px(5),313top: px(5),314..default()315},316..default()317},318BackgroundColor(palette[1]),319))320.with_children(|parent| {321spawn_buttons(parent, target_ids.pop().unwrap());322323parent324.spawn((325Node {326width: px(300),327height: px(300),328flex_direction: FlexDirection::Column,329align_items: AlignItems::FlexEnd,330justify_content: JustifyContent::SpaceBetween,331padding: UiRect {332left: px(5),333top: px(5),334..default()335},336..default()337},338BackgroundColor(palette[2]),339))340.with_children(|parent| {341spawn_buttons(parent, target_ids.pop().unwrap());342343parent344.spawn((345Node {346width: px(200),347height: px(200),348align_items: AlignItems::FlexStart,349justify_content: JustifyContent::SpaceBetween,350flex_direction: FlexDirection::Column,351padding: UiRect {352left: px(5),353top: px(5),354..default()355},356..default()357},358BackgroundColor(palette[3]),359))360.with_children(|parent| {361spawn_buttons(parent, target_ids.pop().unwrap());362363parent.spawn(Node {364width: px(100),365height: px(100),366..default()367});368});369});370});371});372});373}374375fn spawn_button<T>(parent: &mut ChildSpawnerCommands, text_font: TextFont, target: Entity)376where377T: Default + std::fmt::Debug + Send + Sync + 'static,378Target<T>: TargetUpdate,379{380parent381.spawn((382Button,383Node {384align_self: AlignSelf::FlexStart,385padding: UiRect::axes(px(5), px(1)),386..default()387},388BackgroundColor(Color::BLACK.with_alpha(0.5)),389Target::<T>::new(target),390))391.with_children(|builder| {392builder.spawn((393Text(format!("{}::{:?}", Target::<T>::NAME, T::default())),394text_font,395TextLayout::new_with_justify(Justify::Center),396));397});398}399400fn buttons_handler<T>(401mut left_panel_query: Query<&mut <Target<T> as TargetUpdate>::TargetComponent>,402mut visibility_button_query: Query<(&Target<T>, &Interaction, &Children), Changed<Interaction>>,403mut text_query: Query<(&mut Text, &mut TextColor)>,404) where405T: Send + Sync,406Target<T>: TargetUpdate + Component,407{408for (target, interaction, children) in visibility_button_query.iter_mut() {409if matches!(interaction, Interaction::Pressed) {410let mut target_value = left_panel_query.get_mut(target.id).unwrap();411for &child in children {412if let Ok((mut text, mut text_color)) = text_query.get_mut(child) {413**text = target.update_target(target_value.as_mut());414text_color.0 = if text.contains("None") || text.contains("Hidden") {415Color::srgb(1.0, 0.7, 0.7)416} else {417Color::WHITE418};419}420}421}422}423}424425fn text_hover(426mut button_query: Query<(&Interaction, &mut BackgroundColor, &Children), Changed<Interaction>>,427mut text_query: Query<(&Text, &mut TextColor)>,428) {429for (interaction, mut color, children) in button_query.iter_mut() {430match interaction {431Interaction::Hovered => {432*color = Color::BLACK.with_alpha(0.6).into();433for &child in children {434if let Ok((_, mut text_color)) = text_query.get_mut(child) {435// Bypass change detection to avoid recomputation of the text when only changing the color436text_color.bypass_change_detection().0 = YELLOW.into();437}438}439}440_ => {441*color = Color::BLACK.with_alpha(0.5).into();442for &child in children {443if let Ok((text, mut text_color)) = text_query.get_mut(child) {444text_color.bypass_change_detection().0 =445if text.contains("None") || text.contains("Hidden") {446HIDDEN_COLOR447} else {448Color::WHITE449};450}451}452}453}454}455}456457458