1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#![deprecated(since = "0.5.0", note = "please use the `set_viewport` flag instead")]
#![allow(deprecated)]

use bevy::prelude::*;
use bevy::sprite::Anchor;

use crate::PixelProjection;

/// Provides an opaque border around the desired resolution.
pub struct PixelBorderPlugin {
    pub color: Color,
}

impl Plugin for PixelBorderPlugin {
    fn build(&self, app: &mut App) {
        app.insert_resource(BorderColor(self.color))
            .add_systems(Startup, spawn_borders)
            .add_systems(
                PostUpdate,
                resize_borders
                    .after(bevy::render::camera::camera_system::<PixelProjection>)
                    .before(bevy::render::view::visibility::update_frusta::<PixelProjection>),
            );
    }
}

/// Resource used to specify the color of the opaque border.
#[derive(Clone, Debug, Resource)]
pub struct BorderColor(Color);

// Component
#[derive(Component)]
enum Border {
    Left,
    Right,
    Top,
    Bottom,
}

/// System to spawn the opaque border. Automatically added by the plugin as a
/// startup system.
pub fn spawn_borders(mut commands: Commands, color: Res<BorderColor>) {
    let mut spawn_border = |name: &'static str, side: Border| -> Entity {
        commands
            .spawn((
                Name::new(name),
                side,
                SpriteBundle {
                    sprite: Sprite {
                        anchor: Anchor::BottomLeft,
                        color: color.0,
                        ..Default::default()
                    },
                    ..Default::default()
                },
            ))
            .id()
    };

    let left = spawn_border("Left", Border::Left);
    let right = spawn_border("Right", Border::Right);
    let top = spawn_border("Top", Border::Top);
    let bottom = spawn_border("Bottom", Border::Bottom);

    commands
        .spawn((SpatialBundle::default(), Name::new("Borders")))
        .push_children(&[left, right, top, bottom]);
}

#[allow(clippy::type_complexity)]
fn resize_borders(
    cameras: Query<
        (&PixelProjection, &GlobalTransform),
        Or<(Changed<PixelProjection>, Changed<GlobalTransform>)>,
    >,
    mut borders: Query<(&mut Sprite, &mut Transform, &Border), Without<PixelProjection>>,
) {
    for (projection, transform) in cameras.iter() {
        let z = projection.far - 0.2;
        let width = projection.desired_width.map(|w| w as f32).unwrap_or(0.0);
        let height = projection.desired_height.map(|h| h as f32).unwrap_or(0.0);
        let left = transform.translation().x
            + if projection.centered {
                -(width / 2.0).round()
            } else {
                0.0
            };
        let right = left + width;
        let bottom = transform.translation().y
            + if projection.centered {
                (-height / 2.0).round()
            } else {
                0.0
            };
        let top = bottom + height;

        for (mut sprite, mut transform, border) in borders.iter_mut() {
            match border {
                Border::Left => {
                    *transform = Transform::from_xyz(left - width, bottom - height, z);
                    sprite.custom_size = Some(Vec2::new(width, 3.0 * height));
                }
                Border::Right => {
                    *transform = Transform::from_xyz(right, bottom - height, z);
                    sprite.custom_size = Some(Vec2::new(width, 3.0 * height));
                }
                Border::Top => {
                    *transform = Transform::from_xyz(left - width, top, z);
                    sprite.custom_size = Some(Vec2::new(3.0 * width, height));
                }
                Border::Bottom => {
                    *transform = Transform::from_xyz(left - width, bottom - height, z);
                    sprite.custom_size = Some(Vec2::new(3.0 * width, height));
                }
            }
        }
    }
}