Skip to main content

cu_bevymon/
split.rs

1use bevy::prelude::*;
2use bevy::ui::{Node as UiNode, UiRect, UiTargetCamera, widget::ViewportNode};
3
4use crate::{CuBevyMonFocusBorder, CuBevyMonPanel, CuBevyMonSurface, CuBevyMonSurfaceNode};
5
6#[derive(Component)]
7pub struct CuBevyMonSplitRoot;
8
9#[derive(Component)]
10pub struct CuBevyMonSplitSimPanel;
11
12#[derive(Component)]
13pub struct CuBevyMonSplitMonitorPanel;
14
15#[derive(Clone, Copy, Debug)]
16pub struct CuBevyMonSplitStyle {
17    pub outer_padding_px: f32,
18    pub panel_gap_px: f32,
19    pub sim_panel_percent: f32,
20    pub monitor_panel_percent: f32,
21    pub monitor_panel_inset_px: f32,
22    pub focused_border_color: Color,
23    pub unfocused_border_color: Color,
24    pub focused_border_width_px: f32,
25    pub unfocused_border_width_px: f32,
26    pub monitor_background: Color,
27}
28
29impl Default for CuBevyMonSplitStyle {
30    fn default() -> Self {
31        Self {
32            outer_padding_px: 12.0,
33            panel_gap_px: 12.0,
34            sim_panel_percent: 67.0,
35            monitor_panel_percent: 33.0,
36            monitor_panel_inset_px: 4.0,
37            focused_border_color: Color::srgb(0.38, 0.40, 0.43),
38            unfocused_border_color: Color::BLACK,
39            focused_border_width_px: 1.0,
40            unfocused_border_width_px: 1.0,
41            monitor_background: Color::srgba(0.04, 0.05, 0.08, 0.98),
42        }
43    }
44}
45
46pub struct CuBevyMonSplitLayoutConfig {
47    pub scene_camera: Entity,
48    pub ui_camera: Option<Entity>,
49    pub style: CuBevyMonSplitStyle,
50}
51
52impl CuBevyMonSplitLayoutConfig {
53    pub fn new(scene_camera: Entity) -> Self {
54        Self {
55            scene_camera,
56            ui_camera: None,
57            style: CuBevyMonSplitStyle::default(),
58        }
59    }
60
61    pub fn with_ui_camera(mut self, ui_camera: Entity) -> Self {
62        self.ui_camera = Some(ui_camera);
63        self
64    }
65
66    pub fn with_style(mut self, style: CuBevyMonSplitStyle) -> Self {
67        self.style = style;
68        self
69    }
70}
71
72#[derive(Clone, Copy, Debug)]
73pub struct CuBevyMonSplitLayoutEntities {
74    pub root: Entity,
75    pub sim_panel: Entity,
76    pub monitor_panel: Entity,
77    pub monitor_texture: Entity,
78}
79
80pub fn spawn_split_layout(
81    commands: &mut Commands,
82    monitor_texture: Handle<Image>,
83    config: CuBevyMonSplitLayoutConfig,
84) -> CuBevyMonSplitLayoutEntities {
85    let style = config.style;
86    let sim_border = CuBevyMonFocusBorder::new(CuBevyMonSurface::Sim)
87        .with_colors(style.focused_border_color, style.unfocused_border_color)
88        .with_widths(
89            style.focused_border_width_px,
90            style.unfocused_border_width_px,
91        );
92    let monitor_border = CuBevyMonFocusBorder::new(CuBevyMonSurface::Monitor)
93        .with_colors(style.focused_border_color, style.unfocused_border_color)
94        .with_widths(
95            style.focused_border_width_px,
96            style.unfocused_border_width_px,
97        );
98
99    let mut root_builder = commands.spawn((
100        UiNode {
101            width: Val::Percent(100.0),
102            height: Val::Percent(100.0),
103            flex_direction: FlexDirection::Row,
104            padding: UiRect::all(Val::Px(style.outer_padding_px)),
105            column_gap: Val::Px(style.panel_gap_px),
106            ..default()
107        },
108        BackgroundColor(Color::NONE),
109        CuBevyMonSplitRoot,
110    ));
111    if let Some(ui_camera) = config.ui_camera {
112        root_builder.insert(UiTargetCamera(ui_camera));
113    }
114
115    let mut sim_panel = None;
116    let mut monitor_panel = None;
117    let mut monitor_texture_node = None;
118    let root = root_builder
119        .with_children(|parent| {
120            sim_panel = Some(
121                parent
122                    .spawn((
123                        UiNode {
124                            height: Val::Percent(100.0),
125                            min_width: Val::Px(0.0),
126                            flex_basis: Val::Px(0.0),
127                            flex_grow: style.sim_panel_percent,
128                            position_type: PositionType::Relative,
129                            border: UiRect::all(Val::Px(style.unfocused_border_width_px)),
130                            ..default()
131                        },
132                        ViewportNode::new(config.scene_camera),
133                        BackgroundColor(Color::NONE),
134                        BorderColor::all(style.unfocused_border_color),
135                        CuBevyMonSurfaceNode(CuBevyMonSurface::Sim),
136                        sim_border,
137                        CuBevyMonSplitSimPanel,
138                    ))
139                    .id(),
140            );
141
142            monitor_panel = Some(
143                parent
144                    .spawn((
145                        UiNode {
146                            height: Val::Percent(100.0),
147                            min_width: Val::Px(0.0),
148                            flex_basis: Val::Px(0.0),
149                            flex_grow: style.monitor_panel_percent,
150                            border: UiRect::all(Val::Px(style.unfocused_border_width_px)),
151                            padding: UiRect::all(Val::Px(style.monitor_panel_inset_px)),
152                            ..default()
153                        },
154                        BackgroundColor(style.monitor_background),
155                        BorderColor::all(style.unfocused_border_color),
156                        CuBevyMonSurfaceNode(CuBevyMonSurface::Monitor),
157                        monitor_border,
158                        CuBevyMonSplitMonitorPanel,
159                    ))
160                    .with_children(|panel| {
161                        monitor_texture_node = Some(
162                            panel
163                                .spawn((
164                                    ImageNode::new(monitor_texture.clone()),
165                                    UiNode {
166                                        width: Val::Percent(100.0),
167                                        height: Val::Percent(100.0),
168                                        ..default()
169                                    },
170                                    BackgroundColor(Color::BLACK),
171                                    CuBevyMonPanel,
172                                ))
173                                .id(),
174                        );
175                    })
176                    .id(),
177            );
178        })
179        .id();
180
181    CuBevyMonSplitLayoutEntities {
182        root,
183        sim_panel: sim_panel.expect("split layout missing sim panel"),
184        monitor_panel: monitor_panel.expect("split layout missing monitor panel"),
185        monitor_texture: monitor_texture_node.expect("split layout missing monitor texture"),
186    }
187}