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}