overflow_debug/
overflow_debug.rs

1//! Tests how different transforms behave when clipped with `Overflow::Hidden`
2
3use bevy::{input::common_conditions::input_just_pressed, prelude::*, ui::widget::TextUiWriter};
4use std::f32::consts::{FRAC_PI_2, PI, TAU};
5
6const CONTAINER_SIZE: f32 = 150.0;
7const LOOP_LENGTH: f32 = 4.0;
8
9fn main() {
10    App::new()
11        .add_plugins(DefaultPlugins)
12        .init_resource::<AnimationState>()
13        .add_systems(Startup, setup)
14        .add_systems(
15            Update,
16            (
17                toggle_overflow.run_if(input_just_pressed(KeyCode::KeyO)),
18                next_container_size.run_if(input_just_pressed(KeyCode::KeyS)),
19                update_transform::<Move>,
20                update_transform::<Scale>,
21                update_transform::<Rotate>,
22                update_animation,
23            ),
24        )
25        .run();
26}
27
28#[derive(Component)]
29struct Instructions;
30
31#[derive(Resource, Default)]
32struct AnimationState {
33    playing: bool,
34    paused_at: f32,
35    paused_total: f32,
36    t: f32,
37}
38
39#[derive(Component)]
40struct Container(u8);
41
42trait UpdateTransform {
43    fn update(&self, t: f32, transform: &mut UiTransform);
44}
45
46#[derive(Component)]
47struct Move;
48
49impl UpdateTransform for Move {
50    fn update(&self, t: f32, transform: &mut UiTransform) {
51        transform.translation.x = percent(ops::sin(t * TAU - FRAC_PI_2) * 50.);
52        transform.translation.y = percent(-ops::cos(t * TAU - FRAC_PI_2) * 50.);
53    }
54}
55
56#[derive(Component)]
57struct Scale;
58
59impl UpdateTransform for Scale {
60    fn update(&self, t: f32, transform: &mut UiTransform) {
61        transform.scale.x = 1.0 + 0.5 * ops::cos(t * TAU).max(0.0);
62        transform.scale.y = 1.0 + 0.5 * ops::cos(t * TAU + PI).max(0.0);
63    }
64}
65
66#[derive(Component)]
67struct Rotate;
68
69impl UpdateTransform for Rotate {
70    fn update(&self, t: f32, transform: &mut UiTransform) {
71        transform.rotation = Rot2::radians(ops::cos(t * TAU) * 45.0);
72    }
73}
74
75fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
76    // Camera
77
78    commands.spawn(Camera2d);
79
80    // Instructions
81
82    let text_font = TextFont::default();
83
84    commands
85        .spawn((
86            Text::new(
87                "Next Overflow Setting (O)\nNext Container Size (S)\nToggle Animation (space)\n\n",
88            ),
89            text_font.clone(),
90            Node {
91                position_type: PositionType::Absolute,
92                top: px(12),
93                left: px(12),
94                ..default()
95            },
96            Instructions,
97        ))
98        .with_child((
99            TextSpan::new(format!("{:?}", Overflow::clip())),
100            text_font.clone(),
101        ));
102
103    // Overflow Debug
104
105    commands
106        .spawn(Node {
107            width: percent(100),
108            height: percent(100),
109            justify_content: JustifyContent::Center,
110            align_items: AlignItems::Center,
111            ..default()
112        })
113        .with_children(|parent| {
114            parent
115                .spawn(Node {
116                    display: Display::Grid,
117                    grid_template_columns: RepeatedGridTrack::px(3, CONTAINER_SIZE),
118                    grid_template_rows: RepeatedGridTrack::px(2, CONTAINER_SIZE),
119                    row_gap: px(80),
120                    column_gap: px(80),
121                    ..default()
122                })
123                .with_children(|parent| {
124                    spawn_image(parent, &asset_server, Move);
125                    spawn_image(parent, &asset_server, Scale);
126                    spawn_image(parent, &asset_server, Rotate);
127
128                    spawn_text(parent, &asset_server, Move);
129                    spawn_text(parent, &asset_server, Scale);
130                    spawn_text(parent, &asset_server, Rotate);
131                });
132        });
133}
134
135fn spawn_image(
136    parent: &mut ChildSpawnerCommands,
137    asset_server: &Res<AssetServer>,
138    update_transform: impl UpdateTransform + Component,
139) {
140    spawn_container(parent, update_transform, |parent| {
141        parent.spawn((
142            ImageNode::new(asset_server.load("branding/bevy_logo_dark_big.png")),
143            Node {
144                height: px(100),
145                position_type: PositionType::Absolute,
146                top: px(-50),
147                left: px(-200),
148                ..default()
149            },
150        ));
151    });
152}
153
154fn spawn_text(
155    parent: &mut ChildSpawnerCommands,
156    asset_server: &Res<AssetServer>,
157    update_transform: impl UpdateTransform + Component,
158) {
159    spawn_container(parent, update_transform, |parent| {
160        parent.spawn((
161            Text::new("Bevy"),
162            TextFont {
163                font: asset_server.load("fonts/FiraSans-Bold.ttf"),
164                font_size: 100.0,
165                ..default()
166            },
167        ));
168    });
169}
170
171fn spawn_container(
172    parent: &mut ChildSpawnerCommands,
173    update_transform: impl UpdateTransform + Component,
174    spawn_children: impl FnOnce(&mut ChildSpawnerCommands),
175) {
176    parent
177        .spawn((
178            Node {
179                width: percent(100),
180                height: percent(100),
181                align_items: AlignItems::Center,
182                justify_content: JustifyContent::Center,
183                overflow: Overflow::clip(),
184                ..default()
185            },
186            BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
187            Container(0),
188        ))
189        .with_children(|parent| {
190            parent
191                .spawn((
192                    Node {
193                        align_items: AlignItems::Center,
194                        justify_content: JustifyContent::Center,
195                        ..default()
196                    },
197                    update_transform,
198                ))
199                .with_children(spawn_children);
200        });
201}
202
203fn update_animation(
204    mut animation: ResMut<AnimationState>,
205    time: Res<Time>,
206    keys: Res<ButtonInput<KeyCode>>,
207) {
208    let delta = time.elapsed_secs();
209
210    if keys.just_pressed(KeyCode::Space) {
211        animation.playing = !animation.playing;
212
213        if !animation.playing {
214            animation.paused_at = delta;
215        } else {
216            animation.paused_total += delta - animation.paused_at;
217        }
218    }
219
220    if animation.playing {
221        animation.t = (delta - animation.paused_total) % LOOP_LENGTH / LOOP_LENGTH;
222    }
223}
224
225fn update_transform<T: UpdateTransform + Component>(
226    animation: Res<AnimationState>,
227    mut containers: Query<(&mut UiTransform, &T)>,
228) {
229    for (mut transform, update_transform) in &mut containers {
230        update_transform.update(animation.t, &mut transform);
231    }
232}
233
234fn toggle_overflow(
235    mut containers: Query<&mut Node, With<Container>>,
236    instructions: Single<Entity, With<Instructions>>,
237    mut writer: TextUiWriter,
238) {
239    for mut node in &mut containers {
240        node.overflow = match node.overflow {
241            Overflow {
242                x: OverflowAxis::Visible,
243                y: OverflowAxis::Visible,
244            } => Overflow::clip_y(),
245            Overflow {
246                x: OverflowAxis::Visible,
247                y: OverflowAxis::Clip,
248            } => Overflow::clip_x(),
249            Overflow {
250                x: OverflowAxis::Clip,
251                y: OverflowAxis::Visible,
252            } => Overflow::clip(),
253            _ => Overflow::visible(),
254        };
255
256        let entity = *instructions;
257        *writer.text(entity, 1) = format!("{:?}", node.overflow);
258    }
259}
260
261fn next_container_size(mut containers: Query<(&mut Node, &mut Container)>) {
262    for (mut node, mut container) in &mut containers {
263        container.0 = (container.0 + 1) % 3;
264
265        node.width = match container.0 {
266            2 => percent(30),
267            _ => percent(100),
268        };
269        node.height = match container.0 {
270            1 => percent(30),
271            _ => percent(100),
272        };
273    }
274}