Skip to main content

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