bevy_yoleck/
editor_panels.rs1use bevy::ecs::system::SystemId;
2use bevy::prelude::*;
3use bevy_egui::egui;
4use std::ops::{Deref, DerefMut};
5
6use crate::util::EditSpecificResources;
7
8#[derive(Resource)]
10pub struct YoleckPanelUi(pub egui::Ui);
11
12impl Deref for YoleckPanelUi {
13 type Target = egui::Ui;
14
15 fn deref(&self) -> &Self::Target {
16 &self.0
17 }
18}
19
20impl DerefMut for YoleckPanelUi {
21 fn deref_mut(&mut self) -> &mut Self::Target {
22 &mut self.0
23 }
24}
25
26pub(crate) trait EditorPanel: Resource + Sized {
27 fn iter_sections(&self) -> impl Iterator<Item = SystemId<(), Result>>;
28 fn wrapper(
29 &mut self,
30 ctx: &mut egui::Context,
31 viewport_ui: &mut egui::Ui,
32 add_content: impl FnOnce(&mut Self, &mut egui::Ui),
33 ) -> egui::Response;
34
35 fn show_panel(
36 world: &mut World,
37 ctx: &mut egui::Context,
38 viewport_ui: &mut egui::Ui,
39 ) -> egui::Response {
40 world.resource_scope(|world, mut this: Mut<Self>| {
41 this.wrapper(ctx, viewport_ui, |this, ui| {
42 let frame = egui::Frame::new();
43 let mut prepared = frame.begin(ui);
44 let content_ui = std::mem::replace(
45 &mut prepared.content_ui,
46 ui.new_child(egui::UiBuilder {
47 max_rect: Some(ui.max_rect()),
48 layout: Some(*ui.layout()), ..Default::default()
50 }),
51 );
52 world.insert_resource(YoleckPanelUi(content_ui));
53
54 world.resource_scope(|world, mut edit_specific: Mut<EditSpecificResources>| {
55 edit_specific.inject_to_world(world);
56 for section in this.iter_sections() {
57 world.run_system(section).unwrap().unwrap();
58 }
59 edit_specific.take_from_world(world);
60 });
61
62 let YoleckPanelUi(content_ui) = world.remove_resource().expect(
63 "The YoleckPanelUi resource was put in the world by this very function",
64 );
65 prepared.content_ui = content_ui;
66 prepared.end(ui);
67 })
68 })
69 }
70}
71
72#[derive(Resource)]
92pub struct YoleckEditorLeftPanelSections(pub Vec<SystemId<(), Result>>);
93
94impl FromWorld for YoleckEditorLeftPanelSections {
95 fn from_world(world: &mut World) -> Self {
96 Self(vec![
97 world.register_system(crate::editor::new_entity_section),
98 world.register_system(crate::editor::entity_selection_section),
99 ])
100 }
101}
102
103impl EditorPanel for YoleckEditorLeftPanelSections {
104 fn iter_sections(&self) -> impl Iterator<Item = SystemId<(), Result>> {
105 self.0.iter().copied()
106 }
107
108 fn wrapper(
109 &mut self,
110 ctx: &mut egui::Context,
111 viewport_ui: &mut egui::Ui,
112 add_content: impl FnOnce(&mut Self, &mut egui::Ui),
113 ) -> egui::Response {
114 egui::Panel::left("yoleck_left_panel")
115 .resizable(true)
116 .default_size(300.0)
117 .max_size(ctx.content_rect().width() / 4.0)
118 .show_inside(viewport_ui, |ui| {
119 ui.heading("Level Hierarchy");
120 ui.separator();
121 egui::ScrollArea::vertical().show(ui, |ui| {
122 add_content(self, ui);
123 });
124 })
125 .response
126 }
127}
128
129#[derive(Resource)]
132pub struct YoleckEditorRightPanelSections(pub Vec<SystemId<(), Result>>);
133
134impl FromWorld for YoleckEditorRightPanelSections {
135 fn from_world(world: &mut World) -> Self {
136 Self(vec![
137 world.register_system(crate::editor::entity_editing_section),
138 ])
139 }
140}
141
142impl EditorPanel for YoleckEditorRightPanelSections {
143 fn iter_sections(&self) -> impl Iterator<Item = SystemId<(), Result>> {
144 self.0.iter().copied()
145 }
146
147 fn wrapper(
148 &mut self,
149 ctx: &mut egui::Context,
150 viewport_ui: &mut egui::Ui,
151 add_content: impl FnOnce(&mut Self, &mut egui::Ui),
152 ) -> egui::Response {
153 egui::Panel::right("yoleck_right_panel")
154 .resizable(true)
155 .default_size(300.0)
156 .max_size(ctx.content_rect().width() / 4.0)
157 .show_inside(viewport_ui, |ui| {
158 ui.heading("Properties");
159 ui.separator();
160 egui::ScrollArea::vertical().show(ui, |ui| {
161 add_content(self, ui);
162 });
163 })
164 .response
165 }
166}
167
168#[derive(Resource)]
171pub struct YoleckEditorTopPanelSections(pub Vec<SystemId<(), Result>>);
172
173impl FromWorld for YoleckEditorTopPanelSections {
174 fn from_world(world: &mut World) -> Self {
175 Self(vec![
176 world.register_system(crate::level_files_manager::level_files_manager_top_section),
177 world.register_system(crate::level_files_manager::playtest_buttons_section),
178 ])
179 }
180}
181
182impl EditorPanel for YoleckEditorTopPanelSections {
183 fn iter_sections(&self) -> impl Iterator<Item = SystemId<(), Result>> {
184 self.0.iter().copied()
185 }
186
187 fn wrapper(
188 &mut self,
189 _ctx: &mut egui::Context,
190 viewport_ui: &mut egui::Ui,
191 add_content: impl FnOnce(&mut Self, &mut egui::Ui),
192 ) -> egui::Response {
193 egui::Panel::top("yoleck_top_panel")
194 .resizable(false)
195 .show_inside(viewport_ui, |ui| {
196 let inner_margin = 3.;
197
198 ui.add_space(inner_margin);
199 ui.horizontal(|ui| {
200 ui.add_space(inner_margin);
201 ui.label("Yoleck Editor");
202 ui.separator();
203 add_content(self, ui);
204 ui.add_space(inner_margin);
205 });
206 ui.add_space(inner_margin);
207 })
208 .response
209 }
210}
211
212pub struct YoleckEditorBottomPanelTab {
217 pub name: String,
218 pub sections: Vec<SystemId<(), Result>>,
219}
220
221#[derive(Resource)]
226pub struct YoleckEditorBottomPanelSections {
227 pub tabs: Vec<YoleckEditorBottomPanelTab>,
228 active_tab: usize,
229}
230
231impl FromWorld for YoleckEditorBottomPanelSections {
232 fn from_world(world: &mut World) -> Self {
233 Self {
234 tabs: vec![YoleckEditorBottomPanelTab {
235 name: "Console".to_owned(),
236 sections: vec![world.register_system(crate::console::console_panel_section)],
237 }],
238 active_tab: 0,
239 }
240 }
241}
242
243impl EditorPanel for YoleckEditorBottomPanelSections {
244 fn iter_sections(&self) -> impl Iterator<Item = SystemId<(), Result>> {
245 self.tabs
246 .get(self.active_tab)
247 .into_iter()
248 .flat_map(|tab| tab.sections.iter().copied())
249 }
250
251 fn wrapper(
252 &mut self,
253 ctx: &mut egui::Context,
254 viewport_ui: &mut egui::Ui,
255 add_content: impl FnOnce(&mut Self, &mut egui::Ui),
256 ) -> egui::Response {
257 egui::Panel::bottom("yoleck_bottom_panel")
258 .resizable(true)
259 .default_size(200.0)
260 .max_size(ctx.content_rect().height() / 4.0)
261 .show_inside(viewport_ui, |ui| {
262 let inner_margin = 3.;
263 ui.add_space(inner_margin);
264
265 let mut new_active_tab = self.active_tab;
266 ui.horizontal(|ui| {
267 for (i, tab) in self.tabs.iter().enumerate() {
268 if ui
269 .selectable_label(new_active_tab == i, &tab.name)
270 .clicked()
271 {
272 new_active_tab = i;
273 }
274 }
275 });
276 self.active_tab = new_active_tab;
277
278 ui.separator();
279
280 add_content(self, ui);
281 })
282 .response
283 }
284}