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 add_content: impl FnOnce(&mut Self, &mut egui::Ui),
32 ) -> egui::Response;
33
34 fn show_panel(world: &mut World, ctx: &mut egui::Context) -> egui::Response {
35 world.resource_scope(|world, mut this: Mut<Self>| {
36 this.wrapper(ctx, |this, ui| {
37 let frame = egui::Frame::new();
38 let mut prepared = frame.begin(ui);
39 let content_ui = std::mem::replace(
40 &mut prepared.content_ui,
41 ui.new_child(egui::UiBuilder {
42 max_rect: Some(ui.max_rect()),
43 layout: Some(*ui.layout()), ..Default::default()
45 }),
46 );
47 world.insert_resource(YoleckPanelUi(content_ui));
48
49 world.resource_scope(|world, mut edit_specific: Mut<EditSpecificResources>| {
50 edit_specific.inject_to_world(world);
51 for section in this.iter_sections() {
52 world.run_system(section).unwrap().unwrap();
53 }
54 edit_specific.take_from_world(world);
55 });
56
57 let YoleckPanelUi(content_ui) = world.remove_resource().expect(
58 "The YoleckPanelUi resource was put in the world by this very function",
59 );
60 prepared.content_ui = content_ui;
61 prepared.end(ui);
62 })
63 })
64 }
65}
66
67#[derive(Resource)]
87pub struct YoleckEditorLeftPanelSections(pub Vec<SystemId<(), Result>>);
88
89impl FromWorld for YoleckEditorLeftPanelSections {
90 fn from_world(world: &mut World) -> Self {
91 Self(vec![
92 world.register_system(crate::editor::new_entity_section),
93 world.register_system(crate::editor::entity_selection_section),
94 ])
95 }
96}
97
98impl EditorPanel for YoleckEditorLeftPanelSections {
99 fn iter_sections(&self) -> impl Iterator<Item = SystemId<(), Result>> {
100 self.0.iter().copied()
101 }
102
103 fn wrapper(
104 &mut self,
105 ctx: &mut egui::Context,
106 add_content: impl FnOnce(&mut Self, &mut egui::Ui),
107 ) -> egui::Response {
108 egui::SidePanel::left("yoleck_left_panel")
109 .resizable(true)
110 .default_width(300.0)
111 .max_width(ctx.content_rect().width() / 4.0)
112 .show(ctx, |ui| {
113 ui.heading("Level Hierarchy");
114 ui.separator();
115 egui::ScrollArea::vertical().show(ui, |ui| {
116 add_content(self, ui);
117 });
118 })
119 .response
120 }
121}
122
123#[derive(Resource)]
126pub struct YoleckEditorRightPanelSections(pub Vec<SystemId<(), Result>>);
127
128impl FromWorld for YoleckEditorRightPanelSections {
129 fn from_world(world: &mut World) -> Self {
130 Self(vec![
131 world.register_system(crate::editor::entity_editing_section),
132 ])
133 }
134}
135
136impl EditorPanel for YoleckEditorRightPanelSections {
137 fn iter_sections(&self) -> impl Iterator<Item = SystemId<(), Result>> {
138 self.0.iter().copied()
139 }
140
141 fn wrapper(
142 &mut self,
143 ctx: &mut egui::Context,
144 add_content: impl FnOnce(&mut Self, &mut egui::Ui),
145 ) -> egui::Response {
146 egui::SidePanel::right("yoleck_right_panel")
147 .resizable(true)
148 .default_width(300.0)
149 .max_width(ctx.content_rect().width() / 4.0)
150 .show(ctx, |ui| {
151 ui.heading("Properties");
152 ui.separator();
153 egui::ScrollArea::vertical().show(ui, |ui| {
154 add_content(self, ui);
155 });
156 })
157 .response
158 }
159}
160
161#[derive(Resource)]
164pub struct YoleckEditorTopPanelSections(pub Vec<SystemId<(), Result>>);
165
166impl FromWorld for YoleckEditorTopPanelSections {
167 fn from_world(world: &mut World) -> Self {
168 Self(vec![
169 world.register_system(crate::level_files_manager::level_files_manager_top_section),
170 world.register_system(crate::level_files_manager::playtest_buttons_section),
171 ])
172 }
173}
174
175impl EditorPanel for YoleckEditorTopPanelSections {
176 fn iter_sections(&self) -> impl Iterator<Item = SystemId<(), Result>> {
177 self.0.iter().copied()
178 }
179
180 fn wrapper(
181 &mut self,
182 ctx: &mut egui::Context,
183 add_content: impl FnOnce(&mut Self, &mut egui::Ui),
184 ) -> egui::Response {
185 egui::TopBottomPanel::top("yoleck_top_panel")
186 .resizable(false)
187 .show(ctx, |ui| {
188 let inner_margin = 3.;
189
190 ui.add_space(inner_margin);
191 ui.horizontal(|ui| {
192 ui.add_space(inner_margin);
193 ui.label("Yoleck Editor");
194 ui.separator();
195 add_content(self, ui);
196 ui.add_space(inner_margin);
197 });
198 ui.add_space(inner_margin);
199 })
200 .response
201 }
202}
203
204pub struct YoleckEditorBottomPanelTab {
209 pub name: String,
210 pub sections: Vec<SystemId<(), Result>>,
211}
212
213#[derive(Resource)]
218pub struct YoleckEditorBottomPanelSections {
219 pub tabs: Vec<YoleckEditorBottomPanelTab>,
220 active_tab: usize,
221}
222
223impl FromWorld for YoleckEditorBottomPanelSections {
224 fn from_world(world: &mut World) -> Self {
225 Self {
226 tabs: vec![YoleckEditorBottomPanelTab {
227 name: "Console".to_owned(),
228 sections: vec![world.register_system(crate::console::console_panel_section)],
229 }],
230 active_tab: 0,
231 }
232 }
233}
234
235impl EditorPanel for YoleckEditorBottomPanelSections {
236 fn iter_sections(&self) -> impl Iterator<Item = SystemId<(), Result>> {
237 self.tabs
238 .get(self.active_tab)
239 .into_iter()
240 .flat_map(|tab| tab.sections.iter().copied())
241 }
242
243 fn wrapper(
244 &mut self,
245 ctx: &mut egui::Context,
246 add_content: impl FnOnce(&mut Self, &mut egui::Ui),
247 ) -> egui::Response {
248 egui::TopBottomPanel::bottom("yoleck_bottom_panel")
249 .resizable(true)
250 .default_height(200.0)
251 .max_height(ctx.content_rect().height() / 4.0)
252 .show(ctx, |ui| {
253 let inner_margin = 3.;
254 ui.add_space(inner_margin);
255
256 let mut new_active_tab = self.active_tab;
257 ui.horizontal(|ui| {
258 for (i, tab) in self.tabs.iter().enumerate() {
259 if ui
260 .selectable_label(new_active_tab == i, &tab.name)
261 .clicked()
262 {
263 new_active_tab = i;
264 }
265 }
266 });
267 self.active_tab = new_active_tab;
268
269 ui.separator();
270
271 add_content(self, ui);
272 })
273 .response
274 }
275}